diff --git a/src/hotspot/share/cds/aotClassInitializer.cpp b/src/hotspot/share/cds/aotClassInitializer.cpp index 0c8ea97fba00b..4a98ccbf990db 100644 --- a/src/hotspot/share/cds/aotClassInitializer.cpp +++ b/src/hotspot/share/cds/aotClassInitializer.cpp @@ -23,6 +23,7 @@ */ #include "cds/aotClassInitializer.hpp" +#include "cds/aotLinkedClassBulkLoader.hpp" #include "cds/archiveBuilder.hpp" #include "cds/cdsConfig.hpp" #include "cds/heapShared.hpp" diff --git a/src/hotspot/share/cds/aotClassLinker.cpp b/src/hotspot/share/cds/aotClassLinker.cpp index 47c7f6e3bf888..263c17f9c989a 100644 --- a/src/hotspot/share/cds/aotClassLinker.cpp +++ b/src/hotspot/share/cds/aotClassLinker.cpp @@ -192,7 +192,7 @@ void AOTClassLinker::write_to_archive() { if (CDSConfig::is_dumping_aot_linked_classes()) { AOTLinkedClassTable* table = AOTLinkedClassTable::get(CDSConfig::is_dumping_static_archive()); - table->set_boot(write_classes(nullptr, true)); + table->set_boot1(write_classes(nullptr, true)); table->set_boot2(write_classes(nullptr, false)); table->set_platform(write_classes(SystemDictionary::java_platform_loader(), false)); table->set_app(write_classes(SystemDictionary::java_system_loader(), false)); diff --git a/src/hotspot/share/cds/aotLinkedClassBulkLoader.cpp b/src/hotspot/share/cds/aotLinkedClassBulkLoader.cpp index 3923697578285..b82d78c176f91 100644 --- a/src/hotspot/share/cds/aotLinkedClassBulkLoader.cpp +++ b/src/hotspot/share/cds/aotLinkedClassBulkLoader.cpp @@ -27,8 +27,11 @@ #include "cds/aotLinkedClassBulkLoader.hpp" #include "cds/aotLinkedClassTable.hpp" #include "cds/cdsConfig.hpp" +#include "cds/dynamicArchive.hpp" #include "cds/heapShared.hpp" #include "classfile/classLoaderData.hpp" +#include "classfile/classLoaderDataShared.hpp" +#include "classfile/javaClasses.hpp" #include "classfile/systemDictionary.hpp" #include "classfile/systemDictionaryShared.hpp" #include "classfile/vmClasses.hpp" @@ -50,7 +53,7 @@ void AOTLinkedClassBulkLoader::serialize(SerializeClosure* soc, bool is_static_a AOTLinkedClassTable::get(is_static_archive)->serialize(soc); } -bool AOTLinkedClassBulkLoader::class_preloading_finished() { +bool AOTLinkedClassBulkLoader::has_finished_loading_classes() { if (!CDSConfig::is_using_aot_linked_classes()) { return true; } else { @@ -61,6 +64,123 @@ bool AOTLinkedClassBulkLoader::class_preloading_finished() { } } +void AOTLinkedClassBulkLoader::preload_classes(TRAPS) { + log_info(aot, load)("Start preloading classes"); + + ClassLoaderDataShared::restore_archived_modules_for_preloading_classes(THREAD); + Handle h_platform_loader(THREAD, SystemDictionary::java_platform_loader()); + Handle h_system_loader(THREAD, SystemDictionary::java_system_loader()); + + preload_classes_in_table(AOTLinkedClassTable::for_static_archive()->boot1(), "boot1", Handle(), THREAD); + preload_classes_in_table(AOTLinkedClassTable::for_static_archive()->boot2(), "boot2", Handle(), THREAD); + preload_classes_in_table(AOTLinkedClassTable::for_static_archive()->platform(), "plat", h_platform_loader, THREAD); + preload_classes_in_table(AOTLinkedClassTable::for_static_archive()->app(), "app", h_system_loader, THREAD); + + log_info(aot, load)("Finished preloading classes"); +} + +void AOTLinkedClassBulkLoader::preload_classes_in_table(Array* classes, + const char* category_name, Handle loader, TRAPS) { + if (classes == nullptr) { + return; + } + + ClassLoaderData* loader_data = ClassLoaderData::class_loader_data(loader()); + for (int i = 0; i < classes->length(); i++) { + InstanceKlass* ik = classes->at(i); + if (log_is_enabled(Info, aot, load)) { + ResourceMark rm(THREAD); + log_info(aot, load)("Preload %-5s %s%s", category_name, ik->external_name(), + ik->is_hidden() ? " (hidden)" : ""); + } + + DEBUG_ONLY({ + // The list should be sorted such that ik is placed after all of its supertypes. + precond(!ik->is_loaded()); + if (ik->java_super() != nullptr) { + precond(ik->java_super()->is_loaded()); + } + for (int i = 0; i < ik->local_interfaces()->length(); i++) { + precond(ik->local_interfaces()->at(i)->is_loaded()); + } + }); + + SystemDictionary::preload_class(loader_data, ik, CHECK); + + if (ik->is_hidden()) { + DEBUG_ONLY({ + // Make sure we don't make this hidden class available by name, even if we don't + // use any special ClassLoaderData. + ResourceMark rm(THREAD); + assert(SystemDictionary::find_instance_klass(THREAD, ik->name(), loader) == nullptr, + "hidden classes cannot be accessible by name: %s", ik->external_name()); + }); + } else { + precond(SystemDictionary::find_instance_klass(THREAD, ik->name(), loader) == ik); + } + } +} + +#ifdef ASSERT +void AOTLinkedClassBulkLoader::validate_module_of_preloaded_classes() { + oop javabase_module_oop = ModuleEntryTable::javabase_moduleEntry()->module_oop(); + for (int i = T_BOOLEAN; i < T_LONG+1; i++) { + TypeArrayKlass* tak = Universe::typeArrayKlass((BasicType)i); + validate_module(tak, "boot1", javabase_module_oop); + } + + JavaThread* current = JavaThread::current(); + Handle h_platform_loader(current, SystemDictionary::java_platform_loader()); + Handle h_system_loader(current, SystemDictionary::java_system_loader()); + + validate_module_of_preloaded_classes_in_table(AOTLinkedClassTable::for_static_archive()->boot1(), "boot1", Handle()); + validate_module_of_preloaded_classes_in_table(AOTLinkedClassTable::for_static_archive()->boot2(), "boot2", Handle()); + validate_module_of_preloaded_classes_in_table(AOTLinkedClassTable::for_static_archive()->platform(), "plat", h_platform_loader); + validate_module_of_preloaded_classes_in_table(AOTLinkedClassTable::for_static_archive()->app(), "app", h_system_loader); +} + +void AOTLinkedClassBulkLoader::validate_module_of_preloaded_classes_in_table(Array* classes, + const char* category_name, Handle loader) { + if (classes == nullptr) { + return; + } + + ClassLoaderData* loader_data = ClassLoaderData::class_loader_data(loader()); + for (int i = 0; i < classes->length(); i++) { + InstanceKlass* ik = classes->at(i); + PackageEntry* pkg_entry = ik->package(); + oop module_oop; + if (pkg_entry == nullptr) { + module_oop = loader_data->unnamed_module()->module_oop(); + } else { + module_oop = pkg_entry->module()->module_oop(); + } + + validate_module(ik, category_name, module_oop); + } +} + +void AOTLinkedClassBulkLoader::validate_module(Klass* k, const char* category_name, oop module_oop) { + assert(module_oop != nullptr, "module system must have been initialized"); + + if (log_is_enabled(Debug, aot, module)) { + ResourceMark rm; + log_debug(aot, module)("Validate module of %-5s %s", category_name, k->external_name()); + } + precond(java_lang_Class::module(k->java_mirror()) == module_oop); + + ArrayKlass* ak = k->array_klass_or_null(); + while (ak != nullptr) { + if (log_is_enabled(Debug, aot, module)) { + ResourceMark rm; + log_debug(aot, module)("Validate module of %-5s %s", category_name, ak->external_name()); + } + precond(java_lang_Class::module(ak->java_mirror()) == module_oop); + ak = ak->array_klass_or_null(); + } +} +#endif + void AOTLinkedClassBulkLoader::load_javabase_classes(JavaThread* current) { assert(CDSConfig::is_using_aot_linked_classes(), "sanity"); load_classes_in_loader(current, AOTLinkedClassCategory::BOOT1, nullptr); // only java.base classes @@ -69,6 +189,8 @@ void AOTLinkedClassBulkLoader::load_javabase_classes(JavaThread* current) { void AOTLinkedClassBulkLoader::load_non_javabase_classes(JavaThread* current) { assert(CDSConfig::is_using_aot_linked_classes(), "sanity"); + DEBUG_ONLY(validate_module_of_preloaded_classes()); + // is_using_aot_linked_classes() requires is_using_full_module_graph(). As a result, // the platform/system class loader should already have been initialized as part // of the FMG support. @@ -84,6 +206,13 @@ void AOTLinkedClassBulkLoader::load_non_javabase_classes(JavaThread* current) { load_classes_in_loader(current, AOTLinkedClassCategory::APP, SystemDictionary::java_system_loader()); + if (CDSConfig::is_using_preloaded_classes()) { + DynamicArchive::setup_and_restore_array_klasses(current); + if (current->has_pending_exception()) { + exit_on_exception(current); + } + } + if (AOTPrintTrainingInfo) { tty->print_cr("==================== archived_training_data ** after all classes preloaded ===================="); TrainingData::print_archived_training_data_on(tty); @@ -159,26 +288,26 @@ void AOTLinkedClassBulkLoader::load_table(AOTLinkedClassTable* table, AOTLinkedC const char* category_name = AOTClassLinker::class_category_name(class_category); switch (class_category) { case AOTLinkedClassCategory::BOOT1: - load_classes_impl(class_category, table->boot(), category_name, loader, CHECK); + load_classes_impl(table->boot1(), category_name, loader, CHECK); break; case AOTLinkedClassCategory::BOOT2: - load_classes_impl(class_category, table->boot2(), category_name, loader, CHECK); + load_classes_impl(table->boot2(), category_name, loader, CHECK); break; case AOTLinkedClassCategory::PLATFORM: { - initiate_loading(THREAD, category_name, loader, table->boot()); + initiate_loading(THREAD, category_name, loader, table->boot1()); initiate_loading(THREAD, category_name, loader, table->boot2()); - load_classes_impl(class_category, table->platform(), category_name, loader, CHECK); + load_classes_impl(table->platform(), category_name, loader, CHECK); } break; case AOTLinkedClassCategory::APP: { - initiate_loading(THREAD, category_name, loader, table->boot()); + initiate_loading(THREAD, category_name, loader, table->boot1()); initiate_loading(THREAD, category_name, loader, table->boot2()); initiate_loading(THREAD, category_name, loader, table->platform()); - load_classes_impl(class_category, table->app(), category_name, loader, CHECK); + load_classes_impl(table->app(), category_name, loader, CHECK); } break; case AOTLinkedClassCategory::UNREGISTERED: @@ -188,7 +317,7 @@ void AOTLinkedClassBulkLoader::load_table(AOTLinkedClassTable* table, AOTLinkedC } } -void AOTLinkedClassBulkLoader::load_classes_impl(AOTLinkedClassCategory class_category, Array* classes, +void AOTLinkedClassBulkLoader::load_classes_impl(Array* classes, const char* category_name, Handle loader, TRAPS) { if (classes == nullptr) { return; @@ -333,7 +462,7 @@ void AOTLinkedClassBulkLoader::load_hidden_class(ClassLoaderData* loader_data, I } void AOTLinkedClassBulkLoader::finish_loading_javabase_classes(TRAPS) { - init_required_classes_for_loader(Handle(), AOTLinkedClassTable::for_static_archive()->boot(), CHECK); + init_required_classes_for_loader(Handle(), AOTLinkedClassTable::for_static_archive()->boot1(), CHECK); } // Some AOT-linked classes for must be initialized early. This includes @@ -429,9 +558,9 @@ void AOTLinkedClassBulkLoader::replay_training_at_init_for_preloaded_classes(TRA if (CDSConfig::is_using_aot_linked_classes() && TrainingData::have_data()) { // Only static archive can have training data. AOTLinkedClassTable* table = AOTLinkedClassTable::for_static_archive(); - replay_training_at_init(table->boot(), CHECK); + replay_training_at_init(table->boot1(), CHECK); replay_training_at_init(table->boot2(), CHECK); replay_training_at_init(table->platform(), CHECK); replay_training_at_init(table->app(), CHECK); } -} \ No newline at end of file +} diff --git a/src/hotspot/share/cds/aotLinkedClassBulkLoader.hpp b/src/hotspot/share/cds/aotLinkedClassBulkLoader.hpp index 86fb5017eb858..cf94ef4c8d4ab 100644 --- a/src/hotspot/share/cds/aotLinkedClassBulkLoader.hpp +++ b/src/hotspot/share/cds/aotLinkedClassBulkLoader.hpp @@ -47,25 +47,37 @@ class AOTLinkedClassBulkLoader : AllStatic { static bool _platform_completed; static bool _app_completed; static bool _all_completed; + static bool _preloading_non_javavase_classes; + + static void preload_classes_in_table(Array* classes, + const char* category_name, Handle loader, TRAPS); static void load_classes_in_loader(JavaThread* current, AOTLinkedClassCategory class_category, oop class_loader_oop); static void load_classes_in_loader_impl(AOTLinkedClassCategory class_category, oop class_loader_oop, TRAPS); static void load_table(AOTLinkedClassTable* table, AOTLinkedClassCategory class_category, Handle loader, TRAPS); static void initiate_loading(JavaThread* current, const char* category, Handle initiating_loader, Array* classes); - static void load_classes_impl(AOTLinkedClassCategory class_category, Array* classes, + static void load_classes_impl(Array* classes, const char* category_name, Handle loader, TRAPS); static void load_hidden_class(ClassLoaderData* loader_data, InstanceKlass* ik, TRAPS); static void init_required_classes_for_loader(Handle class_loader, Array* classes, TRAPS); static void replay_training_at_init(Array* classes, TRAPS) NOT_CDS_RETURN; + +#ifdef ASSERT + static void validate_module_of_preloaded_classes(); + static void validate_module_of_preloaded_classes_in_table(Array* classes, + const char* category_name, Handle loader); + static void validate_module(Klass* k, const char* category_name, oop module_oop); +#endif + public: static void serialize(SerializeClosure* soc, bool is_static_archive) NOT_CDS_RETURN; - + static void preload_classes(TRAPS); static void load_javabase_classes(JavaThread* current) NOT_CDS_RETURN; static void load_non_javabase_classes(JavaThread* current) NOT_CDS_RETURN; static void finish_loading_javabase_classes(TRAPS) NOT_CDS_RETURN; static void exit_on_exception(JavaThread* current); static void replay_training_at_init_for_preloaded_classes(TRAPS) NOT_CDS_RETURN; - static bool class_preloading_finished(); + static bool has_finished_loading_classes() NOT_CDS_RETURN_(true); static bool is_pending_aot_linked_class(Klass* k) NOT_CDS_RETURN_(false); }; diff --git a/src/hotspot/share/cds/aotLinkedClassTable.cpp b/src/hotspot/share/cds/aotLinkedClassTable.cpp index b602c599f542f..d00ec158ebc89 100644 --- a/src/hotspot/share/cds/aotLinkedClassTable.cpp +++ b/src/hotspot/share/cds/aotLinkedClassTable.cpp @@ -31,7 +31,7 @@ AOTLinkedClassTable AOTLinkedClassTable::_for_static_archive; AOTLinkedClassTable AOTLinkedClassTable::_for_dynamic_archive; void AOTLinkedClassTable::serialize(SerializeClosure* soc) { - soc->do_ptr((void**)&_boot); + soc->do_ptr((void**)&_boot1); soc->do_ptr((void**)&_boot2); soc->do_ptr((void**)&_platform); soc->do_ptr((void**)&_app); diff --git a/src/hotspot/share/cds/aotLinkedClassTable.hpp b/src/hotspot/share/cds/aotLinkedClassTable.hpp index 2a199c15eddec..854eead2aa3e7 100644 --- a/src/hotspot/share/cds/aotLinkedClassTable.hpp +++ b/src/hotspot/share/cds/aotLinkedClassTable.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2025, 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 @@ -44,14 +44,14 @@ class AOTLinkedClassTable { static AOTLinkedClassTable _for_static_archive; static AOTLinkedClassTable _for_dynamic_archive; - Array* _boot; // only java.base classes - Array* _boot2; // boot classes in other modules + Array* _boot1; // boot classes in java.base module + Array* _boot2; // boot classes in all other (named and unnamed) modules Array* _platform; Array* _app; public: AOTLinkedClassTable() : - _boot(nullptr), _boot2(nullptr), + _boot1(nullptr), _boot2(nullptr), _platform(nullptr), _app(nullptr) {} static AOTLinkedClassTable* for_static_archive() { return &_for_static_archive; } @@ -61,12 +61,12 @@ class AOTLinkedClassTable { return is_static_archive ? for_static_archive() : for_dynamic_archive(); } - Array* boot() const { return _boot; } + Array* boot1() const { return _boot1; } Array* boot2() const { return _boot2; } Array* platform() const { return _platform; } Array* app() const { return _app; } - void set_boot (Array* value) { _boot = value; } + void set_boot1 (Array* value) { _boot1 = value; } void set_boot2 (Array* value) { _boot2 = value; } void set_platform(Array* value) { _platform = value; } void set_app (Array* value) { _app = value; } diff --git a/src/hotspot/share/cds/cdsConfig.cpp b/src/hotspot/share/cds/cdsConfig.cpp index 85c40df2606a1..b5c7fa09c4c0e 100644 --- a/src/hotspot/share/cds/cdsConfig.cpp +++ b/src/hotspot/share/cds/cdsConfig.cpp @@ -50,6 +50,7 @@ bool CDSConfig::_is_using_optimized_module_handling = true; bool CDSConfig::_is_dumping_full_module_graph = true; bool CDSConfig::_is_using_full_module_graph = true; bool CDSConfig::_has_aot_linked_classes = false; +bool CDSConfig::_has_preloaded_classes = false; bool CDSConfig::_is_single_command_training = false; bool CDSConfig::_has_temp_aot_config_file = false; bool CDSConfig::_old_cds_flags_used = false; @@ -65,7 +66,8 @@ JavaThread* CDSConfig::_dumper_thread = nullptr; int CDSConfig::get_status() { assert(Universe::is_fully_initialized(), "status is finalized only after Universe is initialized"); - return (is_dumping_archive() ? IS_DUMPING_ARCHIVE : 0) | + return (is_dumping_aot_linked_classes() ? IS_DUMPING_AOT_LINKED_CLASSES : 0) | + (is_dumping_archive() ? IS_DUMPING_ARCHIVE : 0) | (is_dumping_method_handles() ? IS_DUMPING_METHOD_HANDLES : 0) | (is_dumping_static_archive() ? IS_DUMPING_STATIC_ARCHIVE : 0) | (is_logging_lambda_form_invokers() ? IS_LOGGING_LAMBDA_FORM_INVOKERS : 0) | @@ -989,6 +991,14 @@ void CDSConfig::set_has_aot_linked_classes(bool has_aot_linked_classes) { _has_aot_linked_classes |= has_aot_linked_classes; } +bool CDSConfig::is_using_preloaded_classes() { + return is_using_full_module_graph() && _has_preloaded_classes; +} + +void CDSConfig::set_has_preloaded_classes(bool has_preloaded_classes) { + _has_preloaded_classes |= has_preloaded_classes; +} + bool CDSConfig::is_initing_classes_at_dump_time() { return is_dumping_heap() && is_dumping_aot_linked_classes(); } diff --git a/src/hotspot/share/cds/cdsConfig.hpp b/src/hotspot/share/cds/cdsConfig.hpp index 1fd229ff34fff..c9cafa1893794 100644 --- a/src/hotspot/share/cds/cdsConfig.hpp +++ b/src/hotspot/share/cds/cdsConfig.hpp @@ -41,6 +41,7 @@ class CDSConfig : public AllStatic { static bool _is_dumping_full_module_graph; static bool _is_using_full_module_graph; static bool _has_aot_linked_classes; + static bool _has_preloaded_classes; static bool _is_single_command_training; static bool _has_temp_aot_config_file; @@ -78,11 +79,12 @@ class CDSConfig : public AllStatic { public: // Used by jdk.internal.misc.CDS.getCDSConfigStatus(); - static const int IS_DUMPING_ARCHIVE = 1 << 0; - static const int IS_DUMPING_METHOD_HANDLES = 1 << 1; - static const int IS_DUMPING_STATIC_ARCHIVE = 1 << 2; - static const int IS_LOGGING_LAMBDA_FORM_INVOKERS = 1 << 3; - static const int IS_USING_ARCHIVE = 1 << 4; + static const int IS_DUMPING_AOT_LINKED_CLASSES = 1 << 0; + static const int IS_DUMPING_ARCHIVE = 1 << 1; + static const int IS_DUMPING_METHOD_HANDLES = 1 << 2; + static const int IS_DUMPING_STATIC_ARCHIVE = 1 << 3; + static const int IS_LOGGING_LAMBDA_FORM_INVOKERS = 1 << 4; + static const int IS_USING_ARCHIVE = 1 << 5; static int get_status() NOT_CDS_RETURN_(0); @@ -161,6 +163,9 @@ class CDSConfig : public AllStatic { static bool is_using_aot_linked_classes() NOT_CDS_JAVA_HEAP_RETURN_(false); static void set_has_aot_linked_classes(bool has_aot_linked_classes) NOT_CDS_JAVA_HEAP_RETURN; + static bool is_using_preloaded_classes() NOT_CDS_JAVA_HEAP_RETURN_(false); + static void set_has_preloaded_classes(bool has_preloaded_classes) NOT_CDS_JAVA_HEAP_RETURN; + // archive_path // Points to the classes.jsa in $JAVA_HOME (could be input or output) diff --git a/src/hotspot/share/cds/cdsHeapVerifier.cpp b/src/hotspot/share/cds/cdsHeapVerifier.cpp index 0da9e3f2c8da6..6b2649484f6b7 100644 --- a/src/hotspot/share/cds/cdsHeapVerifier.cpp +++ b/src/hotspot/share/cds/cdsHeapVerifier.cpp @@ -99,7 +99,7 @@ CDSHeapVerifier::CDSHeapVerifier() : _archived_objs(0), _problems(0) // you might need to fix the core library code, or fix the ADD_EXCL entries below. // // class field type - ADD_EXCL("java/lang/ClassLoader", "scl"); // A + ADD_EXCL("java/lang/ClassLoader$Holder", "scl"); // A ADD_EXCL("java/lang/Module", "ALL_UNNAMED_MODULE", // A "ALL_UNNAMED_MODULE_SET", // A "EVERYONE_MODULE", // A @@ -146,6 +146,10 @@ CDSHeapVerifier::CDSHeapVerifier() : _archived_objs(0), _problems(0) "ZERO"); // D } + if (CDSConfig::is_dumping_aot_linked_classes()) { + ADD_EXCL("java/lang/Package$VersionInfo", "NULL_VERSION_INFO"); // D + } + # undef ADD_EXCL ClassLoaderDataGraph::classes_do(this); @@ -227,10 +231,16 @@ class CDSHeapVerifier::CheckStaticFields : public FieldClosure { return; } - if (field_ik == vmClasses::internal_Unsafe_klass() && ArchiveUtils::has_aot_initialized_mirror(field_ik)) { - // There's only a single instance of jdk/internal/misc/Unsafe, so all references will - // be pointing to this singleton, which has been archived. - return; + if (ArchiveUtils::has_aot_initialized_mirror(field_ik)) { + if (field_ik == vmClasses::internal_Unsafe_klass()) { + // There's only a single instance of jdk/internal/misc/Unsafe, so all references will + // be pointing to this singleton, which has been archived. + return; + } + if (field_ik == vmClasses::Boolean_klass()) { + // TODO: check if is TRUE or FALSE + return; + } } } diff --git a/src/hotspot/share/cds/dynamicArchive.cpp b/src/hotspot/share/cds/dynamicArchive.cpp index 36afbc65ce34b..0ca048a590f96 100644 --- a/src/hotspot/share/cds/dynamicArchive.cpp +++ b/src/hotspot/share/cds/dynamicArchive.cpp @@ -432,8 +432,6 @@ void DynamicArchive::setup_array_klasses() { if (_dynamic_archive_array_klasses != nullptr) { for (int i = 0; i < _dynamic_archive_array_klasses->length(); i++) { ObjArrayKlass* oak = _dynamic_archive_array_klasses->at(i); - assert(!oak->is_typeArray_klass(), "all type array classes must be in static archive"); - Klass* elm = oak->element_klass(); assert(MetaspaceShared::is_shared_static((void*)elm), "must be"); @@ -450,6 +448,21 @@ void DynamicArchive::setup_array_klasses() { } } +void DynamicArchive::setup_and_restore_array_klasses(TRAPS) { + precond(CDSConfig::is_using_preloaded_classes()); + + if (_dynamic_archive_array_klasses != nullptr) { + setup_array_klasses(); + for (int i = 0; i < _dynamic_archive_array_klasses->length(); i++) { + ObjArrayKlass* oak = _dynamic_archive_array_klasses->at(i); + Klass* elm = oak->element_klass(); + assert(MetaspaceShared::is_shared_static((void*)elm), "must be"); + RecursiveLocker rl(MultiArray_lock, THREAD); + oak->restore_unshareable_info(elm->class_loader_data(), Handle(), CHECK); + } + } +} + void DynamicArchive::serialize_array_klasses(SerializeClosure* soc) { soc->do_ptr(&_dynamic_archive_array_klasses); } diff --git a/src/hotspot/share/cds/dynamicArchive.hpp b/src/hotspot/share/cds/dynamicArchive.hpp index 8c23750734c35..3b9b3dcf968ab 100644 --- a/src/hotspot/share/cds/dynamicArchive.hpp +++ b/src/hotspot/share/cds/dynamicArchive.hpp @@ -70,6 +70,7 @@ class DynamicArchive : AllStatic { static bool validate(FileMapInfo* dynamic_info); static void dump_array_klasses(); static void setup_array_klasses(); + static void setup_and_restore_array_klasses(TRAPS); static void append_array_klass(ObjArrayKlass* oak); static void serialize_array_klasses(SerializeClosure* soc); static void make_array_klasses_shareable(); diff --git a/src/hotspot/share/cds/filemap.cpp b/src/hotspot/share/cds/filemap.cpp index 82a4ae7d9bad6..4d54a310b96de 100644 --- a/src/hotspot/share/cds/filemap.cpp +++ b/src/hotspot/share/cds/filemap.cpp @@ -1858,6 +1858,11 @@ bool FileMapInfo::validate_aot_class_linking() { if (header()->has_aot_linked_classes()) { const char* archive_type = CDSConfig::type_of_archive_being_loaded(); CDSConfig::set_has_aot_linked_classes(true); + if (is_static()) { + // The aot-linked classes in the dynamic archive don't have archived mirror/package/protection domain, so they + // cannot be preloaded. + CDSConfig::set_has_preloaded_classes(true); + } if (JvmtiExport::should_post_class_file_load_hook()) { aot_log_error(aot)("%s has aot-linked classes. It cannot be used when JVMTI ClassFileLoadHook is in use.", archive_type); diff --git a/src/hotspot/share/cds/heapShared.cpp b/src/hotspot/share/cds/heapShared.cpp index 51572bcace71b..3643341440aa1 100644 --- a/src/hotspot/share/cds/heapShared.cpp +++ b/src/hotspot/share/cds/heapShared.cpp @@ -585,7 +585,7 @@ void HeapShared::copy_and_rescan_aot_inited_mirror(InstanceKlass* ik) { } } -static void copy_java_mirror_hashcode(oop orig_mirror, oop scratch_m) { +void HeapShared::copy_java_mirror(oop orig_mirror, oop scratch_m) { // We need to retain the identity_hash, because it may have been used by some hashtables // in the shared heap. if (!orig_mirror->fast_no_hash_check()) { @@ -601,6 +601,11 @@ static void copy_java_mirror_hashcode(oop orig_mirror, oop scratch_m) { DEBUG_ONLY(intptr_t archived_hash = scratch_m->identity_hash()); assert(src_hash == archived_hash, "Different hash codes: original " INTPTR_FORMAT ", archived " INTPTR_FORMAT, src_hash, archived_hash); } + + if (CDSConfig::is_dumping_aot_linked_classes()) { + java_lang_Class::set_module(scratch_m, java_lang_Class::module(orig_mirror)); + java_lang_Class::set_protection_domain(scratch_m, java_lang_Class::protection_domain(orig_mirror)); + } } static objArrayOop get_archived_resolved_references(InstanceKlass* src_ik) { @@ -698,7 +703,7 @@ void HeapShared::write_heap(ArchiveHeapInfo *heap_info) { void HeapShared::scan_java_mirror(oop orig_mirror) { oop m = scratch_java_mirror(orig_mirror); if (m != nullptr) { // nullptr if for custom class loader - copy_java_mirror_hashcode(orig_mirror, m); + copy_java_mirror(orig_mirror, m); bool success = archive_reachable_objects_from(1, _dump_time_special_subgraph, m); assert(success, "sanity"); } diff --git a/src/hotspot/share/cds/heapShared.hpp b/src/hotspot/share/cds/heapShared.hpp index f4e86aa5895a3..12906b6949cec 100644 --- a/src/hotspot/share/cds/heapShared.hpp +++ b/src/hotspot/share/cds/heapShared.hpp @@ -339,6 +339,7 @@ class HeapShared: AllStatic { static void prepare_resolved_references(); static void archive_strings(); static void archive_subgraphs(); + static void copy_java_mirror(oop orig_mirror, oop scratch_m); // PendingOop and PendingOopStack are used for recursively discovering all cacheable // heap objects. The recursion is done using PendingOopStack so we won't overflow the diff --git a/src/hotspot/share/cds/metaspaceShared.cpp b/src/hotspot/share/cds/metaspaceShared.cpp index 8db2195c4348e..8e134d630c862 100644 --- a/src/hotspot/share/cds/metaspaceShared.cpp +++ b/src/hotspot/share/cds/metaspaceShared.cpp @@ -1979,7 +1979,9 @@ void MetaspaceShared::initialize_shared_spaces() { intptr_t* buffer = (intptr_t*)dynamic_mapinfo->serialized_data(); ReadClosure rc(&buffer, (intptr_t)SharedBaseAddress); ArchiveBuilder::serialize_dynamic_archivable_items(&rc); - DynamicArchive::setup_array_klasses(); + if (!CDSConfig::is_using_preloaded_classes()) { + DynamicArchive::setup_array_klasses(); + } dynamic_mapinfo->close(); dynamic_mapinfo->unmap_region(MetaspaceShared::bm); } diff --git a/src/hotspot/share/classfile/classLoaderDataShared.cpp b/src/hotspot/share/classfile/classLoaderDataShared.cpp index a495327864da8..27cdef5dd9b5f 100644 --- a/src/hotspot/share/classfile/classLoaderDataShared.cpp +++ b/src/hotspot/share/classfile/classLoaderDataShared.cpp @@ -280,4 +280,15 @@ void ClassLoaderDataShared::restore_java_system_loader_from_archive(ClassLoaderD _full_module_graph_loaded = true; } +void ClassLoaderDataShared::restore_archived_modules_for_preloading_classes(JavaThread* current) { + precond(CDSConfig::is_using_preloaded_classes()); + + precond(_platform_loader_root_index >= 0); + precond(_system_loader_root_index >= 0); + + Handle h_platform_loader(current, HeapShared::get_root(_platform_loader_root_index)); + Handle h_system_loader(current, HeapShared::get_root(_system_loader_root_index)); + Modules::init_archived_modules(current, h_platform_loader, h_system_loader); +} + #endif // INCLUDE_CDS_JAVA_HEAP diff --git a/src/hotspot/share/classfile/classLoaderDataShared.hpp b/src/hotspot/share/classfile/classLoaderDataShared.hpp index 6ef338f0f34ae..7bb2100a6a5ff 100644 --- a/src/hotspot/share/classfile/classLoaderDataShared.hpp +++ b/src/hotspot/share/classfile/classLoaderDataShared.hpp @@ -27,6 +27,7 @@ #include "memory/allStatic.hpp" #include "oops/oopsHierarchy.hpp" +#include "utilities/macros.hpp" class ClassLoaderData; class MetaspaceClosure; @@ -44,6 +45,7 @@ class ClassLoaderDataShared : AllStatic { static void init_archived_tables(); static void serialize(SerializeClosure* f); static void clear_archived_oops(); + static void restore_archived_modules_for_preloading_classes(JavaThread* current) NOT_CDS_JAVA_HEAP_RETURN; static void restore_archived_entries_for_null_class_loader_data(); static oop restore_archived_oops_for_null_class_loader_data(); static void restore_java_platform_loader_from_archive(ClassLoaderData* loader_data); diff --git a/src/hotspot/share/classfile/javaClasses.cpp b/src/hotspot/share/classfile/javaClasses.cpp index 2dcfc43898c4b..a21ea725dcaa8 100644 --- a/src/hotspot/share/classfile/javaClasses.cpp +++ b/src/hotspot/share/classfile/javaClasses.cpp @@ -1013,6 +1013,15 @@ void java_lang_Class::initialize_mirror_fields(Klass* k, // Set the java.lang.Module module field in the java_lang_Class mirror void java_lang_Class::set_mirror_module_field(JavaThread* current, Klass* k, Handle mirror, Handle module) { + if (CDSConfig::is_using_preloaded_classes()) { + oop archived_module = java_lang_Class:: module(mirror()); + if (archived_module != nullptr) { + precond(module() == nullptr || module() == archived_module); + precond(MetaspaceShared::is_shared_static((void*)k)); + return; + } + } + if (module.is_null()) { // During startup, the module may be null only if java.base has not been defined yet. // Put the class on the fixup_module_list to patch later when the java.lang.Module @@ -1052,9 +1061,11 @@ void java_lang_Class::set_mirror_module_field(JavaThread* current, Klass* k, Han // Statically allocate fixup lists because they always get created. void java_lang_Class::allocate_fixup_lists() { - GrowableArray* mirror_list = - new (mtClass) GrowableArray(40, mtClass); - set_fixup_mirror_list(mirror_list); + if (!CDSConfig::is_using_preloaded_classes()) { + GrowableArray* mirror_list = + new (mtClass) GrowableArray(40, mtClass); + set_fixup_mirror_list(mirror_list); + } GrowableArray* module_list = new (mtModule) GrowableArray(500, mtModule); @@ -1155,6 +1166,7 @@ void java_lang_Class::create_mirror(Klass* k, Handle class_loader, create_scratch_mirror(k, CHECK); } } else { + assert(!CDSConfig::is_using_preloaded_classes(), "should not come here"); assert(fixup_mirror_list() != nullptr, "fixup_mirror_list not initialized"); fixup_mirror_list()->push(k); } @@ -1200,7 +1212,7 @@ bool java_lang_Class::restore_archived_mirror(Klass *k, Handle protection_domain, TRAPS) { // Postpone restoring archived mirror until java.lang.Class is loaded. Please // see more details in vmClasses::resolve_all(). - if (!vmClasses::Class_klass_loaded()) { + if (!vmClasses::Class_klass_loaded() && !CDSConfig::is_using_preloaded_classes()) { assert(fixup_mirror_list() != nullptr, "fixup_mirror_list not initialized"); fixup_mirror_list()->push(k); return true; diff --git a/src/hotspot/share/classfile/javaClasses.hpp b/src/hotspot/share/classfile/javaClasses.hpp index 37ca22e92957b..c70f7e2419823 100644 --- a/src/hotspot/share/classfile/javaClasses.hpp +++ b/src/hotspot/share/classfile/javaClasses.hpp @@ -234,6 +234,7 @@ class java_lang_String : AllStatic { class java_lang_Class : AllStatic { friend class VMStructs; friend class JVMCIVMStructs; + friend class HeapShared; private: diff --git a/src/hotspot/share/classfile/modules.cpp b/src/hotspot/share/classfile/modules.cpp index c7b5a72945106..bf89bd99a0e47 100644 --- a/src/hotspot/share/classfile/modules.cpp +++ b/src/hotspot/share/classfile/modules.cpp @@ -701,6 +701,26 @@ void Modules::serialize_archived_module_info(SerializeClosure* soc) { void Modules::define_archived_modules(Handle h_platform_loader, Handle h_system_loader, TRAPS) { assert(CDSConfig::is_using_full_module_graph(), "must be"); + if (CDSConfig::is_using_preloaded_classes()) { + // Already initialized + precond(SystemDictionary::java_platform_loader() == h_platform_loader()); + precond(SystemDictionary::java_system_loader() == h_system_loader()); + } else { + if (h_platform_loader.is_null()) { + THROW_MSG(vmSymbols::java_lang_NullPointerException(), "Null platform loader object"); + } + + if (h_system_loader.is_null()) { + THROW_MSG(vmSymbols::java_lang_NullPointerException(), "Null system loader object"); + } + + init_archived_modules(THREAD, h_platform_loader, h_system_loader); + } +} + +void Modules::init_archived_modules(JavaThread* current, Handle h_platform_loader, Handle h_system_loader) { + assert(CDSConfig::is_using_full_module_graph(), "must be"); + ExceptionMark em(current); // We don't want the classes used by the archived full module graph to be redefined by JVMTI. // Luckily, such classes are loaded in the JVMTI "early" phase, and CDS is disabled if a JVMTI @@ -709,16 +729,15 @@ void Modules::define_archived_modules(Handle h_platform_loader, Handle h_system_ assert(!(JvmtiExport::should_post_class_file_load_hook() && JvmtiExport::has_early_class_hook_env()), "CDS should be disabled if early class hooks are enabled"); - Handle java_base_module(THREAD, ClassLoaderDataShared::restore_archived_oops_for_null_class_loader_data()); - // Patch any previously loaded class's module field with java.base's java.lang.Module. - ModuleEntryTable::patch_javabase_entries(THREAD, java_base_module); - - if (h_platform_loader.is_null()) { - THROW_MSG(vmSymbols::java_lang_NullPointerException(), "Null platform loader object"); + if (CDSConfig::is_using_preloaded_classes()) { + ClassLoaderData* boot_loader_data = ClassLoaderData::the_null_class_loader_data(); + ClassLoaderDataShared::archived_boot_unnamed_module()->restore_archived_oops(boot_loader_data); } - if (h_system_loader.is_null()) { - THROW_MSG(vmSymbols::java_lang_NullPointerException(), "Null system loader object"); + Handle java_base_module(current, ClassLoaderDataShared::restore_archived_oops_for_null_class_loader_data()); + if (!CDSConfig::is_using_preloaded_classes()) { + // Patch any previously loaded class's module field with java.base's java.lang.Module. + ModuleEntryTable::patch_javabase_entries(current, java_base_module); } ClassLoaderData* platform_loader_data = SystemDictionary::register_loader(h_platform_loader); @@ -778,7 +797,9 @@ void Modules::set_bootloader_unnamed_module(Handle module, TRAPS) { #if INCLUDE_CDS_JAVA_HEAP if (CDSConfig::is_using_full_module_graph()) { precond(unnamed_module == ClassLoaderDataShared::archived_boot_unnamed_module()); - unnamed_module->restore_archived_oops(boot_loader_data); + if (!CDSConfig::is_using_preloaded_classes()) { + unnamed_module->restore_archived_oops(boot_loader_data); + } } else #endif { diff --git a/src/hotspot/share/classfile/modules.hpp b/src/hotspot/share/classfile/modules.hpp index d6d81263449c9..27a22c1017a23 100644 --- a/src/hotspot/share/classfile/modules.hpp +++ b/src/hotspot/share/classfile/modules.hpp @@ -57,6 +57,8 @@ class Modules : AllStatic { static void check_archived_module_oop(oop orig_module_obj) NOT_CDS_JAVA_HEAP_RETURN; static void define_archived_modules(Handle h_platform_loader, Handle h_system_loader, TRAPS) NOT_CDS_JAVA_HEAP_RETURN; + static void init_archived_modules(JavaThread* current, Handle h_platform_loader, Handle h_system_loader) + NOT_CDS_JAVA_HEAP_RETURN; static void verify_archived_modules() NOT_CDS_JAVA_HEAP_RETURN; static void dump_archived_module_info() NOT_CDS_JAVA_HEAP_RETURN; static void serialize_archived_module_info(SerializeClosure* soc) NOT_CDS_JAVA_HEAP_RETURN; diff --git a/src/hotspot/share/classfile/systemDictionary.cpp b/src/hotspot/share/classfile/systemDictionary.cpp index c3623dc25a1af..28d2fb63de458 100644 --- a/src/hotspot/share/classfile/systemDictionary.cpp +++ b/src/hotspot/share/classfile/systemDictionary.cpp @@ -198,14 +198,19 @@ ClassLoaderData* SystemDictionary::register_loader(Handle class_loader, bool cre } void SystemDictionary::set_system_loader(ClassLoaderData *cld) { - assert(_java_system_loader.is_empty(), "already set!"); - _java_system_loader = cld->class_loader_handle(); - + if (_java_system_loader.is_empty()) { + _java_system_loader = cld->class_loader_handle(); + } else { + assert(_java_system_loader.resolve() == cld->class_loader(), "sanity"); + } } void SystemDictionary::set_platform_loader(ClassLoaderData *cld) { - assert(_java_platform_loader.is_empty(), "already set!"); - _java_platform_loader = cld->class_loader_handle(); + if (_java_platform_loader.is_empty()) { + _java_platform_loader = cld->class_loader_handle(); + } else { + assert(_java_platform_loader.resolve() == cld->class_loader(), "sanity"); + } } // ---------------------------------------------------------------------------- @@ -1151,6 +1156,31 @@ void SystemDictionary::load_shared_class_misc(InstanceKlass* ik, ClassLoaderData } } +void SystemDictionary::preload_class(ClassLoaderData* loader_data, InstanceKlass* ik, TRAPS) { + precond(CDSConfig::is_using_preloaded_classes()); + precond(MetaspaceShared::is_shared_static((void*)ik)); + precond(!ik->is_loaded()); + + oop java_mirror = ik->archived_java_mirror(); + precond(java_mirror != nullptr); + + Handle pd(THREAD, java_lang_Class::protection_domain(java_mirror)); + PackageEntry* pkg_entry = ik->package(); + assert(pkg_entry != nullptr || ClassLoader::package_from_class_name(ik->name()) == nullptr, + "non-empty packages must have been archived"); + + ik->restore_unshareable_info(loader_data, pd, pkg_entry, CHECK); + load_shared_class_misc(ik, loader_data); + ik->add_to_hierarchy(THREAD); + + if (!ik->is_hidden()) { + update_dictionary(THREAD, ik, loader_data); + } + + assert(java_lang_Class::module(java_mirror) != nullptr, "must have been archived"); + assert(ik->is_loaded(), "Must be in at least loaded state"); +} + #endif // INCLUDE_CDS InstanceKlass* SystemDictionary::load_instance_class_impl(Symbol* class_name, Handle class_loader, TRAPS) { diff --git a/src/hotspot/share/classfile/systemDictionary.hpp b/src/hotspot/share/classfile/systemDictionary.hpp index 8cf2cd83b822a..897bafde9ca61 100644 --- a/src/hotspot/share/classfile/systemDictionary.hpp +++ b/src/hotspot/share/classfile/systemDictionary.hpp @@ -326,7 +326,7 @@ class SystemDictionary : AllStatic { static void restore_archived_method_handle_intrinsics_impl(TRAPS) NOT_CDS_RETURN; protected: - // Used by SystemDictionaryShared and LambdaProxyClassDictionary + // Used by AOTLinkedClassBulkLoader, LambdaProxyClassDictionary, and SystemDictionaryShared static bool add_loader_constraint(Symbol* name, Klass* klass_being_linked, Handle loader1, Handle loader2); @@ -337,6 +337,7 @@ class SystemDictionary : AllStatic { const ClassFileStream *cfs, PackageEntry* pkg_entry, TRAPS); + static void preload_class(ClassLoaderData* loader_data, InstanceKlass* ik, TRAPS); static Handle get_loader_lock_or_null(Handle class_loader); static InstanceKlass* find_or_define_instance_class(Symbol* class_name, Handle class_loader, diff --git a/src/hotspot/share/classfile/vmClasses.cpp b/src/hotspot/share/classfile/vmClasses.cpp index 813926e51a223..3712b45c7f741 100644 --- a/src/hotspot/share/classfile/vmClasses.cpp +++ b/src/hotspot/share/classfile/vmClasses.cpp @@ -101,7 +101,11 @@ bool vmClasses::resolve(vmClassID id, TRAPS) { void vmClasses::resolve_until(vmClassID limit_id, vmClassID &start_id, TRAPS) { assert((int)start_id <= (int)limit_id, "IDs are out of order!"); for (auto id : EnumRange{start_id, limit_id}) { // (inclusive start, exclusive end) - resolve(id, CHECK); + if (CDSConfig::is_using_preloaded_classes()) { + precond(klass_at(id)->is_loaded()); + } else { + resolve(id, CHECK); + } } // move the starting value forward to the limit: @@ -115,6 +119,10 @@ void vmClasses::resolve_all(TRAPS) { // after vmSymbols::initialize() is called but before any classes are pre-loaded. ClassLoader::classLoader_init2(THREAD); + if (CDSConfig::is_using_preloaded_classes()) { + AOTLinkedClassBulkLoader::preload_classes(THREAD); + } + // Preload commonly used klasses vmClassID scan = vmClassID::FIRST; // first do Object, then String, Class diff --git a/src/hotspot/share/compiler/compilationPolicy.cpp b/src/hotspot/share/compiler/compilationPolicy.cpp index fd1357398f5de..f3bb688c8df56 100644 --- a/src/hotspot/share/compiler/compilationPolicy.cpp +++ b/src/hotspot/share/compiler/compilationPolicy.cpp @@ -847,7 +847,7 @@ nmethod* CompilationPolicy::event(const methodHandle& method, const methodHandle #if INCLUDE_JVMCI if (EnableJVMCI && UseJVMCICompiler && - comp_level == CompLevel_full_optimization CDS_ONLY(&& !AOTLinkedClassBulkLoader::class_preloading_finished())) { + comp_level == CompLevel_full_optimization CDS_ONLY(&& !AOTLinkedClassBulkLoader::has_finished_loading_classes())) { return nullptr; } #endif @@ -1440,7 +1440,7 @@ CompLevel CompilationPolicy::call_event(const methodHandle& method, CompLevel cu } #if INCLUDE_JVMCI if (EnableJVMCI && UseJVMCICompiler && - next_level == CompLevel_full_optimization CDS_ONLY(&& !AOTLinkedClassBulkLoader::class_preloading_finished())) { + next_level == CompLevel_full_optimization CDS_ONLY(&& !AOTLinkedClassBulkLoader::has_finished_loading_classes())) { next_level = cur_level; } #endif diff --git a/src/hotspot/share/memory/iterator.inline.hpp b/src/hotspot/share/memory/iterator.inline.hpp index 7ed2b9b3faae1..6fc7053d87db8 100644 --- a/src/hotspot/share/memory/iterator.inline.hpp +++ b/src/hotspot/share/memory/iterator.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2025, 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 @@ -27,7 +27,6 @@ #include "memory/iterator.hpp" -#include "cds/aotLinkedClassBulkLoader.hpp" #include "classfile/classLoaderData.hpp" #include "code/nmethod.hpp" #include "oops/access.inline.hpp" @@ -51,12 +50,7 @@ inline void ClaimMetadataVisitingOopIterateClosure::do_cld(ClassLoaderData* cld) } inline void ClaimMetadataVisitingOopIterateClosure::do_klass(Klass* k) { - ClassLoaderData* cld = k->class_loader_data(); - if (cld != nullptr) { - ClaimMetadataVisitingOopIterateClosure::do_cld(cld); - } else { - assert(AOTLinkedClassBulkLoader::is_pending_aot_linked_class(k), "sanity"); - } + ClaimMetadataVisitingOopIterateClosure::do_cld(k->class_loader_data()); } inline void ClaimMetadataVisitingOopIterateClosure::do_nmethod(nmethod* nm) { diff --git a/src/hotspot/share/memory/universe.cpp b/src/hotspot/share/memory/universe.cpp index bd47a054bc021..df59a667b77ca 100644 --- a/src/hotspot/share/memory/universe.cpp +++ b/src/hotspot/share/memory/universe.cpp @@ -581,6 +581,10 @@ void Universe::initialize_basic_type_mirrors(TRAPS) { } void Universe::fixup_mirrors(TRAPS) { + if (CDSConfig::is_using_preloaded_classes()) { + return; + } + // Bootstrap problem: all classes gets a mirror (java.lang.Class instance) assigned eagerly, // but we cannot do that for classes created before java.lang.Class is loaded. Here we simply // walk over permanent objects created so far (mostly classes) and fixup their mirrors. Note diff --git a/src/java.base/share/classes/java/lang/ClassLoader.java b/src/java.base/share/classes/java/lang/ClassLoader.java index 6082bc53297af..04a7a99699c23 100644 --- a/src/java.base/share/classes/java/lang/ClassLoader.java +++ b/src/java.base/share/classes/java/lang/ClassLoader.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2019, Azul Systems, Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -58,6 +58,7 @@ import jdk.internal.loader.NativeLibrary; import jdk.internal.loader.NativeLibraries; import jdk.internal.perf.PerfCounter; +import jdk.internal.misc.CDS; import jdk.internal.misc.Unsafe; import jdk.internal.misc.VM; import jdk.internal.reflect.CallerSensitive; @@ -1849,8 +1850,8 @@ public static ClassLoader getSystemClassLoader() { throw new IllegalStateException(msg); default: // system fully initialized - assert VM.isBooted() && scl != null; - return scl; + assert VM.isBooted() && Holder.scl != null; + return Holder.scl; } } @@ -1875,7 +1876,7 @@ static synchronized ClassLoader initSystemClassLoader() { } // detect recursive initialization - if (scl != null) { + if (Holder.scl != null) { throw new IllegalStateException("recursive invocation"); } @@ -1886,7 +1887,7 @@ static synchronized ClassLoader initSystemClassLoader() { // custom class loader is only supported to be loaded from unnamed module Constructor ctor = Class.forName(cn, false, builtinLoader) .getDeclaredConstructor(ClassLoader.class); - scl = (ClassLoader) ctor.newInstance(builtinLoader); + Holder.scl = (ClassLoader) ctor.newInstance(builtinLoader); } catch (Exception e) { Throwable cause = e; if (e instanceof InvocationTargetException) { @@ -1901,9 +1902,9 @@ static synchronized ClassLoader initSystemClassLoader() { throw new Error(cause.getMessage(), cause); } } else { - scl = builtinLoader; + Holder.scl = builtinLoader; } - return scl; + return Holder.scl; } // Returns the class's class loader, or null if none. @@ -1916,9 +1917,13 @@ static ClassLoader getClassLoader(Class caller) { return caller.getClassLoader0(); } - // The system class loader - // @GuardedBy("ClassLoader.class") - private static volatile ClassLoader scl; + // Holder has the field(s) that need to be initialized during JVM bootstrap even if + // the outer is aot-initialized. + private static class Holder { + // The system class loader + // @GuardedBy("ClassLoader.class") + private static volatile ClassLoader scl; + } // -- Package -- @@ -2593,7 +2598,21 @@ private void resetArchivedStates() { if (parallelLockMap != null) { reinitObjectField("parallelLockMap", new ConcurrentHashMap<>()); } - reinitObjectField("packages", new ConcurrentHashMap<>()); + + if (CDS.isDumpingAOTLinkedClasses()) { + if (System.getProperty("cds.debug.archived.packages") != null) { + for (Map.Entry entry : packages.entrySet()) { + String key = entry.getKey(); + NamedPackage value = entry.getValue(); + System.out.println("Archiving " + + (value instanceof Package ? "Package" : "NamedPackage") + + " \"" + key + "\" for " + this); + } + } + } else { + reinitObjectField("packages", new ConcurrentHashMap<>()); + } + reinitObjectField("package2certs", new ConcurrentHashMap<>()); classes.clear(); classes.trimToSize(); diff --git a/src/java.base/share/classes/java/lang/module/ModuleDescriptor.java b/src/java.base/share/classes/java/lang/module/ModuleDescriptor.java index 7aac752cc8922..e3f4f4c7da1cc 100644 --- a/src/java.base/share/classes/java/lang/module/ModuleDescriptor.java +++ b/src/java.base/share/classes/java/lang/module/ModuleDescriptor.java @@ -54,6 +54,8 @@ import jdk.internal.module.Checks; import jdk.internal.module.ModuleInfo; +import jdk.internal.vm.annotation.AOTRuntimeSetup; +import jdk.internal.vm.annotation.AOTSafeClassInitializer; /** @@ -91,6 +93,7 @@ * @since 9 */ +@AOTSafeClassInitializer public final class ModuleDescriptor implements Comparable { @@ -2669,6 +2672,11 @@ private static > long modsValue(Set set) { } static { + runtimeSetup(); + } + + @AOTRuntimeSetup + private static void runtimeSetup() { /** * Setup the shared secret to allow code in other packages access * private package methods in java.lang.module. diff --git a/src/java.base/share/classes/java/net/URI.java b/src/java.base/share/classes/java/net/URI.java index daf63d1903224..d130dc3b46019 100644 --- a/src/java.base/share/classes/java/net/URI.java +++ b/src/java.base/share/classes/java/net/URI.java @@ -43,6 +43,8 @@ import jdk.internal.access.JavaNetUriAccess; import jdk.internal.access.SharedSecrets; import jdk.internal.util.Exceptions; +import jdk.internal.vm.annotation.AOTRuntimeSetup; +import jdk.internal.vm.annotation.AOTSafeClassInitializer; import sun.nio.cs.UTF_8; import static jdk.internal.util.Exceptions.filterNonSocketInfo; @@ -516,6 +518,7 @@ * @see URISyntaxException */ +@AOTSafeClassInitializer public final class URI implements Comparable, Serializable { @@ -3726,7 +3729,13 @@ private int scanHexSeq(int start, int n) } } + static { + runtimeSetup(); + } + + @AOTRuntimeSetup + private static void runtimeSetup() { SharedSecrets.setJavaNetUriAccess( new JavaNetUriAccess() { public URI create(String scheme, String path) { diff --git a/src/java.base/share/classes/java/net/URL.java b/src/java.base/share/classes/java/net/URL.java index 9266b6c94f17e..1435d851f4174 100644 --- a/src/java.base/share/classes/java/net/URL.java +++ b/src/java.base/share/classes/java/net/URL.java @@ -43,6 +43,8 @@ import jdk.internal.access.SharedSecrets; import jdk.internal.misc.ThreadTracker; import jdk.internal.misc.VM; +import jdk.internal.vm.annotation.AOTRuntimeSetup; +import jdk.internal.vm.annotation.AOTSafeClassInitializer; import sun.net.util.IPAddressUtil; import static jdk.internal.util.Exceptions.filterNonSocketInfo; import static jdk.internal.util.Exceptions.formatMsg; @@ -214,6 +216,7 @@ * @author James Gosling * @since 1.0 */ +@AOTSafeClassInitializer public final class URL implements java.io.Serializable { static final String BUILTIN_HANDLERS_PREFIX = "sun.net.www.protocol"; @@ -1758,6 +1761,11 @@ private void setSerializedHashCode(int hc) { } static { + runtimeSetup(); + } + + @AOTRuntimeSetup + private static void runtimeSetup() { SharedSecrets.setJavaNetURLAccess( new JavaNetURLAccess() { @Override diff --git a/src/java.base/share/classes/java/security/SecureClassLoader.java b/src/java.base/share/classes/java/security/SecureClassLoader.java index b398d7332d717..7b0420ec60154 100644 --- a/src/java.base/share/classes/java/security/SecureClassLoader.java +++ b/src/java.base/share/classes/java/security/SecureClassLoader.java @@ -29,6 +29,7 @@ import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Function; +import jdk.internal.misc.CDS; /** * This class extends {@code ClassLoader} with additional support for defining @@ -243,6 +244,20 @@ public boolean equals(Object obj) { * Called by the VM, during -Xshare:dump */ private void resetArchivedStates() { - pdcache.clear(); + if (CDS.isDumpingAOTLinkedClasses()) { + for (CodeSourceKey key : pdcache.keySet()) { + if (key.cs.getCodeSigners() != null) { + // We don't archive any signed classes, so we don't need to cache their ProtectionDomains. + pdcache.remove(key); + } + } + if (System.getProperty("cds.debug.archived.protection.domains") != null) { + for (CodeSourceKey key : pdcache.keySet()) { + System.out.println("Archiving ProtectionDomain " + key.cs + " for " + this); + } + } + } else { + pdcache.clear(); + } } } diff --git a/src/java.base/share/classes/jdk/internal/loader/BootLoader.java b/src/java.base/share/classes/jdk/internal/loader/BootLoader.java index bc5bd9d4265be..72c7e7e745198 100644 --- a/src/java.base/share/classes/jdk/internal/loader/BootLoader.java +++ b/src/java.base/share/classes/jdk/internal/loader/BootLoader.java @@ -75,9 +75,13 @@ private BootLoader() { } private static final ConcurrentHashMap CLASS_LOADER_VALUE_MAP = new ConcurrentHashMap<>(); - // native libraries loaded by the boot class loader - private static final NativeLibraries NATIVE_LIBS - = NativeLibraries.newInstance(null); + // Holder has the field(s) that need to be initialized during JVM bootstrap even if + // the outer is aot-initialized. + private static class Holder { + // native libraries loaded by the boot class loader + private static final NativeLibraries NATIVE_LIBS + = NativeLibraries.newInstance(null); + } /** * Returns the unnamed module for the boot loader. @@ -104,7 +108,7 @@ public static ServicesCatalog getServicesCatalog() { * Returns NativeLibraries for the boot class loader. */ public static NativeLibraries getNativeLibraries() { - return NATIVE_LIBS; + return Holder.NATIVE_LIBS; } /** diff --git a/src/java.base/share/classes/jdk/internal/loader/NativeLibraries.java b/src/java.base/share/classes/jdk/internal/loader/NativeLibraries.java index 44eaab0e83a6a..98cedb0b3bff1 100644 --- a/src/java.base/share/classes/jdk/internal/loader/NativeLibraries.java +++ b/src/java.base/share/classes/jdk/internal/loader/NativeLibraries.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2025, 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 @@ -153,7 +153,7 @@ private NativeLibrary loadLibrary(Class fromClass, String name, boolean isBui } // cannot be loaded by other class loaders - if (loadedLibraryNames.contains(name)) { + if (Holder.loadedLibraryNames.contains(name)) { throw new UnsatisfiedLinkError("Native Library " + name + " already loaded in another classloader"); } @@ -203,7 +203,7 @@ private NativeLibrary loadLibrary(Class fromClass, String name, boolean isBui NativeLibraryContext.pop(); } // register the loaded native library - loadedLibraryNames.add(name); + Holder.loadedLibraryNames.add(name); libraries.put(name, lib); return lib; } finally { @@ -243,6 +243,11 @@ public NativeLibrary loadLibrary(Class fromClass, String name) { return lib; } + // Called at the end of AOTCache assembly phase. + public void clear() { + libraries.clear(); + } + private NativeLibrary findFromPaths(String[] paths, Class fromClass, String name) { for (String path : paths) { File libfile = new File(path, System.mapLibraryName(name)); @@ -368,7 +373,7 @@ public void run() { acquireNativeLibraryLock(name); try { /* remove the native library name */ - if (!loadedLibraryNames.remove(name)) { + if (!Holder.loadedLibraryNames.remove(name)) { throw new IllegalStateException(name + " has already been unloaded"); } NativeLibraryContext.push(UNLOADER); @@ -395,9 +400,13 @@ static class LibraryPaths { static final String[] USER_PATHS = ClassLoaderHelper.parsePath(StaticProperty.javaLibraryPath()); } - // All native libraries we've loaded. - private static final Set loadedLibraryNames = + // Holder has the fields that need to be initialized during JVM bootstrap even if + // the outer is aot-initialized. + static class Holder { + // All native libraries we've loaded. + private static final Set loadedLibraryNames = ConcurrentHashMap.newKeySet(); + } // reentrant lock class that allows exact counting (with external synchronization) @SuppressWarnings("serial") diff --git a/src/java.base/share/classes/jdk/internal/misc/CDS.java b/src/java.base/share/classes/jdk/internal/misc/CDS.java index 72b8479de9a1d..b61743c1fb3e3 100644 --- a/src/java.base/share/classes/jdk/internal/misc/CDS.java +++ b/src/java.base/share/classes/jdk/internal/misc/CDS.java @@ -47,11 +47,13 @@ public class CDS { // Must be in sync with cdsConfig.hpp - private static final int IS_DUMPING_ARCHIVE = 1 << 0; - private static final int IS_DUMPING_METHOD_HANDLES = 1 << 1; - private static final int IS_DUMPING_STATIC_ARCHIVE = 1 << 2; - private static final int IS_LOGGING_LAMBDA_FORM_INVOKERS = 1 << 3; - private static final int IS_USING_ARCHIVE = 1 << 4; + private static final int IS_DUMPING_AOT_LINKED_CLASSES = 1 << 0; + private static final int IS_DUMPING_ARCHIVE = 1 << 1; + private static final int IS_DUMPING_METHOD_HANDLES = 1 << 2; + private static final int IS_DUMPING_STATIC_ARCHIVE = 1 << 3; + private static final int IS_LOGGING_LAMBDA_FORM_INVOKERS = 1 << 4; + private static final int IS_USING_ARCHIVE = 1 << 5; + private static final int configStatus = getCDSConfigStatus(); /** @@ -82,6 +84,10 @@ public static boolean isDumpingStaticArchive() { return (configStatus & IS_DUMPING_STATIC_ARCHIVE) != 0; } + public static boolean isDumpingAOTLinkedClasses() { + return (configStatus & IS_DUMPING_AOT_LINKED_CLASSES) != 0; + } + public static boolean isSingleThreadVM() { return isDumpingStaticArchive(); }