< prev index next > src/hotspot/share/classfile/systemDictionaryShared.cpp
Print this page
#include "cds/cdsConfig.hpp"
#include "cds/classListParser.hpp"
#include "cds/classListWriter.hpp"
#include "cds/dynamicArchive.hpp"
#include "cds/filemap.hpp"
+ #include "cds/heapShared.hpp"
#include "cds/cdsProtectionDomain.hpp"
#include "cds/dumpTimeClassInfo.inline.hpp"
+ #include "cds/lambdaFormInvokers.inline.hpp"
#include "cds/metaspaceShared.hpp"
+ #include "cds/methodDataDictionary.hpp"
#include "cds/runTimeClassInfo.hpp"
#include "classfile/classFileStream.hpp"
#include "classfile/classLoader.hpp"
#include "classfile/classLoaderData.inline.hpp"
#include "classfile/classLoaderDataGraph.hpp"
#include "memory/oopFactory.hpp"
#include "memory/resourceArea.hpp"
#include "memory/universe.hpp"
#include "oops/instanceKlass.hpp"
#include "oops/klass.inline.hpp"
+ #include "oops/methodData.hpp"
+ #include "oops/trainingData.hpp"
#include "oops/objArrayKlass.hpp"
#include "oops/objArrayOop.inline.hpp"
#include "oops/oop.inline.hpp"
#include "oops/oopHandle.inline.hpp"
#include "oops/typeArrayOop.inline.hpp"
SystemDictionaryShared::ArchiveInfo SystemDictionaryShared::_dynamic_archive;
DumpTimeSharedClassTable* SystemDictionaryShared::_dumptime_table = nullptr;
DumpTimeLambdaProxyClassDictionary* SystemDictionaryShared::_dumptime_lambda_proxy_class_dictionary = nullptr;
+ DumpTimeMethodInfoDictionary* SystemDictionaryShared::_dumptime_method_info_dictionary = nullptr;
+ DumpTimeMethodInfoDictionary* SystemDictionaryShared::_cloned_dumptime_method_info_dictionary = nullptr;
+ static Array<InstanceKlass*>* _archived_lambda_form_classes = nullptr;
+ static Array<InstanceKlass*>* _archived_lambda_proxy_classes_boot = nullptr;
+ static Array<InstanceKlass*>* _archived_lambda_proxy_classes_boot2 = nullptr;
+ static Array<InstanceKlass*>* _archived_lambda_proxy_classes_platform = nullptr;
+ static Array<InstanceKlass*>* _archived_lambda_proxy_classes_app = nullptr;
+ static bool _ignore_new_classes = false;
+
// Used by NoClassLoadingMark
DEBUG_ONLY(bool SystemDictionaryShared::_class_loading_may_happen = true;)
InstanceKlass* SystemDictionaryShared::load_shared_class_for_builtin_loader(
Symbol* class_name, Handle class_loader, TRAPS) {
if (ik != nullptr && !ik->shared_loading_failed()) {
if ((SystemDictionary::is_system_class_loader(class_loader()) && ik->is_shared_app_class()) ||
(SystemDictionary::is_platform_class_loader(class_loader()) && ik->is_shared_platform_class())) {
SharedClassLoadingMark slm(THREAD, ik);
PackageEntry* pkg_entry = CDSProtectionDomain::get_package_entry_from_class(ik, class_loader);
- Handle protection_domain =
- CDSProtectionDomain::init_security_info(class_loader, ik, pkg_entry, CHECK_NULL);
+ Handle protection_domain;
+ if (!class_name->starts_with("jdk/proxy")) // java/lang/reflect/Proxy$ProxyBuilder defines the proxy classes with a null protection domain.
+ {
+ protection_domain = CDSProtectionDomain::init_security_info(class_loader, ik, pkg_entry, CHECK_NULL);
+ }
return load_shared_class(ik, class_loader, protection_domain, nullptr, pkg_entry, THREAD);
}
}
return nullptr;
}
// Guaranteed to return non-null value for non-shared classes.
// k must not be a shared class.
DumpTimeClassInfo* SystemDictionaryShared::get_info(InstanceKlass* k) {
MutexLocker ml(DumpTimeTable_lock, Mutex::_no_safepoint_check_flag);
- assert(!k->is_shared(), "sanity");
+ //assert(!k->is_shared(), "sanity"); // FIXME new workflow
return get_info_locked(k);
}
DumpTimeClassInfo* SystemDictionaryShared::get_info_locked(InstanceKlass* k) {
assert_lock_strong(DumpTimeTable_lock);
- assert(!k->is_shared(), "sanity");
+ //assert(!k->is_shared(), "sanity"); // FIXME new workflow
DumpTimeClassInfo* info = _dumptime_table->get_info(k);
assert(info != nullptr, "must be");
return info;
}
+ void SystemDictionaryShared::mark_required_class(InstanceKlass* k) {
+ DumpTimeClassInfo* info = _dumptime_table->get(k);
+ ResourceMark rm;
+ if (info != nullptr) {
+ info->set_is_required();
+ }
+ }
+
bool SystemDictionaryShared::check_for_exclusion(InstanceKlass* k, DumpTimeClassInfo* info) {
- if (MetaspaceShared::is_in_shared_metaspace(k)) {
+ if (!CDSConfig::is_dumping_final_static_archive() && MetaspaceShared::is_in_shared_metaspace(k)) {
// We have reached a super type that's already in the base archive. Treat it
// as "not excluded".
assert(CDSConfig::is_dumping_dynamic_archive(), "must be");
return false;
}
} else {
return false;
}
}
+ void SystemDictionaryShared::ignore_new_classes() {
+ _ignore_new_classes = true;
+ }
+
+
bool SystemDictionaryShared::check_for_exclusion_impl(InstanceKlass* k) {
+ if (CDSConfig::is_dumping_final_static_archive() && k->is_shared_unregistered_class()
+ && k->is_shared()) {
+ return false; // Do not exclude: unregistered classes are passed from preimage to final image.
+ }
+
if (k->is_in_error_state()) {
return warn_excluded(k, "In error state");
}
if (k->is_scratch_class()) {
return warn_excluded(k, "A scratch class");
return warn_excluded(k, "Has been redefined");
}
if (!k->is_hidden() && k->shared_classpath_index() < 0 && is_builtin(k)) {
if (k->name()->starts_with("java/lang/invoke/BoundMethodHandle$Species_")) {
// This class is dynamically generated by the JDK
- ResourceMark rm;
- log_info(cds)("Skipping %s because it is dynamically generated", k->name()->as_C_string());
- return true; // exclude without warning
+ if (CDSConfig::is_dumping_aot_linked_classes()) {
+ k->set_shared_classpath_index(0);
+ } else {
+ ResourceMark rm;
+ log_info(cds)("Skipping %s because it is dynamically generated", k->name()->as_C_string());
+ return true; // exclude without warning
+ }
} else {
// These are classes loaded from unsupported locations (such as those loaded by JVMTI native
// agent during dump time).
return warn_excluded(k, "Unsupported location");
}
if (is_jfr_event_class(k)) {
// We cannot include JFR event classes because they need runtime-specific
// instrumentation in order to work with -XX:FlightRecorderOptions:retransform=false.
// There are only a small number of these classes, so it's not worthwhile to
// support them and make CDS more complicated.
- return warn_excluded(k, "JFR event class");
+ if (!CDSConfig::is_dumping_reflection_data()) { // FIXME: !!! HACK !!!
+ return warn_excluded(k, "JFR event class");
+ }
}
- if (!k->is_linked()) {
- if (has_class_failed_verification(k)) {
- return warn_excluded(k, "Failed verification");
- }
- } else {
- if (!k->can_be_verified_at_dumptime()) {
- // We have an old class that has been linked (e.g., it's been executed during
- // dump time). This class has been verified using the old verifier, which
- // doesn't save the verification constraints, so check_verification_constraints()
- // won't work at runtime.
- // As a result, we cannot store this class. It must be loaded and fully verified
- // at runtime.
- return warn_excluded(k, "Old class has been linked");
+ if (!CDSConfig::preserve_all_dumptime_verification_states(k)) {
+ if (!k->is_linked()) {
+ if (has_class_failed_verification(k)) {
+ return warn_excluded(k, "Failed verification");
+ }
+ } else {
+ if (!k->can_be_verified_at_dumptime()) {
+ // We have an old class that has been linked (e.g., it's been executed during
+ // dump time). This class has been verified using the old verifier, which
+ // doesn't save the verification constraints, so check_verification_constraints()
+ // won't work at runtime.
+ // As a result, we cannot store this class. It must be loaded and fully verified
+ // at runtime.
+ return warn_excluded(k, "Old class has been linked");
+ }
}
}
- if (k->is_hidden() && !is_registered_lambda_proxy_class(k)) {
- ResourceMark rm;
- log_debug(cds)("Skipping %s: Hidden class", k->name()->as_C_string());
+ if (k->is_hidden() && !should_hidden_class_be_archived(k)) {
+ log_info(cds)("Skipping %s: Hidden class", k->name()->as_C_string());
return true;
}
InstanceKlass* super = k->java_super();
if (super != nullptr && check_for_exclusion(super, nullptr)) {
log_warning(cds)("Skipping %s: interface %s is excluded", k->name()->as_C_string(), intf->name()->as_C_string());
return true;
}
}
+ if (ClassLoaderExt::should_be_excluded(k)) {
+ ResourceMark rm;
+ log_info(cds)("Skipping %s: excluded via -XX:CacheOnlyClassesIn", k->name()->as_C_string());
+ return true;
+ }
+
return false; // false == k should NOT be excluded
}
bool SystemDictionaryShared::is_builtin_loader(ClassLoaderData* loader_data) {
oop class_loader = loader_data->class_loader();
void SystemDictionaryShared::initialize() {
if (CDSConfig::is_dumping_archive()) {
_dumptime_table = new (mtClass) DumpTimeSharedClassTable;
_dumptime_lambda_proxy_class_dictionary =
new (mtClass) DumpTimeLambdaProxyClassDictionary;
+ _dumptime_method_info_dictionary = new (mtClass) DumpTimeMethodInfoDictionary;
}
}
void SystemDictionaryShared::init_dumptime_info(InstanceKlass* k) {
MutexLocker ml(DumpTimeTable_lock, Mutex::_no_safepoint_check_flag);
assert(SystemDictionaryShared::class_loading_may_happen(), "sanity");
- _dumptime_table->allocate_info(k);
+ DumpTimeClassInfo* info = _dumptime_table->allocate_info(k);
+ if (_ignore_new_classes) {
+ if (!LambdaFormInvokers::may_be_regenerated_class(k->name())) {
+ ResourceMark rm;
+ log_debug(cds)("Skipping %s: Class loaded for lambda form invoker regeneration", k->name()->as_C_string());
+ info->set_excluded();
+ }
+ }
}
void SystemDictionaryShared::remove_dumptime_info(InstanceKlass* k) {
MutexLocker ml(DumpTimeTable_lock, Mutex::_no_safepoint_check_flag);
_dumptime_table->remove(k);
assert(!class_loading_may_happen(), "class loading must be disabled");
guarantee(info != nullptr, "Class %s must be entered into _dumptime_table", name);
guarantee(!info->is_excluded(), "Should not attempt to archive excluded class %s", name);
if (is_builtin(k)) {
if (k->is_hidden()) {
- assert(is_registered_lambda_proxy_class(k), "unexpected hidden class %s", name);
+ if (CDSConfig::is_dumping_invokedynamic()) { // FIXME -- clean up
+ return;
+ }
+ assert(should_hidden_class_be_archived(k), "unexpected hidden class %s", name);
}
guarantee(!k->is_shared_unregistered_class(),
"Class loader type must be set for BUILTIN class %s", name);
} else {
}
}
}
};
- void SystemDictionaryShared::check_excluded_classes() {
+ void SystemDictionaryShared::scan_constant_pool(InstanceKlass* k) {
+ k->constants()->find_archivable_hidden_classes();
+ }
+
+ bool SystemDictionaryShared::should_hidden_class_be_archived(InstanceKlass* k) {
+ assert(k->is_hidden(), "sanity");
+ if (is_registered_lambda_proxy_class(k)) {
+ return true;
+ }
+
+ if (CDSConfig::is_dumping_invokedynamic()) {
+ if (HeapShared::is_archivable_hidden_klass(k)) {
+ return true;
+ }
+
+ // TODO: merge the following with HeapShared::is_archivable_hidden_klass()
+ DumpTimeClassInfo* info = _dumptime_table->get(k);
+ if (info != nullptr && info->is_required()) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ // Returns true if the class should be excluded. This can be called before
+ // SystemDictionaryShared::find_all_archivable_classes().
+ bool SystemDictionaryShared::check_for_exclusion(Klass* k) {
+ assert(CDSConfig::is_dumping_archive(), "sanity");
+
+ if (k->is_objArray_klass()) {
+ return check_for_exclusion(ObjArrayKlass::cast(k)->bottom_klass());
+ }
+
+ if (!k->is_instance_klass()) {
+ return false;
+ } else {
+ InstanceKlass* ik = InstanceKlass::cast(k);
+
+ if (SafepointSynchronize::is_at_safepoint()) {
+ return is_excluded_class(ik);
+ }
+
+ if (!ik->is_linked()) {
+ JavaThread* THREAD = JavaThread::current();
+ ik->link_class(THREAD);
+ if (HAS_PENDING_EXCEPTION) {
+ CLEAR_PENDING_EXCEPTION;
+ return true;
+ }
+ }
+
+ MutexLocker ml(DumpTimeTable_lock, Mutex::_no_safepoint_check_flag);
+ DumpTimeClassInfo* p = get_info_locked(ik);
+ if (p->is_excluded()) {
+ return true;
+ }
+ return check_for_exclusion(ik, p);
+ }
+ }
+
+ void SystemDictionaryShared::find_all_archivable_classes() {
+ HeapShared::start_finding_archivable_hidden_classes();
+ find_all_archivable_classes_impl();
+ HeapShared::end_finding_archivable_hidden_classes();
+ }
+
+ // Iterate over all the classes in _dumptime_table, marking the ones that must be
+ // excluded from the archive. Those that are not excluded will be archivable.
+ //
+ // (a) Non-hidden classes are easy. They are only check by the rules in
+ // SystemDictionaryShared::check_for_exclusion().
+ // (b) For hidden classes, we only archive those that are required (i.e., they are
+ // referenced by Java objects (such as CallSites) that are reachable from
+ // ConstantPools.
+ void SystemDictionaryShared::find_all_archivable_classes_impl() {
assert(!class_loading_may_happen(), "class loading must be disabled");
assert_lock_strong(DumpTimeTable_lock);
if (CDSConfig::is_dumping_dynamic_archive()) {
// Do this first -- if a base class is excluded due to duplication,
UnregisteredClassesDuplicationChecker dup_checker;
_dumptime_table->iterate_all_live_classes(&dup_checker);
dup_checker.mark_duplicated_classes();
}
- auto check_for_exclusion = [&] (InstanceKlass* k, DumpTimeClassInfo& info) {
- SystemDictionaryShared::check_for_exclusion(k, &info);
+ ResourceMark rm;
+
+ // First, scan all non-hidden classes
+ auto check_non_hidden = [&] (InstanceKlass* k, DumpTimeClassInfo& info) {
+ if (!k->is_hidden()) {
+ SystemDictionaryShared::check_for_exclusion(k, &info);
+ if (!info.is_excluded() && !info.has_scanned_constant_pool()) {
+ scan_constant_pool(k);
+ info.set_has_scanned_constant_pool();
+ }
+ }
};
- _dumptime_table->iterate_all_live_classes(check_for_exclusion);
+ _dumptime_table->iterate_all_live_classes(check_non_hidden);
+
+ // Then, scan all the hidden classes that have been marked as required to
+ // discover more hidden classes. Stop when we cannot make progress anymore.
+ bool made_progress;
+ do {
+ made_progress = false;
+ auto check_hidden = [&] (InstanceKlass* k, DumpTimeClassInfo& info) {
+ if (k->is_hidden() && should_hidden_class_be_archived(k)) {
+ SystemDictionaryShared::check_for_exclusion(k, &info);
+ if (info.is_excluded()) {
+ guarantee(!info.is_required(), "A required hidden class cannot be marked as excluded");
+ } else if (!info.has_scanned_constant_pool()) {
+ scan_constant_pool(k);
+ info.set_has_scanned_constant_pool();
+ // The CP entries in k *MAY* refer to other hidden classes, so scan
+ // every hidden class again.
+ made_progress = true;
+ }
+ }
+ };
+ _dumptime_table->iterate_all_live_classes(check_hidden);
+ } while (made_progress);
+
+ // Now, all hidden classes that have not yet been scanned must be marked as excluded
+ auto exclude_remaining_hidden = [&] (InstanceKlass* k, DumpTimeClassInfo& info) {
+ if (k->is_hidden() && !info.has_checked_exclusion()) {
+ SystemDictionaryShared::check_for_exclusion(k, &info);
+ guarantee(info.is_excluded(), "Must be");
+ }
+ };
+ _dumptime_table->iterate_all_live_classes(exclude_remaining_hidden);
_dumptime_table->update_counts();
cleanup_lambda_proxy_class_dictionary();
+
+ cleanup_method_info_dictionary();
+
+ TrainingData::cleanup_training_data();
}
bool SystemDictionaryShared::is_excluded_class(InstanceKlass* k) {
assert(!class_loading_may_happen(), "class loading must be disabled");
assert_lock_strong(DumpTimeTable_lock);
void SystemDictionaryShared::dumptime_classes_do(class MetaspaceClosure* it) {
assert_lock_strong(DumpTimeTable_lock);
auto do_klass = [&] (InstanceKlass* k, DumpTimeClassInfo& info) {
- if (k->is_loader_alive() && !info.is_excluded()) {
+ if (CDSConfig::is_dumping_final_static_archive() && !k->is_loaded()) {
+ assert(k->is_shared_unregistered_class(), "must be");
+ info.metaspace_pointers_do(it);
+ } else if (k->is_loader_alive() && !info.is_excluded()) {
info.metaspace_pointers_do(it);
}
};
_dumptime_table->iterate_all_live_classes(do_klass);
info.metaspace_pointers_do(it);
key.metaspace_pointers_do(it);
}
};
_dumptime_lambda_proxy_class_dictionary->iterate_all(do_lambda);
+
+ auto do_method_info = [&] (MethodDataKey& key, DumpTimeMethodDataInfo& info) {
+ info.metaspace_pointers_do(it);
+ key.metaspace_pointers_do(it);
+ };
+ _dumptime_method_info_dictionary->iterate_all(do_method_info);
}
bool SystemDictionaryShared::add_verification_constraint(InstanceKlass* k, Symbol* name,
Symbol* from_name, bool from_field_is_protected, bool from_is_array, bool from_is_object) {
assert(CDSConfig::is_dumping_archive(), "sanity");
+ if (CDSConfig::is_dumping_dynamic_archive() && k->is_shared()) {
+ // k is a new class in the static archive, but one of its supertypes is an old class, so k wasn't
+ // verified during dump time. No need to record constraints as k won't be included in the dynamic archive.
+ return false;
+ }
+ if (CDSConfig::is_dumping_aot_linked_classes() && is_builtin(k)) {
+ // There's no need to save verification constraints
+ // TODO -- double check the logic before integrating into mainline!!
+ return false;
+ }
+
DumpTimeClassInfo* info = get_info(k);
info->add_verification_constraint(k, name, from_name, from_field_is_protected,
from_is_array, from_is_object);
if (CDSConfig::is_dumping_dynamic_archive()) {
Symbol* invoked_type,
Symbol* method_type,
Method* member_method,
Symbol* instantiated_method_type,
TRAPS) {
+ if (CDSConfig::is_dumping_invokedynamic()) {
+ // The lambda proxy classes will be stored as part of aot-resolved constant pool entries.
+ // There's no need to remember them in a separate table.
+ return;
+ }
+ if (CDSConfig::is_dumping_preimage_static_archive() || CDSConfig::is_dumping_final_static_archive()) {
+ // TODO: not supported in new workflow
+ return;
+ }
assert(caller_ik->class_loader() == lambda_ik->class_loader(), "mismatched class loader");
assert(caller_ik->class_loader_data() == lambda_ik->class_loader_data(), "mismatched class loader data");
assert(java_lang_Class::class_data(lambda_ik->java_mirror()) == nullptr, "must not have class data");
Symbol* invoked_name,
Symbol* invoked_type,
Symbol* method_type,
Method* member_method,
Symbol* instantiated_method_type) {
+ if (CDSConfig::is_dumping_final_static_archive()) {
+ return nullptr;
+ }
MutexLocker ml(CDSLambda_lock, Mutex::_no_safepoint_check_flag);
LambdaProxyClassKey key(caller_ik, invoked_name, invoked_type,
method_type, member_method, instantiated_method_type);
// Try to retrieve the lambda proxy class from static archive.
prev_klass->set_next_link(nullptr);
proxy_klass = curr_klass;
proxy_klass->clear_lambda_proxy_is_available();
if (log_is_enabled(Debug, cds)) {
ResourceMark rm;
- log_debug(cds)("Loaded lambda proxy: %s ", proxy_klass->external_name());
+ log_debug(cds)("Loaded lambda proxy: " PTR_FORMAT " %s ", p2i(proxy_klass), proxy_klass->external_name());
}
}
}
return proxy_klass;
}
return loaded_lambda;
}
void SystemDictionaryShared::check_verification_constraints(InstanceKlass* klass,
TRAPS) {
- assert(!CDSConfig::is_dumping_static_archive() && CDSConfig::is_using_archive(), "called at run time with CDS enabled only");
+ //assert(!CDSConfig::is_dumping_static_archive() && CDSConfig::is_using_archive(), "called at run time with CDS enabled only");
RunTimeClassInfo* record = RunTimeClassInfo::get_for(klass);
int length = record->_num_verifier_constraints;
if (length > 0) {
for (int i = 0; i < length; i++) {
}
// returns true IFF there's no need to re-initialize the i/v-tables for klass for
// the purpose of checking class loader constraints.
bool SystemDictionaryShared::check_linking_constraints(Thread* current, InstanceKlass* klass) {
- assert(!CDSConfig::is_dumping_static_archive() && CDSConfig::is_using_archive(), "called at run time with CDS enabled only");
+ //assert(!CDSConfig::is_dumping_static_archive() && CDSConfig::is_using_archive(), "called at run time with CDS enabled only");
LogTarget(Info, class, loader, constraints) log;
if (klass->is_shared_boot_class()) {
// No class loader constraint check performed for boot classes.
return true;
}
size_t bytesize = align_up(sizeof(RunTimeLambdaProxyClassInfo), SharedSpaceObjectAlignment);
total_size +=
(bytesize * _dumptime_lambda_proxy_class_dictionary->_count) +
CompactHashtableWriter::estimate_size(_dumptime_lambda_proxy_class_dictionary->_count);
+ size_t method_info_byte_size = align_up(sizeof(RunTimeMethodDataInfo), SharedSpaceObjectAlignment);
+ total_size +=
+ (method_info_byte_size * _dumptime_method_info_dictionary->_count) +
+ CompactHashtableWriter::estimate_size(_dumptime_method_info_dictionary->_count);
+
return total_size;
}
unsigned int SystemDictionaryShared::hash_for_shared_dictionary(address ptr) {
- if (ArchiveBuilder::is_active()) {
+ if (ArchiveBuilder::is_active() && ArchiveBuilder::current()->is_in_buffer_space(ptr)) {
uintx offset = ArchiveBuilder::current()->any_to_offset(ptr);
unsigned int hash = primitive_hash<uintx>(offset);
DEBUG_ONLY({
if (MetaspaceObj::is_shared((const MetaspaceObj*)ptr)) {
assert(hash == SystemDictionaryShared::hash_for_shared_dictionary_quick(ptr), "must be");
: _writer(writer), _builder(ArchiveBuilder::current()) {}
bool do_entry(LambdaProxyClassKey& key, DumpTimeLambdaProxyClassInfo& info) {
// In static dump, info._proxy_klasses->at(0) is already relocated to point to the archived class
// (not the original class).
//
- // The following check has been moved to SystemDictionaryShared::check_excluded_classes(), which
+ // The following check has been moved to SystemDictionaryShared::find_all_archivable_classes(), which
// happens before the classes are copied.
//
// if (SystemDictionaryShared::is_excluded_class(info._proxy_klasses->at(0))) {
// return true;
//}
CopyLambdaProxyClassInfoToArchive copy(&writer);
_dumptime_lambda_proxy_class_dictionary->iterate(©);
writer.dump(dictionary, "lambda proxy class dictionary");
}
+ class CopyMethodDataInfoToArchive : StackObj {
+ CompactHashtableWriter* _writer;
+ ArchiveBuilder* _builder;
+ public:
+ CopyMethodDataInfoToArchive(CompactHashtableWriter* writer)
+ : _writer(writer), _builder(ArchiveBuilder::current()) {}
+
+ bool do_entry(MethodDataKey& key, DumpTimeMethodDataInfo& info) {
+ Method* holder = key.method();
+ log_info(cds,dynamic)("Archiving method info for %s", holder->external_name());
+
+ size_t byte_size = sizeof(RunTimeMethodDataInfo);
+ RunTimeMethodDataInfo* record = (RunTimeMethodDataInfo*)ArchiveBuilder::ro_region_alloc(byte_size);
+
+ DumpTimeMethodDataInfo data(info.method_data(), info.method_counters());
+ record->init(key, data);
+
+ uint hash = SystemDictionaryShared::hash_for_shared_dictionary((address)holder);
+ u4 delta = _builder->buffer_to_offset_u4((address)record);
+ _writer->add(hash, delta);
+
+ return true;
+ }
+ };
+
+ void SystemDictionaryShared::write_method_info_dictionary(MethodDataInfoDictionary* dictionary) {
+ CompactHashtableStats stats;
+ dictionary->reset();
+ CompactHashtableWriter writer(_dumptime_method_info_dictionary->_count, &stats);
+ CopyMethodDataInfoToArchive copy(&writer);
+ _dumptime_method_info_dictionary->iterate(©);
+ writer.dump(dictionary, "method info dictionary");
+ }
+
void SystemDictionaryShared::write_dictionary(RunTimeSharedDictionary* dictionary,
bool is_builtin) {
CompactHashtableStats stats;
dictionary->reset();
CompactHashtableWriter writer(_dumptime_table->count_of(is_builtin), &stats);
write_dictionary(&archive->_builtin_dictionary, true);
write_dictionary(&archive->_unregistered_dictionary, false);
write_lambda_proxy_class_dictionary(&archive->_lambda_proxy_class_dictionary);
+
+ write_method_info_dictionary(&archive->_method_info_dictionary);
}
void SystemDictionaryShared::adjust_lambda_proxy_class_dictionary() {
AdjustLambdaProxyClassInfo adjuster;
_dumptime_lambda_proxy_class_dictionary->iterate(&adjuster);
}
+ class AdjustMethodInfo : StackObj {
+ public:
+ AdjustMethodInfo() {}
+ bool do_entry(MethodDataKey& key, DumpTimeMethodDataInfo& info) {
+ // TODO: is it possible for the data to become stale/invalid?
+ MethodData* md = info.method_data();
+ MethodCounters* mc = info.method_counters();
+ if (md != nullptr) {
+ md = ArchiveBuilder::current()->get_buffered_addr(md);
+ }
+ if (mc != nullptr) {
+ mc = ArchiveBuilder::current()->get_buffered_addr(mc);
+ }
+ assert(ArchiveBuilder::current()->is_in_buffer_space(md) || md == nullptr, "must be");
+ assert(ArchiveBuilder::current()->is_in_buffer_space(mc) || mc == nullptr, "must be");
+ if (md != nullptr) {
+ md->remove_unshareable_info();
+ }
+ if (mc != nullptr) {
+ mc->remove_unshareable_info();
+ }
+ return true;
+ }
+ };
+
+ void SystemDictionaryShared::adjust_method_info_dictionary() {
+ AdjustMethodInfo adjuster;
+ _dumptime_method_info_dictionary->iterate(&adjuster);
+ }
+
void SystemDictionaryShared::serialize_dictionary_headers(SerializeClosure* soc,
bool is_static_archive) {
ArchiveInfo* archive = get_archive(is_static_archive);
archive->_builtin_dictionary.serialize_header(soc);
archive->_unregistered_dictionary.serialize_header(soc);
archive->_lambda_proxy_class_dictionary.serialize_header(soc);
+ archive->_method_info_dictionary.serialize_header(soc);
}
void SystemDictionaryShared::serialize_vm_classes(SerializeClosure* soc) {
for (auto id : EnumRange<vmClassID>{}) {
soc->do_ptr(vmClasses::klass_addr_at(id));
}
+ soc->do_ptr((void**)&_archived_lambda_form_classes);
+ soc->do_ptr((void**)&_archived_lambda_proxy_classes_boot);
+ soc->do_ptr((void**)&_archived_lambda_proxy_classes_boot2);
+ soc->do_ptr((void**)&_archived_lambda_proxy_classes_platform);
+ soc->do_ptr((void**)&_archived_lambda_proxy_classes_app);
}
const RunTimeClassInfo*
SystemDictionaryShared::find_record(RunTimeSharedDictionary* static_dict, RunTimeSharedDictionary* dynamic_dict, Symbol* name) {
if (!CDSConfig::is_using_archive() || !name->is_shared()) {
unsigned int hash = SystemDictionaryShared::hash_for_shared_dictionary_quick(name);
const RunTimeClassInfo* record = nullptr;
if (DynamicArchive::is_mapped()) {
// Use the regenerated holder classes in the dynamic archive as they
// have more methods than those in the base archive.
- if (name == vmSymbols::java_lang_invoke_Invokers_Holder() ||
- name == vmSymbols::java_lang_invoke_DirectMethodHandle_Holder() ||
- name == vmSymbols::java_lang_invoke_LambdaForm_Holder() ||
- name == vmSymbols::java_lang_invoke_DelegatingMethodHandle_Holder()) {
+ if (LambdaFormInvokers::may_be_regenerated_class(name)) {
record = dynamic_dict->lookup(name, hash, 0);
if (record != nullptr) {
return record;
}
}
assert(CDSConfig::is_dumping_static_archive(), "class ID is used only for static dump (from classlist)");
DumpTimeClassInfo* info = get_info(k);
info->_id = id;
}
- static const char* class_loader_name_for_shared(Klass* k) {
+ const char* SystemDictionaryShared::class_loader_name_for_shared(Klass* k) {
assert(k != nullptr, "Sanity");
assert(k->is_shared(), "Must be");
assert(k->is_instance_klass(), "Must be");
InstanceKlass* ik = InstanceKlass::cast(k);
if (ik->is_shared_boot_class()) {
SharedDictionaryPrinter(outputStream* st) : _st(st), _index(0) {}
void do_value(const RunTimeClassInfo* record) {
ResourceMark rm;
_st->print_cr("%4d: %s %s", _index++, record->_klass->external_name(),
- class_loader_name_for_shared(record->_klass));
+ SystemDictionaryShared::class_loader_name_for_shared(record->_klass));
if (record->_klass->array_klasses() != nullptr) {
record->_klass->array_klasses()->cds_print_value_on(_st);
_st->cr();
}
}
if (record->proxy_klass_head()->lambda_proxy_is_available()) {
ResourceMark rm;
Klass* k = record->proxy_klass_head();
while (k != nullptr) {
_st->print_cr("%4d: %s %s", _index++, k->external_name(),
- class_loader_name_for_shared(k));
+ SystemDictionaryShared::class_loader_name_for_shared(k));
k = k->next_link();
}
}
}
};
+ class SharedMethodInfoDictionaryPrinter : StackObj {
+ outputStream* _st;
+ int _index;
+
+ private:
+ static const char* tag(void* p) {
+ if (p == nullptr) {
+ return " ";
+ } else if (MetaspaceShared::is_shared_dynamic(p)) {
+ return "<D>";
+ } else if (MetaspaceShared::is_in_shared_metaspace(p)) {
+ return "<S>";
+ } else {
+ return "???";
+ }
+ }
+ public:
+ SharedMethodInfoDictionaryPrinter(outputStream* st) : _st(st), _index(0) {}
+
+ void do_value(const RunTimeMethodDataInfo* record) {
+ ResourceMark rm;
+ Method* m = record->method();
+ MethodCounters* mc = record->method_counters();
+ MethodData* md = record->method_data();
+
+ _st->print_cr("%4d: %s" PTR_FORMAT " %s" PTR_FORMAT " %s" PTR_FORMAT " %s", _index++,
+ tag(m), p2i(m),
+ tag(mc), p2i(mc),
+ tag(md), p2i(md),
+ m->external_name());
+ if (Verbose) {
+ if (mc != nullptr) {
+ mc->print_on(_st);
+ }
+ if (md != nullptr) {
+ md->print_on(_st);
+ }
+ _st->cr();
+ }
+ }
+ };
+
void SystemDictionaryShared::ArchiveInfo::print_on(const char* prefix,
outputStream* st) {
st->print_cr("%sShared Dictionary", prefix);
SharedDictionaryPrinter p(st);
st->print_cr("%sShared Builtin Dictionary", prefix);
if (!_lambda_proxy_class_dictionary.empty()) {
st->print_cr("%sShared Lambda Dictionary", prefix);
SharedLambdaDictionaryPrinter ldp(st, p.index());
_lambda_proxy_class_dictionary.iterate(&ldp);
}
+ if (!_method_info_dictionary.empty()) {
+ st->print_cr("%sShared MethodData Dictionary", prefix);
+ SharedMethodInfoDictionaryPrinter mdp(st);
+ _method_info_dictionary.iterate(&mdp);
+ }
}
void SystemDictionaryShared::ArchiveInfo::print_table_statistics(const char* prefix,
outputStream* st) {
st->print_cr("%sArchve Statistics", prefix);
_builtin_dictionary.print_table_statistics(st, "Builtin Shared Dictionary");
_unregistered_dictionary.print_table_statistics(st, "Unregistered Shared Dictionary");
_lambda_proxy_class_dictionary.print_table_statistics(st, "Lambda Shared Dictionary");
+ _method_info_dictionary.print_table_statistics(st, "MethodData Dictionary");
}
void SystemDictionaryShared::print_shared_archive(outputStream* st, bool is_static) {
if (CDSConfig::is_using_archive()) {
if (is_static) {
void SystemDictionaryShared::cleanup_lambda_proxy_class_dictionary() {
assert_lock_strong(DumpTimeTable_lock);
CleanupDumpTimeLambdaProxyClassTable cleanup_proxy_classes;
_dumptime_lambda_proxy_class_dictionary->unlink(&cleanup_proxy_classes);
}
+
+ class CleanupDumpTimeMethodInfoTable : StackObj {
+ public:
+ bool do_entry(MethodDataKey& key, DumpTimeMethodDataInfo& info) {
+ assert_lock_strong(DumpTimeTable_lock);
+ assert(MetaspaceShared::is_in_shared_metaspace(key.method()), "");
+ InstanceKlass* holder = key.method()->method_holder();
+ bool is_excluded = SystemDictionaryShared::check_for_exclusion(holder, nullptr);
+ return is_excluded;
+ }
+ };
+
+ void SystemDictionaryShared::cleanup_method_info_dictionary() {
+ assert_lock_strong(DumpTimeTable_lock);
+
+ CleanupDumpTimeMethodInfoTable cleanup_method_info;
+ _dumptime_method_info_dictionary->unlink(&cleanup_method_info);
+ }
+
+ void SystemDictionaryShared::create_loader_positive_lookup_cache(TRAPS) {
+ GrowableArray<InstanceKlass*> shared_classes_list;
+ {
+ // With static dumping, we have only a single Java thread (see JVM_StartThread) so
+ // no no other threads should be loading classes. Otherwise, the code below may miss some
+ // classes that are loaded concurrently.
+ assert(CDSConfig::is_dumping_static_archive(), "no other threads should be loading classes");
+
+ MutexLocker ml(DumpTimeTable_lock, Mutex::_no_safepoint_check_flag);
+ _dumptime_table->iterate_all_classes_in_builtin_loaders([&](InstanceKlass* k, DumpTimeClassInfo& info) {
+ if (!k->is_hidden() && !check_for_exclusion(k, &info)) {
+ shared_classes_list.append(k);
+ }
+ }
+ );
+ }
+
+ InstanceKlass* ik = vmClasses::Class_klass();
+ objArrayOop r = oopFactory::new_objArray(ik, shared_classes_list.length(), CHECK);
+ objArrayHandle array_h(THREAD, r);
+
+ for (int i = 0; i < shared_classes_list.length(); i++) {
+ oop mirror = shared_classes_list.at(i)->java_mirror();
+ Handle mirror_h(THREAD, mirror);
+ array_h->obj_at_put(i, mirror_h());
+ }
+
+ TempNewSymbol method = SymbolTable::new_symbol("generatePositiveLookupCache");
+ TempNewSymbol signature = SymbolTable::new_symbol("([Ljava/lang/Class;)V");
+
+ JavaCallArguments args(Handle(THREAD, SystemDictionary::java_system_loader()));
+ args.push_oop(array_h);
+ JavaValue result(T_VOID);
+ JavaCalls::call_virtual(&result,
+ vmClasses::jdk_internal_loader_ClassLoaders_AppClassLoader_klass(),
+ method,
+ signature,
+ &args,
+ CHECK);
+
+ if (HAS_PENDING_EXCEPTION) {
+ Handle exc_handle(THREAD, PENDING_EXCEPTION);
+ CLEAR_PENDING_EXCEPTION;
+ ResourceMark rm(THREAD);
+
+ log_warning(cds)("Exception during AppClassLoader::generatePositiveLookupCache() call");
+ LogStreamHandle(Debug, cds) log;
+ if (log.is_enabled()) {
+ java_lang_Throwable::print_stack_trace(exc_handle, &log);
+ }
+ return;
+ }
+ }
< prev index next >