mirror of
https://github.com/openjdk/jdk.git
synced 2026-08-02 06:06:44 +00:00
8265602: -XX:DumpLoadedClassList should support custom loaders
Reviewed-by: ccheung, minqi
This commit is contained in:
parent
ea02dade43
commit
e7b6f48182
@ -161,9 +161,6 @@ ArchiveBuilder::ArchiveBuilder() :
|
||||
_rw_src_objs(),
|
||||
_ro_src_objs(),
|
||||
_src_obj_table(INITIAL_TABLE_SIZE, MAX_TABLE_SIZE),
|
||||
_num_instance_klasses(0),
|
||||
_num_obj_array_klasses(0),
|
||||
_num_type_array_klasses(0),
|
||||
_total_closed_heap_region_size(0),
|
||||
_total_open_heap_region_size(0),
|
||||
_estimated_metaspaceobj_bytes(0),
|
||||
@ -222,14 +219,6 @@ bool ArchiveBuilder::gather_klass_and_symbol(MetaspaceClosure::Ref* ref, bool re
|
||||
assert(klass->is_klass(), "must be");
|
||||
if (!is_excluded(klass)) {
|
||||
_klasses->append(klass);
|
||||
if (klass->is_instance_klass()) {
|
||||
_num_instance_klasses ++;
|
||||
} else if (klass->is_objArray_klass()) {
|
||||
_num_obj_array_klasses ++;
|
||||
} else {
|
||||
assert(klass->is_typeArray_klass(), "sanity");
|
||||
_num_type_array_klasses ++;
|
||||
}
|
||||
}
|
||||
// See RunTimeClassInfo::get_for()
|
||||
_estimated_metaspaceobj_bytes += align_up(BytesPerWord, SharedSpaceObjectAlignment);
|
||||
@ -258,12 +247,6 @@ void ArchiveBuilder::gather_klasses_and_symbols() {
|
||||
#endif
|
||||
doit.finish();
|
||||
|
||||
log_info(cds)("Number of classes %d", _num_instance_klasses + _num_obj_array_klasses + _num_type_array_klasses);
|
||||
log_info(cds)(" instance classes = %5d", _num_instance_klasses);
|
||||
log_info(cds)(" obj array classes = %5d", _num_obj_array_klasses);
|
||||
log_info(cds)(" type array classes = %5d", _num_type_array_klasses);
|
||||
log_info(cds)(" symbols = %5d", _symbols->length());
|
||||
|
||||
if (DumpSharedSpaces) {
|
||||
// To ensure deterministic contents in the static archive, we need to ensure that
|
||||
// we iterate the MetaspaceObjs in a deterministic order. It doesn't matter where
|
||||
@ -731,31 +714,85 @@ void ArchiveBuilder::relocate_vm_classes() {
|
||||
}
|
||||
|
||||
void ArchiveBuilder::make_klasses_shareable() {
|
||||
int num_instance_klasses = 0;
|
||||
int num_boot_klasses = 0;
|
||||
int num_platform_klasses = 0;
|
||||
int num_app_klasses = 0;
|
||||
int num_hidden_klasses = 0;
|
||||
int num_unlinked_klasses = 0;
|
||||
int num_unregistered_klasses = 0;
|
||||
int num_obj_array_klasses = 0;
|
||||
int num_type_array_klasses = 0;
|
||||
|
||||
for (int i = 0; i < klasses()->length(); i++) {
|
||||
const char* type;
|
||||
const char* unlinked = "";
|
||||
const char* hidden = "";
|
||||
Klass* k = klasses()->at(i);
|
||||
k->remove_java_mirror();
|
||||
if (k->is_objArray_klass()) {
|
||||
// InstanceKlass and TypeArrayKlass will in turn call remove_unshareable_info
|
||||
// on their array classes.
|
||||
num_obj_array_klasses ++;
|
||||
type = "array";
|
||||
} else if (k->is_typeArray_klass()) {
|
||||
num_type_array_klasses ++;
|
||||
type = "array";
|
||||
k->remove_unshareable_info();
|
||||
} else {
|
||||
assert(k->is_instance_klass(), " must be");
|
||||
num_instance_klasses ++;
|
||||
InstanceKlass* ik = InstanceKlass::cast(k);
|
||||
if (DynamicDumpSharedSpaces) {
|
||||
// For static dump, class loader type are already set.
|
||||
ik->assign_class_loader_type();
|
||||
}
|
||||
if (ik->is_shared_boot_class()) {
|
||||
type = "boot";
|
||||
num_boot_klasses ++;
|
||||
} else if (ik->is_shared_platform_class()) {
|
||||
type = "plat";
|
||||
num_platform_klasses ++;
|
||||
} else if (ik->is_shared_app_class()) {
|
||||
type = "app";
|
||||
num_app_klasses ++;
|
||||
} else {
|
||||
assert(ik->is_shared_unregistered_class(), "must be");
|
||||
type = "unreg";
|
||||
num_unregistered_klasses ++;
|
||||
}
|
||||
|
||||
if (!ik->is_linked()) {
|
||||
num_unlinked_klasses ++;
|
||||
unlinked = " ** unlinked";
|
||||
}
|
||||
|
||||
if (ik->is_hidden()) {
|
||||
num_hidden_klasses ++;
|
||||
hidden = " ** hidden";
|
||||
}
|
||||
|
||||
MetaspaceShared::rewrite_nofast_bytecodes_and_calculate_fingerprints(Thread::current(), ik);
|
||||
ik->remove_unshareable_info();
|
||||
}
|
||||
|
||||
if (log_is_enabled(Debug, cds, class)) {
|
||||
ResourceMark rm;
|
||||
log_debug(cds, class)("klasses[%4d] = " PTR_FORMAT " %s", i, p2i(to_requested(ik)), ik->external_name());
|
||||
}
|
||||
if (log_is_enabled(Debug, cds, class)) {
|
||||
ResourceMark rm;
|
||||
log_debug(cds, class)("klasses[%5d] = " PTR_FORMAT " %-5s %s%s%s", i, p2i(to_requested(k)), type, k->external_name(), hidden, unlinked);
|
||||
}
|
||||
}
|
||||
|
||||
log_info(cds)("Number of classes %d", num_instance_klasses + num_obj_array_klasses + num_type_array_klasses);
|
||||
log_info(cds)(" instance classes = %5d", num_instance_klasses);
|
||||
log_info(cds)(" boot = %5d", num_boot_klasses);
|
||||
log_info(cds)(" app = %5d", num_app_klasses);
|
||||
log_info(cds)(" platform = %5d", num_platform_klasses);
|
||||
log_info(cds)(" unregistered = %5d", num_unregistered_klasses);
|
||||
log_info(cds)(" (hidden) = %5d", num_hidden_klasses);
|
||||
log_info(cds)(" (unlinked) = %5d", num_unlinked_klasses);
|
||||
log_info(cds)(" obj array classes = %5d", num_obj_array_klasses);
|
||||
log_info(cds)(" type array classes = %5d", num_type_array_klasses);
|
||||
log_info(cds)(" symbols = %5d", _symbols->length());
|
||||
}
|
||||
|
||||
uintx ArchiveBuilder::buffer_to_offset(address p) const {
|
||||
|
||||
@ -205,9 +205,6 @@ private:
|
||||
GrowableArray<SpecialRefInfo>* _special_refs;
|
||||
|
||||
// statistics
|
||||
int _num_instance_klasses;
|
||||
int _num_obj_array_klasses;
|
||||
int _num_type_array_klasses;
|
||||
DumpAllocStats _alloc_stats;
|
||||
size_t _total_closed_heap_region_size;
|
||||
size_t _total_open_heap_region_size;
|
||||
|
||||
@ -330,23 +330,24 @@ void ReadClosure::do_region(u_char* start, size_t size) {
|
||||
}
|
||||
}
|
||||
|
||||
fileStream* ClassListWriter::_classlist_file = NULL;
|
||||
|
||||
void ArchiveUtils::log_to_classlist(BootstrapInfo* bootstrap_specifier, TRAPS) {
|
||||
if (ClassListWriter::is_enabled()) {
|
||||
if (SystemDictionaryShared::is_supported_invokedynamic(bootstrap_specifier)) {
|
||||
ResourceMark rm(THREAD);
|
||||
const constantPoolHandle& pool = bootstrap_specifier->pool();
|
||||
int pool_index = bootstrap_specifier->bss_index();
|
||||
ClassListWriter w;
|
||||
w.stream()->print("%s %s", LAMBDA_PROXY_TAG, pool->pool_holder()->name()->as_C_string());
|
||||
CDSIndyInfo cii;
|
||||
ClassListParser::populate_cds_indy_info(pool, pool_index, &cii, CHECK);
|
||||
GrowableArray<const char*>* indy_items = cii.items();
|
||||
for (int i = 0; i < indy_items->length(); i++) {
|
||||
w.stream()->print(" %s", indy_items->at(i));
|
||||
if (SystemDictionaryShared::is_builtin_loader(pool->pool_holder()->class_loader_data())) {
|
||||
// Currently lambda proxy classes are supported only for the built-in loaders.
|
||||
ResourceMark rm(THREAD);
|
||||
int pool_index = bootstrap_specifier->bss_index();
|
||||
ClassListWriter w;
|
||||
w.stream()->print("%s %s", LAMBDA_PROXY_TAG, pool->pool_holder()->name()->as_C_string());
|
||||
CDSIndyInfo cii;
|
||||
ClassListParser::populate_cds_indy_info(pool, pool_index, &cii, CHECK);
|
||||
GrowableArray<const char*>* indy_items = cii.items();
|
||||
for (int i = 0; i < indy_items->length(); i++) {
|
||||
w.stream()->print(" %s", indy_items->at(i));
|
||||
}
|
||||
w.stream()->cr();
|
||||
}
|
||||
w.stream()->cr();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
175
src/hotspot/share/cds/classListWriter.cpp
Normal file
175
src/hotspot/share/cds/classListWriter.cpp
Normal file
@ -0,0 +1,175 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "precompiled.hpp"
|
||||
#include "cds/classListWriter.hpp"
|
||||
#include "classfile/classFileStream.hpp"
|
||||
#include "classfile/classLoader.hpp"
|
||||
#include "classfile/classLoaderData.hpp"
|
||||
#include "classfile/moduleEntry.hpp"
|
||||
#include "classfile/systemDictionaryShared.hpp"
|
||||
#include "memory/resourceArea.hpp"
|
||||
#include "oops/instanceKlass.hpp"
|
||||
#include "runtime/mutexLocker.hpp"
|
||||
|
||||
fileStream* ClassListWriter::_classlist_file = NULL;
|
||||
|
||||
void ClassListWriter::init() {
|
||||
// For -XX:DumpLoadedClassList=<file> option
|
||||
if (DumpLoadedClassList != NULL) {
|
||||
const char* list_name = make_log_name(DumpLoadedClassList, NULL);
|
||||
_classlist_file = new(ResourceObj::C_HEAP, mtInternal)
|
||||
fileStream(list_name);
|
||||
_classlist_file->print_cr("# NOTE: Do not modify this file.");
|
||||
_classlist_file->print_cr("#");
|
||||
_classlist_file->print_cr("# This file is generated via the -XX:DumpLoadedClassList=<class_list_file> option");
|
||||
_classlist_file->print_cr("# and is used at CDS archive dump time (see -Xshare:dump).");
|
||||
_classlist_file->print_cr("#");
|
||||
FREE_C_HEAP_ARRAY(char, list_name);
|
||||
}
|
||||
}
|
||||
|
||||
void ClassListWriter::write(const InstanceKlass* k, const ClassFileStream* cfs) {
|
||||
assert(is_enabled(), "must be");
|
||||
|
||||
if (!ClassLoader::has_jrt_entry()) {
|
||||
warning("DumpLoadedClassList and CDS are not supported in exploded build");
|
||||
DumpLoadedClassList = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
ClassListWriter w;
|
||||
write_to_stream(k, w.stream(), cfs);
|
||||
}
|
||||
|
||||
class ClassListWriter::IDTable : public ResourceHashtable<
|
||||
const InstanceKlass*, int,
|
||||
15889, // prime number
|
||||
ResourceObj::C_HEAP> {};
|
||||
|
||||
ClassListWriter::IDTable* ClassListWriter::_id_table = NULL;
|
||||
int ClassListWriter::_total_ids = 0;
|
||||
|
||||
int ClassListWriter::get_id(const InstanceKlass* k) {
|
||||
assert_locked();
|
||||
if (_id_table == NULL) {
|
||||
_id_table = new (ResourceObj::C_HEAP, mtClass)IDTable();
|
||||
}
|
||||
bool created;
|
||||
int* v = _id_table->put_if_absent(k, &created);
|
||||
if (created) {
|
||||
*v = _total_ids++;
|
||||
}
|
||||
return *v;
|
||||
}
|
||||
|
||||
bool ClassListWriter::has_id(const InstanceKlass* k) {
|
||||
assert_locked();
|
||||
if (_id_table != NULL) {
|
||||
return _id_table->get(k) != NULL;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void ClassListWriter::handle_class_unloading(const InstanceKlass* klass) {
|
||||
assert_locked();
|
||||
if (_id_table != NULL) {
|
||||
_id_table->remove(klass);
|
||||
}
|
||||
}
|
||||
|
||||
void ClassListWriter::write_to_stream(const InstanceKlass* k, outputStream* stream, const ClassFileStream* cfs) {
|
||||
assert_locked();
|
||||
ClassLoaderData* loader_data = k->class_loader_data();
|
||||
|
||||
if (!SystemDictionaryShared::is_builtin_loader(loader_data)) {
|
||||
if (cfs == NULL || strncmp(cfs->source(), "file:", 5) != 0) {
|
||||
return;
|
||||
}
|
||||
if (!SystemDictionaryShared::add_unregistered_class(Thread::current(), (InstanceKlass*)k)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
InstanceKlass* super = k->java_super();
|
||||
if (super != NULL && !has_id(super)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Array<InstanceKlass*>* interfaces = k->local_interfaces();
|
||||
int len = interfaces->length();
|
||||
for (int i = 0; i < len; i++) {
|
||||
InstanceKlass* intf = interfaces->at(i);
|
||||
if (!has_id(intf)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (k->is_hidden()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (k->module()->is_patched()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ResourceMark rm;
|
||||
stream->print("%s id: %d", k->name()->as_C_string(), get_id(k));
|
||||
if (!SystemDictionaryShared::is_builtin_loader(loader_data)) {
|
||||
InstanceKlass* super = k->java_super();
|
||||
assert(super != NULL, "must be");
|
||||
stream->print(" super: %d", get_id(super));
|
||||
|
||||
Array<InstanceKlass*>* interfaces = k->local_interfaces();
|
||||
int len = interfaces->length();
|
||||
if (len > 0) {
|
||||
stream->print(" interfaces:");
|
||||
for (int i = 0; i < len; i++) {
|
||||
InstanceKlass* intf = interfaces->at(i);
|
||||
stream->print(" %d", get_id(intf));
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef _WINDOWS
|
||||
// "file:/C:/dir/foo.jar" -> "C:/dir/foo.jar"
|
||||
stream->print(" source: %s", cfs->source() + 6);
|
||||
#else
|
||||
// "file:/dir/foo.jar" -> "/dir/foo.jar"
|
||||
stream->print(" source: %s", cfs->source() + 5);
|
||||
#endif
|
||||
}
|
||||
|
||||
stream->cr();
|
||||
stream->flush();
|
||||
}
|
||||
|
||||
void ClassListWriter::delete_classlist() {
|
||||
if (_classlist_file != NULL) {
|
||||
delete _classlist_file;
|
||||
}
|
||||
}
|
||||
@ -29,54 +29,44 @@
|
||||
#include "runtime/thread.hpp"
|
||||
#include "utilities/ostream.hpp"
|
||||
|
||||
class ClassListWriter {
|
||||
friend const char* make_log_name(const char* log_name, const char* force_directory);
|
||||
class ClassFileStream;
|
||||
|
||||
static fileStream* _classlist_file;
|
||||
MutexLocker _locker;
|
||||
public:
|
||||
class ClassListWriter {
|
||||
#if INCLUDE_CDS
|
||||
class IDTable;
|
||||
static fileStream* _classlist_file;
|
||||
static IDTable* _id_table;
|
||||
static int _total_ids;
|
||||
MutexLocker _locker;
|
||||
|
||||
static int get_id(const InstanceKlass* k);
|
||||
static bool has_id(const InstanceKlass* k);
|
||||
static void assert_locked() { assert_lock_strong(ClassListFile_lock); }
|
||||
public:
|
||||
ClassListWriter() : _locker(Thread::current(), ClassListFile_lock, Mutex::_no_safepoint_check_flag) {}
|
||||
#else
|
||||
ClassListWriter() : _locker(Thread::current(), NULL, Mutex::_no_safepoint_check_flag) {}
|
||||
#endif
|
||||
|
||||
outputStream* stream() {
|
||||
return _classlist_file;
|
||||
}
|
||||
|
||||
void handle_class_unloading(const InstanceKlass* klass);
|
||||
|
||||
static bool is_enabled() {
|
||||
#if INCLUDE_CDS
|
||||
return _classlist_file != NULL && _classlist_file->is_open();
|
||||
}
|
||||
|
||||
#else
|
||||
public:
|
||||
static bool is_enabled() {
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
#endif // INCLUDE_CDS
|
||||
|
||||
static void init() {
|
||||
#if INCLUDE_CDS
|
||||
// For -XX:DumpLoadedClassList=<file> option
|
||||
if (DumpLoadedClassList != NULL) {
|
||||
const char* list_name = make_log_name(DumpLoadedClassList, NULL);
|
||||
_classlist_file = new(ResourceObj::C_HEAP, mtInternal)
|
||||
fileStream(list_name);
|
||||
_classlist_file->print_cr("# NOTE: Do not modify this file.");
|
||||
_classlist_file->print_cr("#");
|
||||
_classlist_file->print_cr("# This file is generated via the -XX:DumpLoadedClassList=<class_list_file> option");
|
||||
_classlist_file->print_cr("# and is used at CDS archive dump time (see -Xshare:dump).");
|
||||
_classlist_file->print_cr("#");
|
||||
FREE_C_HEAP_ARRAY(char, list_name);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void delete_classlist() {
|
||||
#if INCLUDE_CDS
|
||||
if (_classlist_file != NULL) {
|
||||
delete _classlist_file;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
static void init() NOT_CDS_RETURN;
|
||||
static void write(const InstanceKlass* k, const ClassFileStream* cfs) NOT_CDS_RETURN;
|
||||
static void write_to_stream(const InstanceKlass* k, outputStream* stream, const ClassFileStream* cfs = NULL) NOT_CDS_RETURN;
|
||||
static void delete_classlist() NOT_CDS_RETURN;
|
||||
};
|
||||
|
||||
#endif // SHARE_CDS_CLASSLISTWRITER_HPP
|
||||
|
||||
@ -26,6 +26,7 @@
|
||||
#include "jvm_io.h"
|
||||
#include "cds/archiveBuilder.hpp"
|
||||
#include "cds/cdsProtectionDomain.hpp"
|
||||
#include "cds/classListWriter.hpp"
|
||||
#include "cds/classListParser.hpp"
|
||||
#include "cds/cppVtables.hpp"
|
||||
#include "cds/dumpAllocStats.hpp"
|
||||
@ -75,6 +76,7 @@
|
||||
#include "utilities/bitMap.inline.hpp"
|
||||
#include "utilities/ostream.hpp"
|
||||
#include "utilities/defaultStream.hpp"
|
||||
#include "utilities/resourceHash.hpp"
|
||||
#if INCLUDE_G1GC
|
||||
#include "gc/g1/g1CollectedHeap.inline.hpp"
|
||||
#endif
|
||||
@ -148,16 +150,43 @@ static bool shared_base_valid(char* shared_base) {
|
||||
}
|
||||
|
||||
class DumpClassListCLDClosure : public CLDClosure {
|
||||
static const int INITIAL_TABLE_SIZE = 1987;
|
||||
static const int MAX_TABLE_SIZE = 61333;
|
||||
|
||||
fileStream *_stream;
|
||||
ResizeableResourceHashtable<InstanceKlass*, bool,
|
||||
ResourceObj::C_HEAP, mtClassShared> _dumped_classes;
|
||||
|
||||
void dump(InstanceKlass* ik) {
|
||||
bool created;
|
||||
_dumped_classes.put_if_absent(ik, &created);
|
||||
if (!created) {
|
||||
return;
|
||||
}
|
||||
if (_dumped_classes.maybe_grow(MAX_TABLE_SIZE)) {
|
||||
log_info(cds, hashtables)("Expanded _dumped_classes table to %d", _dumped_classes.table_size());
|
||||
}
|
||||
if (ik->java_super()) {
|
||||
dump(ik->java_super());
|
||||
}
|
||||
Array<InstanceKlass*>* interfaces = ik->local_interfaces();
|
||||
int len = interfaces->length();
|
||||
for (int i = 0; i < len; i++) {
|
||||
dump(interfaces->at(i));
|
||||
}
|
||||
ClassListWriter::write_to_stream(ik, _stream);
|
||||
}
|
||||
|
||||
public:
|
||||
DumpClassListCLDClosure(fileStream* f) : CLDClosure() { _stream = f; }
|
||||
DumpClassListCLDClosure(fileStream* f)
|
||||
: CLDClosure(), _dumped_classes(INITIAL_TABLE_SIZE) {
|
||||
_stream = f;
|
||||
}
|
||||
|
||||
void do_cld(ClassLoaderData* cld) {
|
||||
for (Klass* klass = cld->klasses(); klass != NULL; klass = klass->next_link()) {
|
||||
if (klass->is_instance_klass()) {
|
||||
InstanceKlass* ik = InstanceKlass::cast(klass);
|
||||
if (ik->is_shareable()) {
|
||||
_stream->print_cr("%s", ik->name()->as_C_string());
|
||||
}
|
||||
dump(InstanceKlass::cast(klass));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -167,6 +196,7 @@ void MetaspaceShared::dump_loaded_classes(const char* file_name, TRAPS) {
|
||||
fileStream stream(file_name, "w");
|
||||
if (stream.is_open()) {
|
||||
MutexLocker lock(ClassLoaderDataGraph_lock);
|
||||
MutexLocker lock2(ClassListFile_lock, Mutex::_no_safepoint_check_flag);
|
||||
DumpClassListCLDClosure collect_classes(&stream);
|
||||
ClassLoaderDataGraph::loaded_cld_do(&collect_classes);
|
||||
} else {
|
||||
|
||||
@ -26,6 +26,7 @@
|
||||
#include "cds/archiveUtils.hpp"
|
||||
#include "cds/archiveBuilder.hpp"
|
||||
#include "cds/classListParser.hpp"
|
||||
#include "cds/classListWriter.hpp"
|
||||
#include "cds/dynamicArchive.hpp"
|
||||
#include "cds/filemap.hpp"
|
||||
#include "cds/heapShared.hpp"
|
||||
@ -332,7 +333,7 @@ bool SystemDictionaryShared::check_for_exclusion_impl(InstanceKlass* k) {
|
||||
return false; // false == k should NOT be excluded
|
||||
}
|
||||
|
||||
bool SystemDictionaryShared::is_sharing_possible(ClassLoaderData* loader_data) {
|
||||
bool SystemDictionaryShared::is_builtin_loader(ClassLoaderData* loader_data) {
|
||||
oop class_loader = loader_data->class_loader();
|
||||
return (class_loader == NULL ||
|
||||
SystemDictionary::is_system_class_loader(class_loader) ||
|
||||
@ -433,7 +434,7 @@ bool SystemDictionaryShared::add_unregistered_class(Thread* current, InstanceKla
|
||||
// We don't allow duplicated unregistered classes with the same name.
|
||||
// We only archive the first class with that name that succeeds putting
|
||||
// itself into the table.
|
||||
Arguments::assert_is_dumping_archive();
|
||||
assert(Arguments::is_dumping_archive() || ClassListWriter::is_enabled(), "sanity");
|
||||
MutexLocker ml(current, UnregisteredClassesTable_lock);
|
||||
Symbol* name = klass->name();
|
||||
if (_unregistered_classes_table == NULL) {
|
||||
@ -547,7 +548,9 @@ void SystemDictionaryShared::remove_dumptime_info(InstanceKlass* k) {
|
||||
}
|
||||
|
||||
void SystemDictionaryShared::handle_class_unloading(InstanceKlass* klass) {
|
||||
remove_dumptime_info(klass);
|
||||
if (Arguments::is_dumping_archive()) {
|
||||
remove_dumptime_info(klass);
|
||||
}
|
||||
|
||||
if (_unregistered_classes_table != NULL) {
|
||||
// Remove the class from _unregistered_classes_table: keep the entry but
|
||||
@ -559,6 +562,11 @@ void SystemDictionaryShared::handle_class_unloading(InstanceKlass* klass) {
|
||||
*v = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (ClassListWriter::is_enabled()) {
|
||||
ClassListWriter cw;
|
||||
cw.handle_class_unloading((const InstanceKlass*)klass);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if a class or any of its supertypes has been redefined.
|
||||
|
||||
@ -206,8 +206,7 @@ public:
|
||||
|
||||
static void allocate_shared_data_arrays(int size, TRAPS);
|
||||
|
||||
// Check if sharing is supported for the class loader.
|
||||
static bool is_sharing_possible(ClassLoaderData* loader_data);
|
||||
static bool is_builtin_loader(ClassLoaderData* loader_data);
|
||||
|
||||
static bool add_unregistered_class_for_static_archive(Thread* current, InstanceKlass* k);
|
||||
static InstanceKlass* lookup_super_for_unregistered_class(Symbol* class_name,
|
||||
|
||||
@ -676,9 +676,7 @@ void InstanceKlass::deallocate_contents(ClassLoaderData* loader_data) {
|
||||
}
|
||||
set_annotations(NULL);
|
||||
|
||||
if (Arguments::is_dumping_archive()) {
|
||||
SystemDictionaryShared::handle_class_unloading(this);
|
||||
}
|
||||
SystemDictionaryShared::handle_class_unloading(this);
|
||||
}
|
||||
|
||||
bool InstanceKlass::is_record() const {
|
||||
@ -2620,9 +2618,7 @@ void InstanceKlass::unload_class(InstanceKlass* ik) {
|
||||
// notify ClassLoadingService of class unload
|
||||
ClassLoadingService::notify_class_unloaded(ik);
|
||||
|
||||
if (Arguments::is_dumping_archive()) {
|
||||
SystemDictionaryShared::handle_class_unloading(ik);
|
||||
}
|
||||
SystemDictionaryShared::handle_class_unloading(ik);
|
||||
|
||||
if (log_is_enabled(Info, class, unload)) {
|
||||
ResourceMark rm;
|
||||
@ -3591,7 +3587,9 @@ const char* InstanceKlass::internal_name() const {
|
||||
void InstanceKlass::print_class_load_logging(ClassLoaderData* loader_data,
|
||||
const ModuleEntry* module_entry,
|
||||
const ClassFileStream* cfs) const {
|
||||
log_to_classlist();
|
||||
if (ClassListWriter::is_enabled()) {
|
||||
ClassListWriter::write(this, cfs);
|
||||
}
|
||||
|
||||
if (!log_is_enabled(Info, class, load)) {
|
||||
return;
|
||||
@ -4132,45 +4130,6 @@ unsigned char * InstanceKlass::get_cached_class_file_bytes() {
|
||||
}
|
||||
#endif
|
||||
|
||||
bool InstanceKlass::is_shareable() const {
|
||||
#if INCLUDE_CDS
|
||||
ClassLoaderData* loader_data = class_loader_data();
|
||||
if (!SystemDictionaryShared::is_sharing_possible(loader_data)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (is_hidden()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (module()->is_patched()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void InstanceKlass::log_to_classlist() const {
|
||||
#if INCLUDE_CDS
|
||||
ResourceMark rm;
|
||||
if (ClassListWriter::is_enabled()) {
|
||||
if (!ClassLoader::has_jrt_entry()) {
|
||||
warning("DumpLoadedClassList and CDS are not supported in exploded build");
|
||||
DumpLoadedClassList = NULL;
|
||||
return;
|
||||
}
|
||||
if (is_shareable()) {
|
||||
ClassListWriter w;
|
||||
w.stream()->print_cr("%s", name()->as_C_string());
|
||||
w.stream()->flush();
|
||||
}
|
||||
}
|
||||
#endif // INCLUDE_CDS
|
||||
}
|
||||
|
||||
// Make a step iterating over the class hierarchy under the root class.
|
||||
// Skips subclasses if requested.
|
||||
void ClassHierarchyIterator::next() {
|
||||
|
||||
@ -27,8 +27,9 @@
|
||||
* @summary DumpLoadedClassList should exclude generated classes, classes in bootclasspath/a and
|
||||
* --patch-module.
|
||||
* @requires vm.cds
|
||||
* @modules jdk.jfr
|
||||
* @library /test/lib
|
||||
* @compile test-classes/ArrayListTest.java
|
||||
* @compile test-classes/DumpClassListApp.java
|
||||
* @run driver DumpClassList
|
||||
*/
|
||||
|
||||
@ -40,25 +41,25 @@ import jdk.test.lib.helpers.ClassFileInstaller;
|
||||
public class DumpClassList {
|
||||
public static void main(String[] args) throws Exception {
|
||||
// build The app
|
||||
String[] appClass = new String[] {"ArrayListTest"};
|
||||
String[] appClass = new String[] {"DumpClassListApp"};
|
||||
String classList = "app.list";
|
||||
|
||||
JarBuilder.build("app", appClass[0]);
|
||||
String appJar = TestCommon.getTestJar("app.jar");
|
||||
|
||||
// build patch-module
|
||||
String source = "package java.lang; " +
|
||||
String source = "package jdk.jfr; " +
|
||||
"public class NewClass { " +
|
||||
" static { " +
|
||||
" System.out.println(\"NewClass\"); "+
|
||||
" } " +
|
||||
"}";
|
||||
|
||||
ClassFileInstaller.writeClassToDisk("java/lang/NewClass",
|
||||
InMemoryJavaCompiler.compile("java.lang.NewClass", source, "--patch-module=java.base"),
|
||||
ClassFileInstaller.writeClassToDisk("jdk/jfr/NewClass",
|
||||
InMemoryJavaCompiler.compile("jdk.jfr.NewClass", source, "--patch-module=jdk.jfr"),
|
||||
System.getProperty("test.classes"));
|
||||
|
||||
String patchJar = JarBuilder.build("javabase", "java/lang/NewClass");
|
||||
String patchJar = JarBuilder.build("jdk_jfr", "jdk/jfr/NewClass");
|
||||
|
||||
// build bootclasspath/a
|
||||
String source2 = "package boot.append; " +
|
||||
@ -76,7 +77,7 @@ public class DumpClassList {
|
||||
|
||||
// dump class list
|
||||
CDSTestUtils.dumpClassList(classList,
|
||||
"--patch-module=java.base=" + patchJar,
|
||||
"--patch-module=jdk.jfr=" + patchJar,
|
||||
"-Xbootclasspath/a:" + appendJar,
|
||||
"-cp",
|
||||
appJar,
|
||||
@ -90,10 +91,14 @@ public class DumpClassList {
|
||||
.addPrefix("-cp", appJar,
|
||||
"-Xbootclasspath/a:" + appendJar,
|
||||
"-Xlog:class+load",
|
||||
"-Xlog:cds+class=debug",
|
||||
"-XX:SharedClassListFile=" + classList);
|
||||
CDSTestUtils.createArchiveAndCheck(opts)
|
||||
.shouldNotContain("Preload Warning: Cannot find java/lang/invoke/LambdaForm")
|
||||
.shouldNotContain("Preload Warning: Cannot find boot/append/Foo")
|
||||
.shouldContain("[info][class,load] boot.append.Foo");
|
||||
.shouldNotContain("Preload Warning: Cannot find jdk/jfr/NewClass")
|
||||
.shouldMatch(".info..class,load *. boot.append.Foo") // from -Xlog:class+load
|
||||
.shouldMatch("cds,class.*boot boot.append.Foo") // from -Xlog:cds+class
|
||||
.shouldNotMatch(".info..class,load *. jdk.jfr.NewClass"); // from -Xlog:class+load
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,127 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary -XX:DumpLoadedClassList should support custom loaders
|
||||
* @bug 8265602
|
||||
* @requires vm.cds
|
||||
* @requires vm.cds.custom.loaders
|
||||
* @library /test/lib
|
||||
* /test/hotspot/jtreg/runtime/cds/appcds
|
||||
* /test/hotspot/jtreg/runtime/cds/appcds/test-classes
|
||||
* test-classes
|
||||
* @build CustomLoaderApp OldClass CustomLoadee CustomLoadee2
|
||||
* CustomLoadee3Child CustomLoadee4WithLambda
|
||||
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar CustomLoaderApp
|
||||
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar custom.jar
|
||||
* OldClass CustomLoadee
|
||||
* CustomLoadee2 CustomInterface2_ia CustomInterface2_ib
|
||||
* CustomLoadee3 CustomLoadee3Child
|
||||
* CustomLoadee4WithLambda
|
||||
* @run driver CustomClassListDump
|
||||
*/
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import jdk.test.lib.cds.CDSOptions;
|
||||
import jdk.test.lib.cds.CDSTestUtils;
|
||||
import jdk.test.lib.helpers.ClassFileInstaller;
|
||||
|
||||
public class CustomClassListDump {
|
||||
private static String appJar = ClassFileInstaller.getJarPath("app.jar");
|
||||
private static String customJar = ClassFileInstaller.getJarPath("custom.jar");
|
||||
private static String classList = "app.list";
|
||||
private static String commandLine[] = {
|
||||
"-cp", appJar,
|
||||
"CustomLoaderApp",
|
||||
customJar,
|
||||
"unregistered",
|
||||
"CustomLoadee",
|
||||
"CustomLoadee2",
|
||||
"CustomLoadee3Child",
|
||||
"CustomLoadee4WithLambda",
|
||||
"OldClass",
|
||||
};
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
// Dump the classlist and check that custom-loader classes are in there.
|
||||
CDSTestUtils.dumpClassList(classList, commandLine)
|
||||
.assertNormalExit();
|
||||
|
||||
String listData = new String(Files.readAllBytes(Paths.get(classList)));
|
||||
check(listData, true, "CustomLoaderApp id: [0-9]+");
|
||||
check(listData, true, "CustomLoadee id: [0-9]+ super: [0-9]+ source: .*/custom.jar");
|
||||
check(listData, true, "CustomInterface2_ia id: [0-9]+ super: [0-9]+ source: .*/custom.jar");
|
||||
check(listData, true, "CustomInterface2_ib id: [0-9]+ super: [0-9]+ source: .*/custom.jar");
|
||||
check(listData, true, "CustomLoadee2 id: [0-9]+ super: [0-9]+ interfaces: [0-9]+ [0-9]+ source: .*/custom.jar");
|
||||
check(listData, true, "CustomLoadee3 id: [0-9]+ super: [0-9]+ source: .*/custom.jar");
|
||||
check(listData, true, "CustomLoadee3Child id: [0-9]+ super: [0-9]+ source: .*/custom.jar");
|
||||
check(listData, true, "CustomLoadee4WithLambda id: [0-9]+ super: [0-9]+ source: .*/custom.jar");
|
||||
|
||||
// We don't support archiving of Lambda proxies for custom loaders.
|
||||
check(listData, false, "@lambda-proxy.*CustomLoadee4WithLambda");
|
||||
|
||||
// Dump the static archive
|
||||
CDSOptions opts = (new CDSOptions())
|
||||
.addPrefix("-cp", appJar,
|
||||
"-Xlog:cds+class=debug",
|
||||
"-XX:SharedClassListFile=" + classList);
|
||||
CDSTestUtils.createArchiveAndCheck(opts)
|
||||
.shouldContain("unreg CustomLoadee")
|
||||
.shouldContain("unreg CustomLoadee2")
|
||||
.shouldContain("unreg CustomLoadee3Child")
|
||||
.shouldContain("unreg OldClass ** unlinked");
|
||||
|
||||
// Use the dumped static archive
|
||||
opts = (new CDSOptions())
|
||||
.setUseVersion(false)
|
||||
.addPrefix("-cp", appJar)
|
||||
.addSuffix("-Xlog:class+load,verification")
|
||||
.addSuffix(commandLine);
|
||||
CDSTestUtils.run(opts)
|
||||
.assertNormalExit("CustomLoadee source: shared objects file",
|
||||
"CustomLoadee2 source: shared objects file",
|
||||
"CustomLoadee3Child source: shared objects file",
|
||||
"OldClass source: shared objects file",
|
||||
"Verifying class OldClass with old format");
|
||||
}
|
||||
|
||||
static void check(String listData, boolean mustMatch, String regexp) throws Exception {
|
||||
Pattern pattern = Pattern.compile(regexp, Pattern.MULTILINE);
|
||||
Matcher matcher = pattern.matcher(listData);
|
||||
boolean found = matcher.find();
|
||||
if (mustMatch && !found) {
|
||||
System.out.println(listData);
|
||||
throw new RuntimeException("Pattern \"" + regexp + "\" not found in classlist");
|
||||
}
|
||||
|
||||
if (!mustMatch && found) {
|
||||
throw new RuntimeException("Pattern \"" + regexp + "\" found in in classlist: \""
|
||||
+ matcher.group() + "\"");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -63,15 +63,18 @@ public class CustomLoaderApp {
|
||||
String loaderType = args[1];
|
||||
log("loaderType = " + loaderType);
|
||||
|
||||
String testClass = args[2];
|
||||
log("testClass = " + testClass);
|
||||
|
||||
switch(loaderType) {
|
||||
case "unregistered":
|
||||
loadAndUseWithUnregisteredLoader(urls, testClass);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("loader type is wrong: " + loaderType);
|
||||
for (int i = 2; i < args.length; i++) {
|
||||
String testClass = args[i];
|
||||
log("testClass = " + testClass);
|
||||
|
||||
switch(loaderType) {
|
||||
case "unregistered":
|
||||
loadAndUseWithUnregisteredLoader(urls, testClass);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("loader type is wrong: " + loaderType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -87,7 +90,7 @@ public class CustomLoaderApp {
|
||||
private static Class loadAndCheck(ClassLoader loader, String className)
|
||||
throws ClassNotFoundException {
|
||||
Class c = loader.loadClass(className);
|
||||
log("class =" + c);
|
||||
log("class = " + c);
|
||||
log("loader = " + c.getClassLoader());
|
||||
|
||||
// Check that c is defined by the correct loader
|
||||
|
||||
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
|
||||
public class CustomLoadee4WithLambda {
|
||||
public static void test() {
|
||||
doit(() -> {
|
||||
System.out.println("Hello inside a Lambda expression");
|
||||
});
|
||||
}
|
||||
|
||||
static void doit(Runnable r) {
|
||||
r.run();
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -26,7 +26,7 @@ import java.util.*;
|
||||
|
||||
// This is a test case executed by DumpClassList.java to load classes
|
||||
// from various places to ensure that they are not written to the class list.
|
||||
public class ArrayListTest {
|
||||
public class DumpClassListApp {
|
||||
public static void main(String args[]) throws Exception {
|
||||
// The following lambda usage should generate various classes like
|
||||
// java.lang.invoke.LambdaForm$MH/1146743572. All of them should be excluded from
|
||||
@ -35,7 +35,7 @@ public class ArrayListTest {
|
||||
a.add("hello world.");
|
||||
a.forEach(str -> System.out.println(str));
|
||||
|
||||
System.out.println(Class.forName("java.lang.NewClass")); // should be excluded from the class list.
|
||||
System.out.println(Class.forName("jdk.jfr.NewClass")); // should be excluded from the class list.
|
||||
System.out.println(Class.forName("boot.append.Foo")); // should be excluded from the class list.
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user