From a57bd2f76ecf852752dfde4ceec4ec88e58566c9 Mon Sep 17 00:00:00 2001 From: Ashutosh Mehra Date: Sun, 18 Jan 2026 00:29:50 -0500 Subject: [PATCH 01/30] Support custom loaders Signed-off-by: Ashutosh Mehra --- src/hotspot/share/cds/aotMappedHeapWriter.cpp | 3 + src/hotspot/share/cds/aotMetaspace.cpp | 3 + src/hotspot/share/cds/cdsProtectionDomain.cpp | 144 +++++++++----- src/hotspot/share/cds/cdsProtectionDomain.hpp | 2 + src/hotspot/share/cds/finalImageRecipes.cpp | 44 ++++- src/hotspot/share/cds/finalImageRecipes.hpp | 1 + src/hotspot/share/cds/heapShared.cpp | 10 +- .../share/classfile/classFileParser.cpp | 2 +- .../share/classfile/classLoaderData.cpp | 19 +- .../share/classfile/classLoaderData.hpp | 7 +- .../share/classfile/classLoaderDataShared.cpp | 184 ++++++++++++++++-- .../share/classfile/classLoaderDataShared.hpp | 4 +- src/hotspot/share/classfile/javaClasses.cpp | 12 +- src/hotspot/share/classfile/javaClasses.hpp | 2 + src/hotspot/share/classfile/moduleEntry.cpp | 3 +- src/hotspot/share/classfile/modules.cpp | 7 +- .../share/classfile/systemDictionary.cpp | 2 +- .../classfile/systemDictionaryShared.cpp | 77 ++++---- .../classfile/systemDictionaryShared.hpp | 10 +- src/hotspot/share/classfile/vmSymbols.hpp | 4 + src/hotspot/share/include/jvm.h | 3 + src/hotspot/share/memory/universe.cpp | 5 +- src/hotspot/share/oops/constantPool.cpp | 2 +- src/hotspot/share/oops/instanceKlass.cpp | 8 +- src/hotspot/share/oops/klass.cpp | 1 + src/hotspot/share/oops/klass.hpp | 12 +- src/hotspot/share/oops/symbol.cpp | 10 + src/hotspot/share/oops/symbol.hpp | 7 + src/hotspot/share/prims/jvm.cpp | 5 + .../share/classes/java/lang/ClassLoader.java | 29 ++- .../share/classes/java/lang/NamedPackage.java | 2 +- .../jdk/internal/loader/ClassLoaders.java | 3 + .../share/native/libjava/ClassLoader.c | 3 +- 33 files changed, 496 insertions(+), 134 deletions(-) diff --git a/src/hotspot/share/cds/aotMappedHeapWriter.cpp b/src/hotspot/share/cds/aotMappedHeapWriter.cpp index f360486586e..b6afd545682 100644 --- a/src/hotspot/share/cds/aotMappedHeapWriter.cpp +++ b/src/hotspot/share/cds/aotMappedHeapWriter.cpp @@ -550,10 +550,13 @@ size_t AOTMappedHeapWriter::copy_one_source_obj_to_buffer(oop src_obj) { } else if (java_lang_ClassLoader::is_instance(src_obj)) { #ifdef ASSERT // We only archive these loaders + assert(java_lang_ClassLoader::aotIdentity(src_obj) != nullptr, "sanity check"); +#if 0 if (src_obj != SystemDictionary::java_platform_loader() && src_obj != SystemDictionary::java_system_loader()) { assert(src_obj->klass()->name()->equals("jdk/internal/loader/ClassLoaders$BootClassLoader"), "must be"); } +#endif #endif update_buffered_object_field(to, java_lang_ClassLoader::loader_data_offset(), nullptr); } diff --git a/src/hotspot/share/cds/aotMetaspace.cpp b/src/hotspot/share/cds/aotMetaspace.cpp index e00ac36200e..884ba5bb7a5 100644 --- a/src/hotspot/share/cds/aotMetaspace.cpp +++ b/src/hotspot/share/cds/aotMetaspace.cpp @@ -735,6 +735,9 @@ char* VM_PopulateDumpSharedSpace::dump_early_read_only_tables() { char* VM_PopulateDumpSharedSpace::dump_read_only_tables(AOTClassLocationConfig*& cl_config) { ArchiveBuilder::OtherROAllocMark mark; + if (CDSConfig::is_dumping_full_module_graph()) { + ClassLoaderDataShared::write_cld_table(); + } SystemDictionaryShared::write_to_archive(); cl_config = AOTClassLocationConfig::dumptime()->write_to_archive(); AOTClassLinker::write_to_archive(); diff --git a/src/hotspot/share/cds/cdsProtectionDomain.cpp b/src/hotspot/share/cds/cdsProtectionDomain.cpp index ff15fdccabe..2fa6da81f28 100644 --- a/src/hotspot/share/cds/cdsProtectionDomain.cpp +++ b/src/hotspot/share/cds/cdsProtectionDomain.cpp @@ -47,59 +47,66 @@ OopHandle CDSProtectionDomain::_shared_jar_manifests; // the given InstanceKlass. // Returns the ProtectionDomain for the InstanceKlass. Handle CDSProtectionDomain::init_security_info(Handle class_loader, InstanceKlass* ik, PackageEntry* pkg_entry, TRAPS) { - int index = ik->shared_classpath_index(); - assert(index >= 0, "Sanity"); - const AOTClassLocation* cl = AOTClassLocationConfig::runtime()->class_location_at(index); - Symbol* class_name = ik->name(); - - if (cl->is_modules_image()) { - // For shared app/platform classes originated from the run-time image: - // The ProtectionDomains are cached in the corresponding ModuleEntries - // for fast access by the VM. - // all packages from module image are already created during VM bootstrap in - // Modules::define_module(). - assert(pkg_entry != nullptr, "archived class in module image cannot be from unnamed package"); - ModuleEntry* mod_entry = pkg_entry->module(); - return get_shared_protection_domain(class_loader, mod_entry, THREAD); + if (ik->defined_by_other_loaders()) { + // define NamedPackage in java layer + define_named_package(class_loader, pkg_entry, THREAD); + // Get ClassLoader::defaultDomain + return get_default_protection_domain(class_loader, THREAD); } else { - // For shared app/platform classes originated from JAR files on the class path: - // Each of the 3 CDSProtectionDomain::_shared_xxx arrays has the same length - // as the shared classpath table in the shared archive. - // - // If a shared InstanceKlass k is loaded from the class path, let - // - // index = k->shared_classpath_index(); - // - // AOTClassLocationConfig::_runtime_instance->_array->at(index) identifies the JAR file that contains k. - // - // k's protection domain is: - // - // ProtectionDomain pd = _shared_protection_domains[index]; - // - // and k's Package is initialized using - // - // manifest = _shared_jar_manifests[index]; - // url = _shared_jar_urls[index]; - // define_shared_package(class_name, class_loader, manifest, url, CHECK_NH); - // - // Note that if an element of these 3 _shared_xxx arrays is null, it will be initialized by - // the corresponding CDSProtectionDomain::get_shared_xxx() function. - Handle manifest = get_shared_jar_manifest(index, CHECK_NH); - Handle url = get_shared_jar_url(index, CHECK_NH); - int index_offset = index - AOTClassLocationConfig::runtime()->app_cp_start_index(); - if (index_offset < PackageEntry::max_index_for_defined_in_class_path()) { - if (pkg_entry == nullptr || !pkg_entry->is_defined_by_cds_in_class_path(index_offset)) { - // define_shared_package only needs to be called once for each package in a jar specified - // in the shared class path. - define_shared_package(class_name, class_loader, manifest, url, CHECK_NH); - if (pkg_entry != nullptr) { - pkg_entry->set_defined_by_cds_in_class_path(index_offset); + int index = ik->shared_classpath_index(); + assert(index >= 0, "Sanity"); + const AOTClassLocation* cl = AOTClassLocationConfig::runtime()->class_location_at(index); + Symbol* class_name = ik->name(); + + if (cl->is_modules_image()) { + // For shared app/platform classes originated from the run-time image: + // The ProtectionDomains are cached in the corresponding ModuleEntries + // for fast access by the VM. + // all packages from module image are already created during VM bootstrap in + // Modules::define_module(). + assert(pkg_entry != nullptr, "archived class in module image cannot be from unnamed package"); + ModuleEntry* mod_entry = pkg_entry->module(); + return get_shared_protection_domain(class_loader, mod_entry, THREAD); + } else { + // For shared app/platform classes originated from JAR files on the class path: + // Each of the 3 CDSProtectionDomain::_shared_xxx arrays has the same length + // as the shared classpath table in the shared archive. + // + // If a shared InstanceKlass k is loaded from the class path, let + // + // index = k->shared_classpath_index(); + // + // AOTClassLocationConfig::_runtime_instance->_array->at(index) identifies the JAR file that contains k. + // + // k's protection domain is: + // + // ProtectionDomain pd = _shared_protection_domains[index]; + // + // and k's Package is initialized using + // + // manifest = _shared_jar_manifests[index]; + // url = _shared_jar_urls[index]; + // define_shared_package(class_name, class_loader, manifest, url, CHECK_NH); + // + // Note that if an element of these 3 _shared_xxx arrays is null, it will be initialized by + // the corresponding CDSProtectionDomain::get_shared_xxx() function. + Handle manifest = get_shared_jar_manifest(index, CHECK_NH); + Handle url = get_shared_jar_url(index, CHECK_NH); + int index_offset = index - AOTClassLocationConfig::runtime()->app_cp_start_index(); + if (index_offset < PackageEntry::max_index_for_defined_in_class_path()) { + if (pkg_entry == nullptr || !pkg_entry->is_defined_by_cds_in_class_path(index_offset)) { + // define_shared_package only needs to be called once for each package in a jar specified + // in the shared class path. + define_shared_package(class_name, class_loader, manifest, url, CHECK_NH); + if (pkg_entry != nullptr) { + pkg_entry->set_defined_by_cds_in_class_path(index_offset); + } } + } else { + define_shared_package(class_name, class_loader, manifest, url, CHECK_NH); } - } else { - define_shared_package(class_name, class_loader, manifest, url, CHECK_NH); + return get_shared_protection_domain(class_loader, index, url, THREAD); } - return get_shared_protection_domain(class_loader, index, url, THREAD); } } @@ -119,7 +126,7 @@ PackageEntry* CDSProtectionDomain::get_package_entry_from_class(InstanceKlass* i PackageEntry* pkg_entry = ik->package(); if (CDSConfig::is_using_full_module_graph() && ik->in_aot_cache() && pkg_entry != nullptr) { assert(AOTMetaspace::in_aot_cache(pkg_entry), "must be"); - assert(!ik->defined_by_other_loaders(), "unexpected archived package entry for an unregistered class"); + assert(!ik->defined_by_other_loaders() || ik->cl_aot_identity() != nullptr, "unexpected archived package entry for an unregistered class"); return pkg_entry; } TempNewSymbol pkg_name = ClassLoader::package_from_class_name(ik->name()); @@ -158,6 +165,33 @@ void CDSProtectionDomain::define_shared_package(Symbol* class_name, } } +void CDSProtectionDomain::define_named_package(Handle class_loader, PackageEntry* pkg_entry, TRAPS) { + Handle pkgname_string; + Handle module_h; + + if (pkg_entry != nullptr) { + ResourceMark rm(THREAD); + Symbol* pkg = pkg_entry->name(); + const char* pkgname = pkg->as_klass_external_name(); + pkgname_string = java_lang_String::create_from_str(pkgname, CHECK); + module_h = Handle(THREAD, pkg_entry->module()->module_oop()); + } else { + // unnamed package; get unnamed module from class_loader + module_h = Handle(THREAD, java_lang_ClassLoader::unnamedModule(class_loader())); + } + if (pkgname_string.not_null()) { + JavaValue result(T_OBJECT); + JavaCallArguments args(2); + args.set_receiver(class_loader); + args.push_oop(pkgname_string); + args.push_oop(module_h); + JavaCalls::call_virtual(&result, vmClasses::ClassLoader_klass(), + vmSymbols::getNamedPackage_name(), + vmSymbols::getNamedPackage_signature(), + &args, + CHECK); + } +} Handle CDSProtectionDomain::create_jar_manifest(const char* manifest_chars, size_t size, TRAPS) { typeArrayOop buf = oopFactory::new_byteArray((int)size, CHECK_NH); typeArrayHandle bufhandle(THREAD, buf); @@ -235,6 +269,16 @@ Handle CDSProtectionDomain::get_protection_domain_from_classloader(Handle class_ return Handle(THREAD, obj_result.get_oop()); } +Handle CDSProtectionDomain::get_default_protection_domain(Handle class_loader, TRAPS) { + Handle protection_domain; + JavaValue obj_result(T_OBJECT); + JavaCalls::call_virtual(&obj_result, class_loader, vmClasses::ClassLoader_klass(), + vmSymbols::getDefaultProtectionDomain_name(), + vmSymbols::getDefaultProtectionDomain_signature(), + CHECK_NH); + return Handle(THREAD, obj_result.get_oop()); +} + // Returns the ProtectionDomain associated with the JAR file identified by the url. Handle CDSProtectionDomain::get_shared_protection_domain(Handle class_loader, int shared_path_index, diff --git a/src/hotspot/share/cds/cdsProtectionDomain.hpp b/src/hotspot/share/cds/cdsProtectionDomain.hpp index 4a82d6fe869..6141431b78b 100644 --- a/src/hotspot/share/cds/cdsProtectionDomain.hpp +++ b/src/hotspot/share/cds/cdsProtectionDomain.hpp @@ -113,6 +113,8 @@ class CDSProtectionDomain : AllStatic { static void atomic_set_shared_jar_manifest(int index, oop man) { atomic_set_array_index(_shared_jar_manifests, index, man); } + static void define_named_package(Handle class_loader, PackageEntry* pkg_entry, TRAPS); + static Handle get_default_protection_domain(Handle class_loader, TRAPS); }; #endif // SHARE_CDS_CDSPROTECTIONDOMAIN_HPP diff --git a/src/hotspot/share/cds/finalImageRecipes.cpp b/src/hotspot/share/cds/finalImageRecipes.cpp index fccf08f0a91..2bee970d04f 100644 --- a/src/hotspot/share/cds/finalImageRecipes.cpp +++ b/src/hotspot/share/cds/finalImageRecipes.cpp @@ -35,6 +35,7 @@ #include "memory/oopFactory.hpp" #include "memory/resourceArea.hpp" #include "oops/constantPool.inline.hpp" +#include "oops/symbol.hpp" #include "runtime/handles.inline.hpp" #include "runtime/mutexLocker.hpp" @@ -43,6 +44,9 @@ GrowableArray* FinalImageRecipes::_tmp_reflect_flags = nullptr; GrowableArray* FinalImageRecipes::_tmp_dynamic_proxy_classes = nullptr; static FinalImageRecipes* _final_image_recipes = nullptr; +using AOTIdentityToLoaderObjectMap = HashTable; +static AOTIdentityToLoaderObjectMap* _aot_compatible_loaders_map = nullptr; + void* FinalImageRecipes::operator new(size_t size) throw() { return ArchiveBuilder::current()->ro_region_alloc(size); } @@ -257,11 +261,11 @@ void FinalImageRecipes::load_all_classes(TRAPS) { int flags = _flags->at(i); if (k->is_instance_klass()) { InstanceKlass* ik = InstanceKlass::cast(k); - if (ik->defined_by_other_loaders()) { + if (ik->cl_aot_identity() == nullptr) { SystemDictionaryShared::init_dumptime_info(ik); SystemDictionaryShared::add_unregistered_class(THREAD, ik); SystemDictionaryShared::copy_unregistered_class_size_and_crc32(ik); - } else if (!ik->is_hidden()) { + } else if (!ik->is_hidden() && !ik->defined_by_other_loaders()) { Klass* actual = SystemDictionary::resolve_or_fail(ik->name(), class_loader, true, CHECK); if (actual != ik) { ResourceMark rm(THREAD); @@ -274,12 +278,32 @@ void FinalImageRecipes::load_all_classes(TRAPS) { ik->link_class(CHECK); if (ik->has_aot_safe_initializer() && (flags & WAS_INITED) != 0) { - assert(ik->class_loader() == nullptr, "supported only for boot classes for now"); + //assert(ik->class_loader() == nullptr, "supported only for boot classes for now"); ik->initialize(CHECK); } } } } + + // Custom Loaders must be marked with AOTSafeClassInitializer annotation. + // They would have been created as part of running class initializer for custom loaders. + // Objects of such custom loaders get registered with VM in ClassLoader::registerAsAOTCompatibleLoader(). + // Use these objects to load additional classes not yet loaded. + for (int i = 0; i < _all_klasses->length(); i++) { + Klass* k = _all_klasses->at(i); + int flags = _flags->at(i); + if (k->is_instance_klass()) { + InstanceKlass* ik = InstanceKlass::cast(k); + if (ik->defined_by_other_loaders() && ik->cl_aot_identity() != nullptr) { + Symbol* aot_identity = ik->cl_aot_identity(); + OopHandle* loader_h_ptr = _aot_compatible_loaders_map->get(aot_identity); + if (loader_h_ptr != nullptr) { + OopHandle loader_h = *loader_h_ptr; + Klass* actual = SystemDictionary::resolve_or_fail(ik->name(), Handle(THREAD, loader_h.resolve()), true, CHECK); + } + } + } + } } void FinalImageRecipes::apply_recipes_for_reflection_data(JavaThread* current) { @@ -406,3 +430,17 @@ void FinalImageRecipes::apply_recipes_impl(TRAPS) { void FinalImageRecipes::serialize(SerializeClosure* soc) { soc->do_ptr((void**)&_final_image_recipes); } + +void FinalImageRecipes::add_aot_compatible_loader(oop loader, TRAPS) { + ResourceMark rm; + if (_aot_compatible_loaders_map == nullptr) { + _aot_compatible_loaders_map = new (mtClassShared) AOTIdentityToLoaderObjectMap(); + } + + OopHandle loader_h(Universe::vm_global(), loader); + Klass* klass = loader->klass(); + Symbol* aot_identity = java_lang_String::as_symbol(java_lang_ClassLoader::aotIdentity(loader)); + + log_info(cds)("Adding AOT compatible loader with aot identity=%s", aot_identity->as_C_string()); + _aot_compatible_loaders_map->put(aot_identity, loader_h); +} diff --git a/src/hotspot/share/cds/finalImageRecipes.hpp b/src/hotspot/share/cds/finalImageRecipes.hpp index 9638fc954bc..29a78c94d76 100644 --- a/src/hotspot/share/cds/finalImageRecipes.hpp +++ b/src/hotspot/share/cds/finalImageRecipes.hpp @@ -110,6 +110,7 @@ class FinalImageRecipes { // Called when dumping final image static void apply_recipes(TRAPS); + static void add_aot_compatible_loader(oop loader, TRAPS); }; #endif // SHARE_CDS_FINALIMAGERECIPES_HPP diff --git a/src/hotspot/share/cds/heapShared.cpp b/src/hotspot/share/cds/heapShared.cpp index f5197df6674..8c808168273 100644 --- a/src/hotspot/share/cds/heapShared.cpp +++ b/src/hotspot/share/cds/heapShared.cpp @@ -1164,7 +1164,7 @@ void KlassSubGraphInfo::add_subgraph_object_klass(Klass* orig_k) { if (CDSConfig::is_dumping_method_handles()) { // -XX:AOTInitTestClass must be used carefully in regression tests to // include only classes that are safe to aot-initialize. - assert(ik->class_loader() == nullptr || + assert(ik->class_loader() == nullptr || ik->cl_aot_identity() != nullptr || HeapShared::is_lambda_proxy_klass(ik) || AOTClassInitializer::has_test_class(), "we can archive only instances of boot classes or lambda proxy classes"); @@ -1244,6 +1244,10 @@ void KlassSubGraphInfo::check_allowed_klass(InstanceKlass* ik) { const char* testcls_msg = ""; #endif + if (ik->cl_aot_identity() != nullptr) { + return; + } + ResourceMark rm; log_error(aot, heap)("Class %s not allowed in archive heap. Must be in java.base%s%s", ik->external_name(), lambda_msg, testcls_msg); @@ -1477,7 +1481,7 @@ void HeapShared::resolve_classes_for_subgraphs(JavaThread* current, ArchivableSt for (int i = 0; fields[i].valid(); i++) { ArchivableStaticFieldInfo* info = &fields[i]; TempNewSymbol klass_name = SymbolTable::new_symbol(info->klass_name); - InstanceKlass* k = SystemDictionaryShared::find_builtin_class(klass_name); + InstanceKlass* k = SystemDictionaryShared::find_class_in_aot_compatible_dictionary(klass_name); assert(k != nullptr && k->defined_by_boot_loader(), "sanity"); resolve_classes_for_subgraph_of(current, k); } @@ -1681,7 +1685,7 @@ HeapShared::resolve_or_init_classes_for_subgraph_of(Klass* k, bool do_init, TRAP void HeapShared::resolve_or_init(const char* klass_name, bool do_init, TRAPS) { TempNewSymbol klass_name_sym = SymbolTable::new_symbol(klass_name); - InstanceKlass* k = SystemDictionaryShared::find_builtin_class(klass_name_sym); + InstanceKlass* k = SystemDictionaryShared::find_class_in_aot_compatible_dictionary(klass_name_sym); if (k == nullptr) { return; } diff --git a/src/hotspot/share/classfile/classFileParser.cpp b/src/hotspot/share/classfile/classFileParser.cpp index eb8a2a389b9..1da22ec8d60 100644 --- a/src/hotspot/share/classfile/classFileParser.cpp +++ b/src/hotspot/share/classfile/classFileParser.cpp @@ -1902,7 +1902,7 @@ AnnotationCollector::annotation_index(const ClassLoaderData* loader_data, } case VM_SYMBOL_ENUM_NAME(jdk_internal_vm_annotation_AOTSafeClassInitializer_signature): { if (_location != _in_class) break; // only allow for classes - if (!privileged) break; // only allow in privileged code + //if (!privileged) break; // only allow in privileged code return _jdk_internal_vm_annotation_AOTSafeClassInitializer; } case VM_SYMBOL_ENUM_NAME(jdk_internal_vm_annotation_AOTRuntimeSetup_signature): { diff --git a/src/hotspot/share/classfile/classLoaderData.cpp b/src/hotspot/share/classfile/classLoaderData.cpp index dfc3b74db96..0806c287234 100644 --- a/src/hotspot/share/classfile/classLoaderData.cpp +++ b/src/hotspot/share/classfile/classLoaderData.cpp @@ -48,6 +48,7 @@ #include "classfile/classLoaderData.inline.hpp" #include "classfile/classLoaderDataGraph.inline.hpp" +#include "classfile/classLoaderDataShared.hpp" #include "classfile/dictionary.hpp" #include "classfile/javaClasses.inline.hpp" #include "classfile/moduleEntry.hpp" @@ -89,7 +90,7 @@ void ClassLoaderData::init_null_class_loader_data() { _the_null_class_loader_data = new ClassLoaderData(Handle(), false); ClassLoaderDataGraph::_head = _the_null_class_loader_data; assert(_the_null_class_loader_data->is_the_null_class_loader_data(), "Must be"); - + _the_null_class_loader_data->_aot_identity = SymbolTable::new_symbol("BOOT"); LogTarget(Trace, class, loader, data) lt; if (lt.is_enabled()) { ResourceMark rm; @@ -149,11 +150,16 @@ ClassLoaderData::ClassLoaderData(Handle h_class_loader, bool has_class_mirror_ho _deallocate_list(nullptr), _next(nullptr), _unloading_next(nullptr), - _class_loader_klass(nullptr), _name(nullptr), _name_and_id(nullptr) { + _class_loader_klass(nullptr), _name(nullptr), _name_and_id(nullptr), + _aot_identity(nullptr), _restored(false) { if (!h_class_loader.is_null()) { _class_loader = _handles.add(h_class_loader()); _class_loader_klass = h_class_loader->klass(); + oop aot_identity = java_lang_ClassLoader::aotIdentity(h_class_loader()); + if (aot_identity != nullptr) { + _aot_identity = java_lang_String::as_symbol(aot_identity); + } initialize_name(h_class_loader); } @@ -174,6 +180,11 @@ ClassLoaderData::ClassLoaderData(Handle h_class_loader, bool has_class_mirror_ho } _dictionary = create_dictionary(); } +#if 0 + if (_aot_identity != nullptr && CDSConfig::is_using_full_module_graph()) { + ClassLoaderDataShared::restore_archived_data(this); + } +#endif NOT_PRODUCT(_dependency_count = 0); // number of class loader dependencies @@ -632,11 +643,11 @@ void ClassLoaderData::unload() { } } -ModuleEntryTable* ClassLoaderData::modules() { +ModuleEntryTable* ClassLoaderData::modules(bool create_if_null) { // Lazily create the module entry table at first request. // Lock-free access requires load_acquire. ModuleEntryTable* modules = AtomicAccess::load_acquire(&_modules); - if (modules == nullptr) { + if (modules == nullptr && create_if_null) { MutexLocker m1(Module_lock); // Check if _modules got allocated while we were waiting for this lock. if ((modules = _modules) == nullptr) { diff --git a/src/hotspot/share/classfile/classLoaderData.hpp b/src/hotspot/share/classfile/classLoaderData.hpp index 64fcfb7519f..1ab76b7d772 100644 --- a/src/hotspot/share/classfile/classLoaderData.hpp +++ b/src/hotspot/share/classfile/classLoaderData.hpp @@ -174,6 +174,8 @@ class ClassLoaderData : public CHeapObj { Klass* _class_loader_klass; Symbol* _name; Symbol* _name_and_id; + Symbol* _aot_identity; + bool _restored; JFR_ONLY(DEFINE_TRACE_ID_FIELD;) void set_next(ClassLoaderData* next); @@ -329,7 +331,7 @@ class ClassLoaderData : public CHeapObj { void record_dependency(const Klass* to); PackageEntryTable* packages() { return _packages; } ModuleEntry* unnamed_module() { return _unnamed_module; } - ModuleEntryTable* modules(); + ModuleEntryTable* modules(bool create_if_null = true); bool modules_defined() { return (_modules != nullptr); } // Offsets @@ -358,6 +360,9 @@ class ClassLoaderData : public CHeapObj { // Obtain the class loader's _name_and_id, works during unloading. const char* loader_name_and_id() const; Symbol* name_and_id() const { return _name_and_id; } + Symbol* aot_identity() const { return _aot_identity; } + bool restored() const { return _restored; } + void set_restored(bool v) { _restored = v; } unsigned identity_hash() const { return (unsigned)((uintptr_t)this >> LogBytesPerWord); diff --git a/src/hotspot/share/classfile/classLoaderDataShared.cpp b/src/hotspot/share/classfile/classLoaderDataShared.cpp index 1097289addb..e11f4e425f4 100644 --- a/src/hotspot/share/classfile/classLoaderDataShared.cpp +++ b/src/hotspot/share/classfile/classLoaderDataShared.cpp @@ -23,15 +23,19 @@ */ #include "cds/aotLogging.hpp" +#include "cds/archiveBuilder.hpp" #include "cds/cdsConfig.hpp" #include "cds/heapShared.inline.hpp" #include "cds/serializeClosure.hpp" #include "classfile/classLoaderData.inline.hpp" +#include "classfile/classLoaderDataGraph.hpp" #include "classfile/classLoaderDataShared.hpp" #include "classfile/moduleEntry.hpp" #include "classfile/modules.hpp" #include "classfile/packageEntry.hpp" +#include "classfile/symbolTable.hpp" #include "classfile/systemDictionary.hpp" +#include "classfile/systemDictionaryShared.hpp" #include "logging/log.hpp" #include "runtime/handles.inline.hpp" #include "runtime/safepoint.hpp" @@ -44,6 +48,7 @@ class ArchivedClassLoaderData { Array* _packages; Array* _modules; ModuleEntry* _unnamed_module; + Symbol* _aot_identity; void assert_valid(ClassLoaderData* loader_data) { // loader_data may be null if the boot layer has loaded no modules for the platform or @@ -62,6 +67,7 @@ class ArchivedClassLoaderData { ModuleEntry* unnamed_module() { return _unnamed_module; } + Symbol* aot_identity() { return _aot_identity; } void serialize(SerializeClosure* f) { f->do_ptr(&_packages); @@ -73,13 +79,23 @@ class ArchivedClassLoaderData { void clear_archived_oops(); }; +#if 0 static ArchivedClassLoaderData _archived_boot_loader_data; static ArchivedClassLoaderData _archived_platform_loader_data; static ArchivedClassLoaderData _archived_system_loader_data; +#endif + static ModuleEntry* _archived_javabase_moduleEntry = nullptr; static int _platform_loader_root_index = -1; static int _system_loader_root_index = -1; +static bool archived_cld_map_equals_fn(ArchivedClassLoaderData* archived_cld, Symbol* sym, int len) { + return archived_cld->aot_identity()->equals(sym); +} + +using ArchivedCLDataMap = OffsetCompactHashtable; +static ArchivedCLDataMap _archived_cld_map; + void ArchivedClassLoaderData::iterate_symbols(ClassLoaderData* loader_data, MetaspaceClosure* closure) { assert(CDSConfig::is_dumping_full_module_graph(), "must be"); assert_valid(loader_data); @@ -99,8 +115,15 @@ void ArchivedClassLoaderData::allocate(ClassLoaderData* loader_data) { // So we store the packages/modules in Arrays. At runtime, we create // the hashtables using these arrays. _packages = loader_data->packages()->allocate_archived_entries(); - _modules = loader_data->modules() ->allocate_archived_entries(); + if (loader_data->modules(false) != nullptr) { + _modules = loader_data->modules(false)->allocate_archived_entries(); + } _unnamed_module = loader_data->unnamed_module()->allocate_archived_entry(); + _aot_identity = loader_data->aot_identity(); + ArchivePtrMarker::mark_pointer((address*)&_packages); + ArchivePtrMarker::mark_pointer((address*)&_modules); + ArchivePtrMarker::mark_pointer((address*)&_unnamed_module); + ArchivePtrMarker::mark_pointer((address*)&_aot_identity); } } @@ -109,8 +132,12 @@ void ArchivedClassLoaderData::init_archived_entries(ClassLoaderData* loader_data assert_valid(loader_data); if (loader_data != nullptr) { loader_data->packages()->init_archived_entries(_packages); - loader_data->modules() ->init_archived_entries(_modules); + if (loader_data->modules(false) != nullptr) { + loader_data->modules(false) ->init_archived_entries(_modules); + } _unnamed_module->init_as_archived_entry(); + _aot_identity = ArchiveBuilder::get_buffered_symbol(_aot_identity); + assert(_aot_identity != nullptr, "sanity check"); } } @@ -152,7 +179,8 @@ void ArchivedClassLoaderData::clear_archived_oops() { // ------------------------------ -void ClassLoaderDataShared::load_archived_platform_and_system_class_loaders() { +void ClassLoaderDataShared::load_archived_platform_and_system_class_loaders(Handle h_platform_loader, Handle h_system_loader) { + #if INCLUDE_CDS_JAVA_HEAP // The streaming object loader prefers loading the class loader related objects before // the CLD constructor which has a NoSafepointVerifier. @@ -169,12 +197,24 @@ void ClassLoaderDataShared::load_archived_platform_and_system_class_loaders() { } // When using the full module graph, we need to load unnamed modules too. - ModuleEntry* platform_loader_module_entry = _archived_platform_loader_data.unnamed_module(); + + Symbol* aot_identity_sym = nullptr; + oop aot_identity = java_lang_ClassLoader::aotIdentity(h_platform_loader()); + assert(aot_identity != nullptr, "sanity check"); + aot_identity_sym = java_lang_String::as_symbol(aot_identity); + unsigned int hash = Symbol::symbol_hash(aot_identity_sym); + ArchivedClassLoaderData* archived_platform_cld = _archived_cld_map.lookup(aot_identity_sym, hash, 0); + ModuleEntry* platform_loader_module_entry = archived_platform_cld->unnamed_module(); if (platform_loader_module_entry != nullptr) { platform_loader_module_entry->preload_archived_oops(); } - ModuleEntry* system_loader_module_entry = _archived_system_loader_data.unnamed_module(); + aot_identity = java_lang_ClassLoader::aotIdentity(h_system_loader()); + assert(aot_identity != nullptr, "sanity check"); + aot_identity_sym = java_lang_String::as_symbol(aot_identity); + hash = Symbol::symbol_hash(aot_identity_sym); + ArchivedClassLoaderData* archived_system_cld = _archived_cld_map.lookup(aot_identity_sym, hash, 0); + ModuleEntry* system_loader_module_entry = archived_system_cld->unnamed_module(); if (system_loader_module_entry != nullptr) { system_loader_module_entry->preload_archived_oops(); } @@ -186,7 +226,7 @@ static ClassLoaderData* null_class_loader_data() { assert(loader_data != nullptr, "must be"); return loader_data; } - +#if 0 static ClassLoaderData* java_platform_loader_data_or_null() { return ClassLoaderData::class_loader_data_or_null(SystemDictionary::java_platform_loader()); } @@ -194,6 +234,7 @@ static ClassLoaderData* java_platform_loader_data_or_null() { static ClassLoaderData* java_system_loader_data_or_null() { return ClassLoaderData::class_loader_data_or_null(SystemDictionary::java_system_loader()); } +#endif // ModuleEntryTables (even if empty) are required for iterate_symbols() to scan the // platform/system loaders inside the CDS safepoint, but the tables can be created only @@ -210,26 +251,69 @@ void ClassLoaderDataShared::ensure_module_entry_table_exists(oop class_loader) { assert(met != nullptr, "sanity"); } +class CLDSymbolsIterator : public CLDClosure { + MetaspaceClosure* _closure; + public: + CLDSymbolsIterator(MetaspaceClosure* closure) : _closure(closure) {} + void do_cld(ClassLoaderData* cld) { + if (cld->aot_identity() != nullptr) { + cld->packages()->iterate_symbols(_closure); + if (cld->modules(false) != nullptr) { + cld->modules(false)->iterate_symbols(_closure); + } + cld->unnamed_module()->iterate_symbols(_closure); + } + } +}; + void ClassLoaderDataShared::iterate_symbols(MetaspaceClosure* closure) { assert(CDSConfig::is_dumping_full_module_graph(), "must be"); + CLDSymbolsIterator sym_iterator(closure); + ClassLoaderDataGraph::cld_do(&sym_iterator); +#if 0 _archived_boot_loader_data.iterate_symbols (null_class_loader_data(), closure); _archived_platform_loader_data.iterate_symbols(java_platform_loader_data_or_null(), closure); _archived_system_loader_data.iterate_symbols (java_system_loader_data_or_null(), closure); +#endif } +using ArchivedCLDToCLDMap = HashTable; +ArchivedCLDToCLDMap* _aot_compatible_loaders = nullptr; + +class ArchiveAOTCompatibleLoaders : public CLDClosure { + public: + void do_cld(ClassLoaderData* cld) { + if (cld->aot_identity() != nullptr) { + ArchivedClassLoaderData* archived_entry = (ArchivedClassLoaderData*)ArchiveBuilder::rw_region_alloc(sizeof(ArchivedClassLoaderData)); + archived_entry->allocate(cld); + _aot_compatible_loaders->put(archived_entry, cld); + } + } +}; + void ClassLoaderDataShared::allocate_archived_tables() { assert(CDSConfig::is_dumping_full_module_graph(), "must be"); + _aot_compatible_loaders = new (mtClassShared) ArchivedCLDToCLDMap(); + ArchiveAOTCompatibleLoaders archive_closure; + ClassLoaderDataGraph::cld_do(&archive_closure); + +#if 0 _archived_boot_loader_data.allocate (null_class_loader_data()); _archived_platform_loader_data.allocate(java_platform_loader_data_or_null()); _archived_system_loader_data.allocate (java_system_loader_data_or_null()); +#endif } void ClassLoaderDataShared::init_archived_tables() { assert(CDSConfig::is_dumping_full_module_graph(), "must be"); - +#if 0 _archived_boot_loader_data.init_archived_entries (null_class_loader_data()); _archived_platform_loader_data.init_archived_entries(java_platform_loader_data_or_null()); _archived_system_loader_data.init_archived_entries (java_system_loader_data_or_null()); +#endif + _aot_compatible_loaders->iterate_all([&](ArchivedClassLoaderData* &archived_cld, ClassLoaderData* &cld) { + archived_cld->init_archived_entries(cld); + }); _archived_javabase_moduleEntry = ModuleEntry::get_archived_entry(ModuleEntryTable::javabase_moduleEntry()); @@ -237,10 +321,33 @@ void ClassLoaderDataShared::init_archived_tables() { _system_loader_root_index = HeapShared::append_root(SystemDictionary::java_system_loader()); } +void ClassLoaderDataShared::write_cld_table() { + CompactHashtableStats stats; + _archived_cld_map.reset(); + CompactHashtableWriter writer(_aot_compatible_loaders->number_of_entries(), &stats); + _aot_compatible_loaders->iterate_all([&](ArchivedClassLoaderData* &archived_cld, ClassLoaderData* &cld) { + ResourceMark rm; + Symbol* aot_id = cld->aot_identity(); + assert(aot_id != nullptr, "sanity check"); + ArchiveBuilder* builder = ArchiveBuilder::current(); + assert(builder->get_buffered_addr(aot_id) != nullptr, "Symbol %s is not in the buffer", aot_id->as_C_string()); + unsigned int hash = Symbol::symbol_hash(aot_id); + u4 delta = builder->buffer_to_offset_u4((address)archived_cld); + writer.add(hash, delta); + if (log_is_enabled(Trace, aot, hashtables)) { + log_trace(aot, hashtables)("archived cld dictionary: %s", aot_id->as_C_string()); + } + }); + writer.dump(&_archived_cld_map, "aot_cld_map dictionary"); +} + void ClassLoaderDataShared::serialize(SerializeClosure* f) { +#if 0 _archived_boot_loader_data.serialize(f); _archived_platform_loader_data.serialize(f); _archived_system_loader_data.serialize(f); +#endif + _archived_cld_map.serialize_header(f); f->do_ptr(&_archived_javabase_moduleEntry); f->do_int(&_platform_loader_root_index); f->do_int(&_system_loader_root_index); @@ -248,7 +355,11 @@ void ClassLoaderDataShared::serialize(SerializeClosure* f) { ModuleEntry* ClassLoaderDataShared::archived_boot_unnamed_module() { if (CDSConfig::is_using_full_module_graph()) { - return _archived_boot_loader_data.unnamed_module(); + Symbol* boot_loader_aot_identity = SymbolTable::new_symbol("BOOT"); + unsigned int hash = Symbol::symbol_hash(boot_loader_aot_identity); + ArchivedClassLoaderData* archived_cld = _archived_cld_map.lookup(boot_loader_aot_identity, hash, 0); + assert(archived_cld != nullptr, "sanity check"); + return archived_cld->unnamed_module(); } else { return nullptr; } @@ -257,26 +368,38 @@ ModuleEntry* ClassLoaderDataShared::archived_boot_unnamed_module() { ModuleEntry* ClassLoaderDataShared::archived_unnamed_module(ClassLoaderData* loader_data) { ModuleEntry* archived_module = nullptr; - if (!Universe::is_module_initialized() && CDSConfig::is_using_full_module_graph()) { + //if (!Universe::is_module_initialized() && CDSConfig::is_using_full_module_graph()) { + if (CDSConfig::is_using_full_module_graph() && loader_data->aot_identity() != nullptr) { precond(_platform_loader_root_index >= 0); precond(_system_loader_root_index >= 0); +#if 0 if (loader_data->class_loader() == HeapShared::get_root(_platform_loader_root_index)) { archived_module = _archived_platform_loader_data.unnamed_module(); } else if (loader_data->class_loader() == HeapShared::get_root(_system_loader_root_index)) { archived_module = _archived_system_loader_data.unnamed_module(); } +#endif + unsigned int hash = Symbol::symbol_hash(loader_data->aot_identity()); + ArchivedClassLoaderData* archived_cld = _archived_cld_map.lookup(loader_data->aot_identity(), hash, 0); + if (archived_cld != nullptr) { + archived_module = archived_cld->unnamed_module(); + } } - return archived_module; } void ClassLoaderDataShared::clear_archived_oops() { assert(!CDSConfig::is_using_full_module_graph(), "must be"); +#if 0 _archived_boot_loader_data.clear_archived_oops(); _archived_platform_loader_data.clear_archived_oops(); _archived_system_loader_data.clear_archived_oops(); +#endif + _archived_cld_map.iterate([&](ArchivedClassLoaderData* archived_cld) { + archived_cld->clear_archived_oops(); + }); if (_platform_loader_root_index >= 0) { HeapShared::clear_root(_platform_loader_root_index); HeapShared::clear_root(_system_loader_root_index); @@ -286,7 +409,10 @@ void ClassLoaderDataShared::clear_archived_oops() { // Must be done before ClassLoader::create_javabase() void ClassLoaderDataShared::restore_archived_entries_for_null_class_loader_data() { precond(CDSConfig::is_using_full_module_graph()); - _archived_boot_loader_data.restore(null_class_loader_data(), true, false); + unsigned int hash = Symbol::symbol_hash(null_class_loader_data()->aot_identity()); + ArchivedClassLoaderData* archived_cld = _archived_cld_map.lookup(null_class_loader_data()->aot_identity(), hash, 0); + archived_cld->restore(null_class_loader_data(), true, false); + //_archived_boot_loader_data.restore(null_class_loader_data(), true, false); ModuleEntryTable::set_javabase_moduleEntry(_archived_javabase_moduleEntry); aot_log_info(aot)("use_full_module_graph = true; java.base = " INTPTR_FORMAT, p2i(_archived_javabase_moduleEntry)); @@ -294,19 +420,33 @@ void ClassLoaderDataShared::restore_archived_entries_for_null_class_loader_data( oop ClassLoaderDataShared::restore_archived_oops_for_null_class_loader_data() { assert(CDSConfig::is_using_full_module_graph(), "must be"); - _archived_boot_loader_data.restore(null_class_loader_data(), false, true); + unsigned int hash = Symbol::symbol_hash(null_class_loader_data()->aot_identity()); + ArchivedClassLoaderData* archived_cld = _archived_cld_map.lookup(null_class_loader_data()->aot_identity(), hash, 0); + archived_cld->restore(null_class_loader_data(), false, true); + //_archived_boot_loader_data.restore(null_class_loader_data(), false, true); + null_class_loader_data()->set_restored(true); return _archived_javabase_moduleEntry->module_oop(); } void ClassLoaderDataShared::restore_java_platform_loader_from_archive(ClassLoaderData* loader_data) { assert(CDSConfig::is_using_full_module_graph(), "must be"); - _archived_platform_loader_data.restore(loader_data, true, true); + unsigned int hash = Symbol::symbol_hash(loader_data->aot_identity()); + ArchivedClassLoaderData* archived_cld = _archived_cld_map.lookup(loader_data->aot_identity(), hash, 0); + assert(archived_cld != nullptr, "ArchivedClassLoaderData for platform loader not found"); + archived_cld->restore(loader_data, true, true); + //_archived_platform_loader_data.restore(loader_data, true, true); + loader_data->set_restored(true); } void ClassLoaderDataShared::restore_java_system_loader_from_archive(ClassLoaderData* loader_data) { assert(CDSConfig::is_using_full_module_graph(), "must be"); - _archived_system_loader_data.restore(loader_data, true, true); + unsigned int hash = Symbol::symbol_hash(loader_data->aot_identity()); + ArchivedClassLoaderData* archived_cld = _archived_cld_map.lookup(loader_data->aot_identity(), hash, 0); + assert(archived_cld != nullptr, "ArchivedClassLoaderData for system loader not found"); + archived_cld->restore(loader_data, true, true); + //_archived_system_loader_data.restore(loader_data, true, true); _full_module_graph_loaded = true; + loader_data->set_restored(true); } // This is called before AOTLinkedClassBulkLoader starts preloading classes. It makes sure that @@ -322,4 +462,20 @@ void ClassLoaderDataShared::restore_archived_modules_for_preloading_classes(Java Modules::init_archived_modules(current, h_platform_loader, h_system_loader); } +void ClassLoaderDataShared::restore_archived_data(ClassLoaderData* loader_data) { + //if already restored, nothing to do + if (loader_data->restored()) { + return; + } + ResourceMark rm; + assert(CDSConfig::is_using_full_module_graph(), "must be"); + Symbol* aot_id = loader_data->aot_identity(); + assert(aot_id != nullptr, "sanity check"); + unsigned int hash = Symbol::symbol_hash(aot_id); + ArchivedClassLoaderData* archived_cld = _archived_cld_map.lookup(aot_id, hash, 0); + assert(archived_cld != nullptr, "ArchivedClassLoaderData for loader with aot_id=%s not found", aot_id->as_C_string()); + archived_cld->restore(loader_data, true, true); + loader_data->set_restored(true); +} + #endif // INCLUDE_CDS_JAVA_HEAP diff --git a/src/hotspot/share/classfile/classLoaderDataShared.hpp b/src/hotspot/share/classfile/classLoaderDataShared.hpp index 39d0a89418f..7316de105b4 100644 --- a/src/hotspot/share/classfile/classLoaderDataShared.hpp +++ b/src/hotspot/share/classfile/classLoaderDataShared.hpp @@ -38,7 +38,7 @@ class ClassLoaderDataShared : AllStatic { static bool _full_module_graph_loaded; CDS_JAVA_HEAP_ONLY(static void ensure_module_entry_table_exists(oop class_loader);) public: - static void load_archived_platform_and_system_class_loaders() NOT_CDS_JAVA_HEAP_RETURN; + static void load_archived_platform_and_system_class_loaders(Handle h_platform_loader, Handle h_system_loader) NOT_CDS_JAVA_HEAP_RETURN; static void restore_archived_modules_for_preloading_classes(JavaThread* current) NOT_CDS_JAVA_HEAP_RETURN; #if INCLUDE_CDS_JAVA_HEAP static void ensure_module_entry_tables_exist(); @@ -55,6 +55,8 @@ class ClassLoaderDataShared : AllStatic { static ModuleEntry* archived_unnamed_module(ClassLoaderData* loader_data); #endif // INCLUDE_CDS_JAVA_HEAP static bool is_full_module_graph_loaded() { return _full_module_graph_loaded; } + static void write_cld_table(); + static void restore_archived_data(ClassLoaderData* loader_data); }; #endif // SHARE_CLASSFILE_CLASSLOADERDATASHARED_HPP diff --git a/src/hotspot/share/classfile/javaClasses.cpp b/src/hotspot/share/classfile/javaClasses.cpp index 879a990ec10..6ce3f727076 100644 --- a/src/hotspot/share/classfile/javaClasses.cpp +++ b/src/hotspot/share/classfile/javaClasses.cpp @@ -1195,7 +1195,8 @@ void java_lang_Class::create_mirror(Klass* k, Handle class_loader, // latter may contain dumptime-specific information that cannot be archived // (e.g., ClassLoaderData*, or static fields that are modified by Java code execution). void java_lang_Class::create_scratch_mirror(Klass* k, TRAPS) { - if (k->class_loader() != nullptr && + if (k->cl_aot_identity() == nullptr && + k->class_loader() != nullptr && k->class_loader() != SystemDictionary::java_platform_loader() && k->class_loader() != SystemDictionary::java_system_loader()) { // We only archive the mirrors of classes loaded by the built-in loaders @@ -4790,6 +4791,7 @@ int java_lang_ClassLoader::_name_offset; int java_lang_ClassLoader::_nameAndId_offset; int java_lang_ClassLoader::_unnamedModule_offset; int java_lang_ClassLoader::_parent_offset; +int java_lang_ClassLoader::_aotIdentity_offset; ClassLoaderData* java_lang_ClassLoader::loader_data_acquire(oop loader) { assert(loader != nullptr, "loader must not be null"); @@ -4814,7 +4816,8 @@ void java_lang_ClassLoader::release_set_loader_data(oop loader, ClassLoaderData* macro(_name_offset, k1, vmSymbols::name_name(), string_signature, false); \ macro(_nameAndId_offset, k1, "nameAndId", string_signature, false); \ macro(_unnamedModule_offset, k1, "unnamedModule", module_signature, false); \ - macro(_parent_offset, k1, "parent", classloader_signature, false) + macro(_parent_offset, k1, "parent", classloader_signature, false); \ + macro(_aotIdentity_offset, k1, "aotIdentity", string_signature, false); void java_lang_ClassLoader::compute_offsets() { InstanceKlass* k1 = vmClasses::ClassLoader_klass(); @@ -4858,6 +4861,11 @@ oop java_lang_ClassLoader::nameAndId(oop loader) { return loader->obj_field(_nameAndId_offset); } +oop java_lang_ClassLoader::aotIdentity(oop loader) { + assert(is_instance(loader), "loader must be oop"); + return loader->obj_field(_aotIdentity_offset); +} + bool java_lang_ClassLoader::isAncestor(oop loader, oop cl) { assert(is_instance(loader), "loader must be oop"); assert(cl == nullptr || is_instance(cl), "cl argument must be oop"); diff --git a/src/hotspot/share/classfile/javaClasses.hpp b/src/hotspot/share/classfile/javaClasses.hpp index cc9fe0c1f89..625a7b24af0 100644 --- a/src/hotspot/share/classfile/javaClasses.hpp +++ b/src/hotspot/share/classfile/javaClasses.hpp @@ -1499,6 +1499,7 @@ class java_lang_ClassLoader : AllStatic { static int _name_offset; static int _nameAndId_offset; static int _unnamedModule_offset; + static int _aotIdentity_offset; static void compute_offsets(); @@ -1515,6 +1516,7 @@ class java_lang_ClassLoader : AllStatic { static oop parent_no_keepalive(oop loader); static oop name(oop loader); static oop nameAndId(oop loader); + static oop aotIdentity(oop loader); static bool isAncestor(oop loader, oop cl); // Support for parallelCapable field diff --git a/src/hotspot/share/classfile/moduleEntry.cpp b/src/hotspot/share/classfile/moduleEntry.cpp index 3ab9ac73ce6..344f794366b 100644 --- a/src/hotspot/share/classfile/moduleEntry.cpp +++ b/src/hotspot/share/classfile/moduleEntry.cpp @@ -417,7 +417,8 @@ static int _num_inited_module_entries = 0; #endif bool ModuleEntry::should_be_archived() const { - return SystemDictionaryShared::is_builtin_loader(loader_data()); + //return SystemDictionaryShared::is_builtin_loader(loader_data()); + return loader_data()->aot_identity() != nullptr; } ModuleEntry* ModuleEntry::allocate_archived_entry() const { diff --git a/src/hotspot/share/classfile/modules.cpp b/src/hotspot/share/classfile/modules.cpp index 3029c8fb936..aa18423f345 100644 --- a/src/hotspot/share/classfile/modules.cpp +++ b/src/hotspot/share/classfile/modules.cpp @@ -505,7 +505,8 @@ void Modules::check_archived_module_oop(oop orig_module_obj) { // We only archive the default module graph, which should contain only java.lang.Module oops // for the 3 built-in loaders (boot/platform/system) ClassLoaderData* loader_data = orig_module_ent->loader_data(); - assert(loader_data->is_builtin_class_loader_data(), "must be"); + //assert(loader_data->is_builtin_class_loader_data(), "must be"); + assert(loader_data->aot_identity() != nullptr, "must be"); if (orig_module_ent->name() != nullptr) { // For each named module, we archive both the java.lang.Module oop and the ModuleEntry. @@ -525,7 +526,7 @@ void Modules::check_archived_module_oop(oop orig_module_obj) { assert(!_seen_system_unnamed_module, "only once"); _seen_system_unnamed_module = true; } else { - ShouldNotReachHere(); + //ShouldNotReachHere(); } } } @@ -741,7 +742,7 @@ void Modules::init_archived_modules(JavaThread* current, Handle h_platform_loade ModuleEntryTable::patch_javabase_entries(current, java_base_module); } - ClassLoaderDataShared::load_archived_platform_and_system_class_loaders(); + ClassLoaderDataShared::load_archived_platform_and_system_class_loaders(h_platform_loader, h_system_loader); ClassLoaderData* platform_loader_data = SystemDictionary::register_loader(h_platform_loader); SystemDictionary::set_platform_loader(platform_loader_data); diff --git a/src/hotspot/share/classfile/systemDictionary.cpp b/src/hotspot/share/classfile/systemDictionary.cpp index 2e57c0284f5..f3e58cd0620 100644 --- a/src/hotspot/share/classfile/systemDictionary.cpp +++ b/src/hotspot/share/classfile/systemDictionary.cpp @@ -1290,7 +1290,7 @@ InstanceKlass* SystemDictionary::load_instance_class_impl(Symbol* class_name, Ha if (CDSConfig::is_using_archive()) { PerfTraceElapsedTime vmtimer(ClassLoader::perf_shared_classload_time()); - InstanceKlass* ik = SystemDictionaryShared::find_builtin_class(class_name); + InstanceKlass* ik = SystemDictionaryShared::find_class_in_aot_compatible_dictionary(class_name); if (ik != nullptr && ik->defined_by_boot_loader() && !ik->shared_loading_failed()) { SharedClassLoadingMark slm(THREAD, ik); k = load_shared_class(ik, class_loader, Handle(), nullptr, pkg_entry, CHECK_NULL); diff --git a/src/hotspot/share/classfile/systemDictionaryShared.cpp b/src/hotspot/share/classfile/systemDictionaryShared.cpp index fc3b5c14839..1bfeb9e8d45 100644 --- a/src/hotspot/share/classfile/systemDictionaryShared.cpp +++ b/src/hotspot/share/classfile/systemDictionaryShared.cpp @@ -45,6 +45,7 @@ #include "classfile/classLoader.hpp" #include "classfile/classLoaderData.inline.hpp" #include "classfile/classLoaderDataGraph.hpp" +#include "classfile/classLoaderDataShared.hpp" #include "classfile/dictionary.hpp" #include "classfile/javaClasses.inline.hpp" #include "classfile/symbolTable.hpp" @@ -98,14 +99,16 @@ static void check_klass_after_loading(const Klass* k) { } #endif -InstanceKlass* SystemDictionaryShared::load_shared_class_for_builtin_loader( +InstanceKlass* SystemDictionaryShared::load_shared_class_for_aot_compatible_loader( Symbol* class_name, Handle class_loader, TRAPS) { assert(CDSConfig::is_using_archive(), "must be"); - InstanceKlass* ik = find_builtin_class(class_name); + InstanceKlass* ik = find_class_in_aot_compatible_dictionary(class_name); if (ik != nullptr && !ik->shared_loading_failed()) { - if ((SystemDictionary::is_system_class_loader(class_loader()) && ik->defined_by_app_loader()) || - (SystemDictionary::is_platform_class_loader(class_loader()) && ik->defined_by_platform_loader())) { + oop aot_identity = java_lang_ClassLoader::aotIdentity(class_loader()); + assert(aot_identity != nullptr, "must be"); + Symbol* aot_identity_sym = java_lang_String::as_symbol(aot_identity); + if (aot_identity_sym->equals(ik->cl_aot_identity())) { SharedClassLoadingMark slm(THREAD, ik); PackageEntry* pkg_entry = CDSProtectionDomain::get_package_entry_from_class(ik, class_loader); Handle protection_domain; @@ -138,8 +141,8 @@ InstanceKlass* SystemDictionaryShared::lookup_from_stream(Symbol* class_name, return nullptr; } - const RunTimeClassInfo* record = find_record(&_static_archive._unregistered_dictionary, - &_dynamic_archive._unregistered_dictionary, + const RunTimeClassInfo* record = find_record(&_static_archive._aot_incompatible_loader_dict, + &_dynamic_archive._aot_incompatible_loader_dict, class_name); if (record == nullptr) { return nullptr; @@ -628,15 +631,17 @@ InstanceKlass* SystemDictionaryShared::find_or_load_shared_class( return nullptr; } - if (SystemDictionary::is_system_class_loader(class_loader()) || - SystemDictionary::is_platform_class_loader(class_loader())) { + if (java_lang_ClassLoader::aotIdentity(class_loader()) != nullptr) { ClassLoaderData *loader_data = register_loader(class_loader); + if (CDSConfig::is_using_full_module_graph()) { + ClassLoaderDataShared::restore_archived_data(loader_data); + } Dictionary* dictionary = loader_data->dictionary(); // Note: currently, find_or_load_shared_class is called only from // JVM_FindLoadedClass and used for PlatformClassLoader and AppClassLoader, // which are parallel-capable loaders, so a lock here is NOT taken. - assert(get_loader_lock_or_null(class_loader) == nullptr, "ObjectLocker not required"); + //assert(get_loader_lock_or_null(class_loader) == nullptr, "ObjectLocker not required"); { MutexLocker mu(THREAD, SystemDictionary_lock); InstanceKlass* check = dictionary->find_class(THREAD, name); @@ -645,7 +650,7 @@ InstanceKlass* SystemDictionaryShared::find_or_load_shared_class( } } - k = load_shared_class_for_builtin_loader(name, class_loader, THREAD); + k = load_shared_class_for_aot_compatible_loader(name, class_loader, THREAD); if (k != nullptr) { SharedClassLoadingMark slm(THREAD, k); k = find_or_define_instance_class(name, class_loader, k, CHECK_NULL); @@ -698,7 +703,7 @@ void SystemDictionaryShared::copy_unregistered_class_size_and_crc32(InstanceKlas precond(klass->in_aot_cache()); // A shared class must have a RunTimeClassInfo record - const RunTimeClassInfo* record = find_record(&_static_archive._unregistered_dictionary, + const RunTimeClassInfo* record = find_record(&_static_archive._aot_incompatible_loader_dict, nullptr, klass->name()); precond(record != nullptr); precond(record->klass() == klass); @@ -1314,15 +1319,15 @@ unsigned int SystemDictionaryShared::hash_for_shared_dictionary(address ptr) { class CopySharedClassInfoToArchive : StackObj { CompactHashtableWriter* _writer; - bool _is_builtin; + bool _is_aot_compatible_loader; ArchiveBuilder *_builder; public: CopySharedClassInfoToArchive(CompactHashtableWriter* writer, - bool is_builtin) - : _writer(writer), _is_builtin(is_builtin), _builder(ArchiveBuilder::current()) {} + bool is_aot_compatible_loader) + : _writer(writer), _is_aot_compatible_loader(is_aot_compatible_loader), _builder(ArchiveBuilder::current()) {} void do_entry(InstanceKlass* k, DumpTimeClassInfo& info) { - if (!info.is_excluded() && info.is_builtin() == _is_builtin) { + if (!info.is_excluded() && k->is_aot_compatible_loader() == _is_aot_compatible_loader) { size_t byte_size = info.runtime_info_bytesize(); RunTimeClassInfo* record; record = (RunTimeClassInfo*)ArchiveBuilder::ro_region_alloc(byte_size); @@ -1333,14 +1338,14 @@ class CopySharedClassInfoToArchive : StackObj { name = ArchiveBuilder::current()->get_buffered_addr(name); hash = SystemDictionaryShared::hash_for_shared_dictionary((address)name); u4 delta = _builder->buffer_to_offset_u4((address)record); - if (_is_builtin && info._klass->is_hidden()) { + if (_is_aot_compatible_loader && info._klass->is_hidden()) { // skip } else { _writer->add(hash, delta); } if (log_is_enabled(Trace, aot, hashtables)) { ResourceMark rm; - log_trace(aot, hashtables)("%s dictionary: %s", (_is_builtin ? "builtin" : "unregistered"), info._klass->external_name()); + log_trace(aot, hashtables)("%s dictionary: %s", (_is_aot_compatible_loader ? "aot_compatible_loader" : "aot_incompatible_loader"), info._klass->external_name()); } // Save this for quick runtime lookup of InstanceKlass* -> RunTimeClassInfo* @@ -1351,21 +1356,21 @@ class CopySharedClassInfoToArchive : StackObj { }; void SystemDictionaryShared::write_dictionary(RunTimeSharedDictionary* dictionary, - bool is_builtin) { + bool is_aot_compatible_loader) { CompactHashtableStats stats; dictionary->reset(); - CompactHashtableWriter writer(_dumptime_table->count_of(is_builtin), &stats); - CopySharedClassInfoToArchive copy(&writer, is_builtin); + CompactHashtableWriter writer(_dumptime_table->count_of(is_aot_compatible_loader), &stats); + CopySharedClassInfoToArchive copy(&writer, is_aot_compatible_loader); assert_lock_strong(DumpTimeTable_lock); _dumptime_table->iterate_all_live_classes(©); - writer.dump(dictionary, is_builtin ? "builtin dictionary" : "unregistered dictionary"); + writer.dump(dictionary, is_aot_compatible_loader? "aot_compatible_loader dictionary" : "aot_incompatible_loader dictionary"); } void SystemDictionaryShared::write_to_archive(bool is_static_archive) { ArchiveInfo* archive = get_archive(is_static_archive); - write_dictionary(&archive->_builtin_dictionary, true); - write_dictionary(&archive->_unregistered_dictionary, false); + write_dictionary(&archive->_aot_compatible_loader_dict, true); + write_dictionary(&archive->_aot_incompatible_loader_dict, false); if (CDSConfig::is_dumping_lambdas_in_legacy_mode()) { LambdaProxyClassDictionary::write_dictionary(is_static_archive); } else { @@ -1377,8 +1382,8 @@ 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->_aot_compatible_loader_dict.serialize_header(soc); + archive->_aot_incompatible_loader_dict.serialize_header(soc); LambdaProxyClassDictionary::serialize(soc, is_static_archive); } @@ -1421,9 +1426,9 @@ SystemDictionaryShared::find_record(RunTimeSharedDictionary* static_dict, RunTim return record; } -InstanceKlass* SystemDictionaryShared::find_builtin_class(Symbol* name) { - const RunTimeClassInfo* record = find_record(&_static_archive._builtin_dictionary, - &_dynamic_archive._builtin_dictionary, +InstanceKlass* SystemDictionaryShared::find_class_in_aot_compatible_dictionary(Symbol* name) { + const RunTimeClassInfo* record = find_record(&_static_archive._aot_compatible_loader_dict, + &_dynamic_archive._aot_compatible_loader_dict, name); if (record != nullptr) { assert(!record->klass()->is_hidden(), "hidden class cannot be looked up by name"); @@ -1464,11 +1469,11 @@ const char* SystemDictionaryShared::loader_type_for_shared_class(Klass* k) { } void SystemDictionaryShared::get_all_archived_classes(bool is_static_archive, GrowableArray* classes) { - get_archive(is_static_archive)->_builtin_dictionary.iterate([&] (const RunTimeClassInfo* record) { + get_archive(is_static_archive)->_aot_compatible_loader_dict.iterate([&] (const RunTimeClassInfo* record) { classes->append(record->klass()); }); - get_archive(is_static_archive)->_unregistered_dictionary.iterate([&] (const RunTimeClassInfo* record) { + get_archive(is_static_archive)->_aot_incompatible_loader_dict.iterate([&] (const RunTimeClassInfo* record) { classes->append(record->klass()); }); } @@ -1496,10 +1501,10 @@ void SystemDictionaryShared::ArchiveInfo::print_on(const char* prefix, bool is_static_archive) { st->print_cr("%sShared Dictionary", prefix); SharedDictionaryPrinter p(st); - st->print_cr("%sShared Builtin Dictionary", prefix); - _builtin_dictionary.iterate(&p); - st->print_cr("%sShared Unregistered Dictionary", prefix); - _unregistered_dictionary.iterate(&p); + st->print_cr("%sShared AOT Compatible Loaders Dictionary", prefix); + _aot_compatible_loader_dict.iterate(&p); + st->print_cr("%sShared AOT Incompatible Loaders Dictionary", prefix); + _aot_incompatible_loader_dict.iterate(&p); LambdaProxyClassDictionary::print_on(prefix, st, p.index(), is_static_archive); } @@ -1507,8 +1512,8 @@ void SystemDictionaryShared::ArchiveInfo::print_table_statistics(const char* pre outputStream* st, bool is_static_archive) { st->print_cr("%sArchve Statistics", prefix); - _builtin_dictionary.print_table_statistics(st, "Builtin Shared Dictionary"); - _unregistered_dictionary.print_table_statistics(st, "Unregistered Shared Dictionary"); + _aot_compatible_loader_dict.print_table_statistics(st, "AOT Compatible Loaders Dictionary"); + _aot_incompatible_loader_dict.print_table_statistics(st, "AOT Incompatible Loaders Dictionary"); LambdaProxyClassDictionary::print_statistics(st, is_static_archive); } diff --git a/src/hotspot/share/classfile/systemDictionaryShared.hpp b/src/hotspot/share/classfile/systemDictionaryShared.hpp index 5c42e0fa96c..89d14a049c2 100644 --- a/src/hotspot/share/classfile/systemDictionaryShared.hpp +++ b/src/hotspot/share/classfile/systemDictionaryShared.hpp @@ -139,8 +139,10 @@ class SystemDictionaryShared: public SystemDictionary { friend class LambdaProxyClassDictionary; struct ArchiveInfo { - RunTimeSharedDictionary _builtin_dictionary; - RunTimeSharedDictionary _unregistered_dictionary; + //RunTimeSharedDictionary _builtin_dictionary; + //RunTimeSharedDictionary _unregistered_dictionary; + RunTimeSharedDictionary _aot_compatible_loader_dict; + RunTimeSharedDictionary _aot_incompatible_loader_dict; void print_on(const char* prefix, outputStream* st, bool is_static_archive); void print_table_statistics(const char* prefix, outputStream* st, bool is_static_archive); @@ -158,7 +160,7 @@ class SystemDictionaryShared: public SystemDictionary { return is_static_archive ? &_static_archive : &_dynamic_archive; } - static InstanceKlass* load_shared_class_for_builtin_loader( + static InstanceKlass* load_shared_class_for_aot_compatible_loader( Symbol* class_name, Handle class_loader, TRAPS); @@ -209,7 +211,7 @@ class SystemDictionaryShared: public SystemDictionary { static bool has_archived_enum_objs(InstanceKlass* ik); static void set_has_archived_enum_objs(InstanceKlass* ik); - static InstanceKlass* find_builtin_class(Symbol* class_name); + static InstanceKlass* find_class_in_aot_compatible_dictionary(Symbol* class_name); static const RunTimeClassInfo* find_record(RunTimeSharedDictionary* static_dict, RunTimeSharedDictionary* dynamic_dict, diff --git a/src/hotspot/share/classfile/vmSymbols.hpp b/src/hotspot/share/classfile/vmSymbols.hpp index 5f01026161f..cb97d479f8a 100644 --- a/src/hotspot/share/classfile/vmSymbols.hpp +++ b/src/hotspot/share/classfile/vmSymbols.hpp @@ -518,6 +518,10 @@ class SerializeClosure; template(objectWaiter_name, "objectWaiter") \ template(atKlassInit_name, "atKlassInit") \ template(hasArgsAtTop_name, "hasArgsAtTop") \ + template(getNamedPackage_name, "getNamedPackage") \ + template(getNamedPackage_signature, "(Ljava/lang/String;Ljava/lang/Module;)Ljava/lang/NamedPackage;") \ + template(getDefaultProtectionDomain_name, "getDefaultProtectionDomain") \ + template(getDefaultProtectionDomain_signature, "()Ljava/security/ProtectionDomain;") \ \ /* name symbols needed by intrinsics */ \ VM_INTRINSICS_DO(VM_INTRINSIC_IGNORE, VM_SYMBOL_IGNORE, template, VM_SYMBOL_IGNORE, VM_ALIAS_IGNORE) \ diff --git a/src/hotspot/share/include/jvm.h b/src/hotspot/share/include/jvm.h index 9b25b85e6c5..755cea9384c 100644 --- a/src/hotspot/share/include/jvm.h +++ b/src/hotspot/share/include/jvm.h @@ -756,6 +756,9 @@ JVM_DesiredAssertionStatus(JNIEnv *env, jclass unused, jclass cls); JNIEXPORT jobject JNICALL JVM_AssertionStatusDirectives(JNIEnv *env, jclass unused); +JNIEXPORT void JNICALL +JVM_RegisterAsAOTCompatibleLoader(JNIEnv *env, jobject loader); + /* * java.lang.ref.Finalizer */ diff --git a/src/hotspot/share/memory/universe.cpp b/src/hotspot/share/memory/universe.cpp index 1e4bdec1478..5031b52312b 100644 --- a/src/hotspot/share/memory/universe.cpp +++ b/src/hotspot/share/memory/universe.cpp @@ -932,6 +932,9 @@ jint universe_init() { } #endif + // Create symbol table before initializing bootloader which needs symbol table for creating AOT identity + SymbolTable::create_table(); + ClassLoaderData::init_null_class_loader_data(); #if INCLUDE_CDS @@ -945,8 +948,6 @@ jint universe_init() { } #endif - SymbolTable::create_table(); - if (strlen(VerifySubSet) > 0) { Universe::initialize_verify_flags(); } diff --git a/src/hotspot/share/oops/constantPool.cpp b/src/hotspot/share/oops/constantPool.cpp index 012a2ba0db6..59f5b58e4a3 100644 --- a/src/hotspot/share/oops/constantPool.cpp +++ b/src/hotspot/share/oops/constantPool.cpp @@ -444,7 +444,7 @@ void ConstantPool::remove_unshareable_info() { if (CDSConfig::is_dumping_final_static_archive()) { ConstantPool* src_cp = ArchiveBuilder::current()->get_source_addr(this); InstanceKlass* src_holder = src_cp->pool_holder(); - if (src_holder->defined_by_other_loaders()) { + if (src_holder->defined_by_other_loaders() && src_holder->cl_aot_identity() == nullptr) { // Unregistered classes are not loaded in the AOT assembly phase. The resolved reference length // is already saved during the training run. precond(!src_holder->is_loaded()); diff --git a/src/hotspot/share/oops/instanceKlass.cpp b/src/hotspot/share/oops/instanceKlass.cpp index c7eb5399669..f62dfd63dcc 100644 --- a/src/hotspot/share/oops/instanceKlass.cpp +++ b/src/hotspot/share/oops/instanceKlass.cpp @@ -2859,7 +2859,7 @@ void InstanceKlass::init_shared_package_entry() { _package_entry = nullptr; #else if (CDSConfig::is_dumping_full_module_graph()) { - if (defined_by_other_loaders()) { + if (defined_by_other_loaders() && cl_aot_identity() == nullptr) { _package_entry = nullptr; } else { _package_entry = PackageEntry::get_archived_entry(_package_entry); @@ -4090,7 +4090,11 @@ void InstanceKlass::print_class_load_helper(ClassLoaderData* loader_data, info_stream.print(" loader:"); #if INCLUDE_CDS if (in_aot_cache()) { - info_stream.print(" %s", SystemDictionaryShared::loader_type_for_shared_class((Klass*)this)); + if (loader_data->aot_identity() != nullptr) { + info_stream.print(" %s", loader_data->aot_identity()->as_C_string()); + } else { + info_stream.print(" %s", SystemDictionaryShared::loader_type_for_shared_class((Klass*)this)); + } } else #endif if (loader_data == ClassLoaderData::the_null_class_loader_data()) { diff --git a/src/hotspot/share/oops/klass.cpp b/src/hotspot/share/oops/klass.cpp index 6b717ab442f..bdf3c2ff086 100644 --- a/src/hotspot/share/oops/klass.cpp +++ b/src/hotspot/share/oops/klass.cpp @@ -766,6 +766,7 @@ void Klass::metaspace_pointers_do(MetaspaceClosure* it) { } it->push(&_name); + it->push(&_cl_aot_identity); it->push(&_secondary_supers); for (int i = 0; i < _primary_super_limit; i++) { it->push(&_primary_supers[i]); diff --git a/src/hotspot/share/oops/klass.hpp b/src/hotspot/share/oops/klass.hpp index 4cbe11ca15f..c3b4c085ec1 100644 --- a/src/hotspot/share/oops/klass.hpp +++ b/src/hotspot/share/oops/klass.hpp @@ -25,6 +25,7 @@ #ifndef SHARE_OOPS_KLASS_HPP #define SHARE_OOPS_KLASS_HPP +#include "classfile/classLoaderData.hpp" #include "oops/klassFlags.hpp" #include "oops/markWord.hpp" #include "oops/metadata.hpp" @@ -136,6 +137,7 @@ class Klass : public Metadata { // Class name. Instance classes: java/lang/String, etc. Array classes: [I, // [Ljava/lang/String;, etc. Set to zero for all other kinds of classes. Symbol* _name; + Symbol* _cl_aot_identity; // Cache of last observed secondary supertype Klass* _secondary_super_cache; @@ -311,7 +313,15 @@ class Klass : public Metadata { // class loader data ClassLoaderData* class_loader_data() const { return _class_loader_data; } - void set_class_loader_data(ClassLoaderData* loader_data) { _class_loader_data = loader_data; } + void set_class_loader_data(ClassLoaderData* loader_data) { + _class_loader_data = loader_data; + if (loader_data != nullptr) { + _cl_aot_identity = loader_data->aot_identity(); + } + } + + Symbol* cl_aot_identity() const { return _cl_aot_identity; } + bool is_aot_compatible_loader() const { return _cl_aot_identity != nullptr; } s2 shared_classpath_index() const { return _shared_class_path_index; diff --git a/src/hotspot/share/oops/symbol.cpp b/src/hotspot/share/oops/symbol.cpp index 65a5015350c..80e15440bbf 100644 --- a/src/hotspot/share/oops/symbol.cpp +++ b/src/hotspot/share/oops/symbol.cpp @@ -417,3 +417,13 @@ bool Symbol::is_valid_id(vmSymbolID vm_symbol_id) { return vmSymbols::is_valid_id(vm_symbol_id); } #endif + +unsigned Symbol::symbol_hash(Symbol* const& sym) { + ResourceMark rm; + char* str = sym->as_C_string(); + return java_lang_String::hash_code(str, strlen(str)); +} + +bool Symbol::symbol_equals(Symbol* const& sym1, Symbol* const& sym2) { + return sym1->equals(sym2); +} diff --git a/src/hotspot/share/oops/symbol.hpp b/src/hotspot/share/oops/symbol.hpp index b73e2ff46cf..353dbe5ab53 100644 --- a/src/hotspot/share/oops/symbol.hpp +++ b/src/hotspot/share/oops/symbol.hpp @@ -197,6 +197,10 @@ class Symbol : public MetaspaceObj { int utf8_length() const { return length(); } + bool equals(const Symbol* sym) { + return equals((const char*)sym->bytes(), sym->utf8_length()); + } + // Compares the symbol with a string. bool equals(const char* str, int len) const { int l = utf8_length(); @@ -325,6 +329,9 @@ class Symbol : public MetaspaceObj { static size_t _total_count; #endif + + static unsigned symbol_hash(Symbol* const& sym); + static bool symbol_equals(Symbol* const& sym1, Symbol* const& sym2); }; // Note: this comparison is used for vtable sorting only; it doesn't matter diff --git a/src/hotspot/share/prims/jvm.cpp b/src/hotspot/share/prims/jvm.cpp index a61808febd8..487b0a53f81 100644 --- a/src/hotspot/share/prims/jvm.cpp +++ b/src/hotspot/share/prims/jvm.cpp @@ -29,6 +29,7 @@ #include "cds/classListParser.hpp" #include "cds/classListWriter.hpp" #include "cds/dynamicArchive.hpp" +#include "cds/finalImageRecipes.hpp" #include "cds/heapShared.hpp" #include "cds/lambdaFormInvokers.hpp" #include "cds/lambdaProxyClassDictionary.hpp" @@ -2282,6 +2283,10 @@ JVM_ENTRY_PROF(jobject, JVM_AssertionStatusDirectives, JVM_AssertionStatusDirect return JNIHandles::make_local(THREAD, asd); JVM_END +JVM_ENTRY_PROF(void, JVM_RegisterAsAOTCompatibleLoader, JVM_RegisterAsAOTCompatibleLoader(JNIEnv *env, jobject loader)) + FinalImageRecipes::add_aot_compatible_loader(JNIHandles::resolve_non_null(loader), CHECK); +JVM_END + // Verification //////////////////////////////////////////////////////////////////////////////// // Reflection for the verifier ///////////////////////////////////////////////////////////////// diff --git a/src/java.base/share/classes/java/lang/ClassLoader.java b/src/java.base/share/classes/java/lang/ClassLoader.java index ad9bb9ffc54..b66243862eb 100644 --- a/src/java.base/share/classes/java/lang/ClassLoader.java +++ b/src/java.base/share/classes/java/lang/ClassLoader.java @@ -337,10 +337,16 @@ void addClass(Class c) { private final ConcurrentHashMap packages = new ConcurrentHashMap<>(); - /* + /** * Returns a named package for the given module. + * + * @param pn + * package name + * @param m + * module + * @return NamedPackage */ - private NamedPackage getNamedPackage(String pn, Module m) { + protected NamedPackage getNamedPackage(String pn, Module m) { NamedPackage p = packages.get(pn); if (p == null) { p = new NamedPackage(pn, m); @@ -2651,6 +2657,25 @@ private void resetArchivedStates() { classLoaderValueMap = null; libraries.clear(); } + + ProtectionDomain getDefaultProtectionDomain() { + return defaultDomain; + } + + private String aotIdentity = null; + + /** + * Set unique and repeatable ID of the classloader + * + * @param id + * unique id of the classloader object. + */ + protected void setAOTIdentity(String id) { + aotIdentity = id; + registerAsAOTCompatibleLoader(); + } + + private native void registerAsAOTCompatibleLoader(); } /* diff --git a/src/java.base/share/classes/java/lang/NamedPackage.java b/src/java.base/share/classes/java/lang/NamedPackage.java index 6234b949e65..91756bc33c5 100644 --- a/src/java.base/share/classes/java/lang/NamedPackage.java +++ b/src/java.base/share/classes/java/lang/NamedPackage.java @@ -40,7 +40,7 @@ * packages with minimal footprint and avoid constructing Package * object. */ -class NamedPackage { +public class NamedPackage { private final String name; private final Module module; diff --git a/src/java.base/share/classes/jdk/internal/loader/ClassLoaders.java b/src/java.base/share/classes/jdk/internal/loader/ClassLoaders.java index 1a61d4b1522..1f12b6d69ee 100644 --- a/src/java.base/share/classes/jdk/internal/loader/ClassLoaders.java +++ b/src/java.base/share/classes/jdk/internal/loader/ClassLoaders.java @@ -131,6 +131,7 @@ public static ClassLoader appClassLoader() { private static class BootClassLoader extends BuiltinClassLoader { BootClassLoader(URLClassPath bcp) { super(null, null, bcp); + setAOTIdentity("BOOT"); } @Override @@ -151,6 +152,7 @@ private static class PlatformClassLoader extends BuiltinClassLoader { PlatformClassLoader(BootClassLoader parent) { super("platform", parent, null); + setAOTIdentity("PLATFORM"); } } @@ -166,6 +168,7 @@ private static class AppClassLoader extends BuiltinClassLoader { AppClassLoader(BuiltinClassLoader parent, URLClassPath ucp) { super("app", parent, ucp); + setAOTIdentity("APP"); } /** diff --git a/src/java.base/share/native/libjava/ClassLoader.c b/src/java.base/share/native/libjava/ClassLoader.c index 4519a4777f8..af07e5589c4 100644 --- a/src/java.base/share/native/libjava/ClassLoader.c +++ b/src/java.base/share/native/libjava/ClassLoader.c @@ -35,7 +35,8 @@ #include "jvm.h" static JNINativeMethod methods[] = { - {"retrieveDirectives", "()Ljava/lang/AssertionStatusDirectives;", (void *)&JVM_AssertionStatusDirectives} + {"retrieveDirectives", "()Ljava/lang/AssertionStatusDirectives;", (void *)&JVM_AssertionStatusDirectives}, + {"registerAsAOTCompatibleLoader", "()V", (void *)&JVM_RegisterAsAOTCompatibleLoader} }; JNIEXPORT void JNICALL From 78a9d39a756099a80e58ab6f592dcbc2dc966afe Mon Sep 17 00:00:00 2001 From: Ashutosh Mehra Date: Mon, 19 Jan 2026 22:58:20 -0500 Subject: [PATCH 02/30] Handle aot safe custom loaders separate from builtin loader Signed-off-by: Ashutosh Mehra --- src/hotspot/share/cds/aotMappedHeapWriter.cpp | 7 +- src/hotspot/share/cds/cdsConfig.cpp | 13 ++ src/hotspot/share/cds/cdsConfig.hpp | 5 + src/hotspot/share/cds/cdsProtectionDomain.cpp | 1 + src/hotspot/share/cds/cds_globals.hpp | 4 + src/hotspot/share/cds/dumpTimeClassInfo.cpp | 3 + src/hotspot/share/cds/dumpTimeClassInfo.hpp | 7 +- src/hotspot/share/cds/filemap.cpp | 5 + src/hotspot/share/cds/filemap.hpp | 2 + src/hotspot/share/cds/finalImageRecipes.cpp | 57 +++--- src/hotspot/share/cds/finalImageRecipes.hpp | 2 +- src/hotspot/share/cds/heapShared.cpp | 29 ++- .../share/classfile/classLoaderData.cpp | 7 +- .../share/classfile/classLoaderDataShared.cpp | 169 ++++++++-------- .../share/classfile/classLoaderDataShared.hpp | 4 +- src/hotspot/share/classfile/moduleEntry.cpp | 3 +- src/hotspot/share/classfile/modules.cpp | 5 +- .../share/classfile/systemDictionary.cpp | 7 +- .../classfile/systemDictionaryShared.cpp | 186 ++++++++++++------ .../classfile/systemDictionaryShared.hpp | 18 +- src/hotspot/share/oops/klass.hpp | 5 +- src/hotspot/share/prims/jvm.cpp | 2 +- .../share/classes/java/lang/ClassLoader.java | 4 +- .../jdk/internal/loader/ClassLoaders.java | 3 - 24 files changed, 331 insertions(+), 217 deletions(-) diff --git a/src/hotspot/share/cds/aotMappedHeapWriter.cpp b/src/hotspot/share/cds/aotMappedHeapWriter.cpp index b6afd545682..644a1d02eab 100644 --- a/src/hotspot/share/cds/aotMappedHeapWriter.cpp +++ b/src/hotspot/share/cds/aotMappedHeapWriter.cpp @@ -549,14 +549,11 @@ size_t AOTMappedHeapWriter::copy_one_source_obj_to_buffer(oop src_obj) { update_buffered_object_field(to, java_lang_Module::module_entry_offset(), nullptr); } else if (java_lang_ClassLoader::is_instance(src_obj)) { #ifdef ASSERT - // We only archive these loaders - assert(java_lang_ClassLoader::aotIdentity(src_obj) != nullptr, "sanity check"); -#if 0 if (src_obj != SystemDictionary::java_platform_loader() && - src_obj != SystemDictionary::java_system_loader()) { + src_obj != SystemDictionary::java_system_loader() && + java_lang_ClassLoader::aotIdentity(src_obj) == nullptr) { assert(src_obj->klass()->name()->equals("jdk/internal/loader/ClassLoaders$BootClassLoader"), "must be"); } -#endif #endif update_buffered_object_field(to, java_lang_ClassLoader::loader_data_offset(), nullptr); } diff --git a/src/hotspot/share/cds/cdsConfig.cpp b/src/hotspot/share/cds/cdsConfig.cpp index abc704554a4..8618ed6259f 100644 --- a/src/hotspot/share/cds/cdsConfig.cpp +++ b/src/hotspot/share/cds/cdsConfig.cpp @@ -61,6 +61,7 @@ bool CDSConfig::_old_cds_flags_used = false; bool CDSConfig::_new_aot_flags_used = false; bool CDSConfig::_disable_heap_dumping = false; bool CDSConfig::_is_at_aot_safepoint = false; +bool CDSConfig::_supports_custom_loaders = false; const char* CDSConfig::_default_archive_path = nullptr; const char* CDSConfig::_input_static_archive_path = nullptr; @@ -780,6 +781,10 @@ bool CDSConfig::check_vm_args_consistency(bool patch_mod_javabase, bool mode_fla } } + if (is_dumping_archive()) { + set_custom_loaders_support(AOTCacheSupportForCustomLoader); + } + return true; } @@ -1159,3 +1164,11 @@ void CDSConfig::enable_dumping_aot_code() { bool CDSConfig::is_dumping_adapters() { return (AOTAdapterCaching && is_dumping_final_static_archive()); } + +bool CDSConfig::supports_custom_loaders() { + return _supports_custom_loaders; +} + +void CDSConfig::set_custom_loaders_support(bool value) { + _supports_custom_loaders = value; +} diff --git a/src/hotspot/share/cds/cdsConfig.hpp b/src/hotspot/share/cds/cdsConfig.hpp index f20e2147df5..0ed830d6599 100644 --- a/src/hotspot/share/cds/cdsConfig.hpp +++ b/src/hotspot/share/cds/cdsConfig.hpp @@ -55,6 +55,8 @@ class CDSConfig : public AllStatic { static bool _new_aot_flags_used; static bool _disable_heap_dumping; + static bool _supports_custom_loaders; + static JavaThread* _dumper_thread; #endif @@ -206,6 +208,9 @@ class CDSConfig : public AllStatic { static void stop_dumping_full_module_graph(const char* reason = nullptr) NOT_CDS_JAVA_HEAP_RETURN; static void stop_using_full_module_graph(const char* reason = nullptr) NOT_CDS_JAVA_HEAP_RETURN; + static bool supports_custom_loaders(); + static void set_custom_loaders_support(bool value); + // --- AOT code static bool is_dumping_aot_code() NOT_CDS_RETURN_(false); diff --git a/src/hotspot/share/cds/cdsProtectionDomain.cpp b/src/hotspot/share/cds/cdsProtectionDomain.cpp index 2fa6da81f28..e9dbce16275 100644 --- a/src/hotspot/share/cds/cdsProtectionDomain.cpp +++ b/src/hotspot/share/cds/cdsProtectionDomain.cpp @@ -48,6 +48,7 @@ OopHandle CDSProtectionDomain::_shared_jar_manifests; // Returns the ProtectionDomain for the InstanceKlass. Handle CDSProtectionDomain::init_security_info(Handle class_loader, InstanceKlass* ik, PackageEntry* pkg_entry, TRAPS) { if (ik->defined_by_other_loaders()) { + assert(ik->cl_aot_identity() != nullptr, "sanity check"); // define NamedPackage in java layer define_named_package(class_loader, pkg_entry, THREAD); // Get ClassLoader::defaultDomain diff --git a/src/hotspot/share/cds/cds_globals.hpp b/src/hotspot/share/cds/cds_globals.hpp index 8e92ea70ae8..042d497872a 100644 --- a/src/hotspot/share/cds/cds_globals.hpp +++ b/src/hotspot/share/cds/cds_globals.hpp @@ -233,6 +233,10 @@ \ product(bool, SkipArchiveHeapVerification, false, \ "Skip verification of CDS archive heap") \ + \ + product(bool, AOTCacheSupportForCustomLoader, false, \ + "Enable support for custom loaders in AOTCache") \ + // end of CDS_FLAGS diff --git a/src/hotspot/share/cds/dumpTimeClassInfo.cpp b/src/hotspot/share/cds/dumpTimeClassInfo.cpp index 0f5773a2729..1f8f4fdf76d 100644 --- a/src/hotspot/share/cds/dumpTimeClassInfo.cpp +++ b/src/hotspot/share/cds/dumpTimeClassInfo.cpp @@ -172,6 +172,8 @@ class CountClassByCategory : StackObj { if (!info.is_excluded()) { if (info.is_builtin()) { _table->inc_builtin_count(); + } else if (k->is_defined_by_aot_safe_custom_loader()) { + _table->inc_defined_by_aot_safe_custom_loaders_count(); } else { _table->inc_unregistered_count(); } @@ -181,6 +183,7 @@ class CountClassByCategory : StackObj { void DumpTimeSharedClassTable::update_counts() { _builtin_count = 0; + _defined_by_aot_safe_custom_loaders_count = 0; _unregistered_count = 0; CountClassByCategory counter(this); iterate_all_live_classes(&counter); diff --git a/src/hotspot/share/cds/dumpTimeClassInfo.hpp b/src/hotspot/share/cds/dumpTimeClassInfo.hpp index ed467b15d08..bf9ecb0c6f4 100644 --- a/src/hotspot/share/cds/dumpTimeClassInfo.hpp +++ b/src/hotspot/share/cds/dumpTimeClassInfo.hpp @@ -276,20 +276,25 @@ using DumpTimeSharedClassTableBaseType = HashTable< class DumpTimeSharedClassTable: public DumpTimeSharedClassTableBaseType { int _builtin_count; + int _defined_by_aot_safe_custom_loaders_count; int _unregistered_count; public: DumpTimeSharedClassTable() { _builtin_count = 0; + _defined_by_aot_safe_custom_loaders_count = 0; _unregistered_count = 0; } DumpTimeClassInfo* allocate_info(InstanceKlass* k); DumpTimeClassInfo* get_info(InstanceKlass* k); void inc_builtin_count() { _builtin_count++; } + void inc_defined_by_aot_safe_custom_loaders_count() { _defined_by_aot_safe_custom_loaders_count++; } void inc_unregistered_count() { _unregistered_count++; } void update_counts(); - int count_of(bool is_builtin) const { + int count_of(bool is_builtin, bool is_defined_by_aot_safe_custom_loaders) const { if (is_builtin) { return _builtin_count; + } else if (is_defined_by_aot_safe_custom_loaders) { + return _defined_by_aot_safe_custom_loaders_count; } else { return _unregistered_count; } diff --git a/src/hotspot/share/cds/filemap.cpp b/src/hotspot/share/cds/filemap.cpp index ab0e3a5ba0c..f228b405e30 100644 --- a/src/hotspot/share/cds/filemap.cpp +++ b/src/hotspot/share/cds/filemap.cpp @@ -246,6 +246,7 @@ void FileMapHeader::populate(FileMapInfo *info, size_t core_region_alignment, _use_optimized_module_handling = CDSConfig::is_using_optimized_module_handling(); _has_aot_linked_classes = CDSConfig::is_dumping_aot_linked_classes(); _has_full_module_graph = CDSConfig::is_dumping_full_module_graph(); + _supports_custom_loaders = CDSConfig::supports_custom_loaders(); _gc_kind = (int)Universe::heap()->kind(); jio_snprintf(_gc_name, sizeof(_gc_name), Universe::heap()->name()); @@ -331,6 +332,7 @@ void FileMapHeader::print(outputStream* st) { st->print_cr("- use_optimized_module_handling: %d", _use_optimized_module_handling); st->print_cr("- has_full_module_graph %d", _has_full_module_graph); st->print_cr("- has_aot_linked_classes %d", _has_aot_linked_classes); + st->print_cr("- supports_custom_loaders: %d", _supports_custom_loaders); st->print_cr("- ptrmap_size_in_bits: %zu", _ptrmap_size_in_bits); } @@ -1770,6 +1772,9 @@ bool FileMapInfo::open_as_input() { } bool FileMapInfo::validate_aot_class_linking() { + if (header()->supports_custom_loaders()) { + CDSConfig::set_custom_loaders_support(true); + } // These checks need to be done after FileMapInfo::initialize(), which gets called before Universe::heap() // is available. if (header()->has_aot_linked_classes()) { diff --git a/src/hotspot/share/cds/filemap.hpp b/src/hotspot/share/cds/filemap.hpp index 4253bd1e542..9b736e963eb 100644 --- a/src/hotspot/share/cds/filemap.hpp +++ b/src/hotspot/share/cds/filemap.hpp @@ -141,6 +141,7 @@ class FileMapHeader: private CDSFileMapHeaderBase { // some expensive operations. bool _has_aot_linked_classes; // Was the CDS archive created with -XX:+AOTClassLinking bool _has_full_module_graph; // Does this CDS archive contain the full archived module graph? + bool _supports_custom_loaders; // int _gc_kind; // Universe::heap()->kind(); char _gc_name[32]; // Universe::heap()->name(); size_t _ptrmap_size_in_bits; // Size of pointer relocation bitmap @@ -211,6 +212,7 @@ class FileMapHeader: private CDSFileMapHeaderBase { int narrow_klass_pointer_bits() const { return _narrow_klass_pointer_bits; } int narrow_klass_shift() const { return _narrow_klass_shift; } bool has_full_module_graph() const { return _has_full_module_graph; } + bool supports_custom_loaders() const { return _supports_custom_loaders; } size_t rw_ptrmap_start_pos() const { return _rw_ptrmap_start_pos; } size_t ro_ptrmap_start_pos() const { return _ro_ptrmap_start_pos; } diff --git a/src/hotspot/share/cds/finalImageRecipes.cpp b/src/hotspot/share/cds/finalImageRecipes.cpp index 2bee970d04f..5d52c3675d9 100644 --- a/src/hotspot/share/cds/finalImageRecipes.cpp +++ b/src/hotspot/share/cds/finalImageRecipes.cpp @@ -45,7 +45,7 @@ GrowableArray* FinalImageRecipes::_ static FinalImageRecipes* _final_image_recipes = nullptr; using AOTIdentityToLoaderObjectMap = HashTable; -static AOTIdentityToLoaderObjectMap* _aot_compatible_loaders_map = nullptr; +static AOTIdentityToLoaderObjectMap* _aot_safe_custom_loaders_map = nullptr; void* FinalImageRecipes::operator new(size_t size) throw() { return ArchiveBuilder::current()->ro_region_alloc(size); @@ -261,7 +261,7 @@ void FinalImageRecipes::load_all_classes(TRAPS) { int flags = _flags->at(i); if (k->is_instance_klass()) { InstanceKlass* ik = InstanceKlass::cast(k); - if (ik->cl_aot_identity() == nullptr) { + if (ik->defined_by_other_loaders() && !k->is_defined_by_aot_safe_custom_loader()) { SystemDictionaryShared::init_dumptime_info(ik); SystemDictionaryShared::add_unregistered_class(THREAD, ik); SystemDictionaryShared::copy_unregistered_class_size_and_crc32(ik); @@ -285,21 +285,23 @@ void FinalImageRecipes::load_all_classes(TRAPS) { } } - // Custom Loaders must be marked with AOTSafeClassInitializer annotation. - // They would have been created as part of running class initializer for custom loaders. - // Objects of such custom loaders get registered with VM in ClassLoader::registerAsAOTCompatibleLoader(). - // Use these objects to load additional classes not yet loaded. - for (int i = 0; i < _all_klasses->length(); i++) { - Klass* k = _all_klasses->at(i); - int flags = _flags->at(i); - if (k->is_instance_klass()) { - InstanceKlass* ik = InstanceKlass::cast(k); - if (ik->defined_by_other_loaders() && ik->cl_aot_identity() != nullptr) { + if (CDSConfig::supports_custom_loaders()) { + // Custom Loaders must be marked with AOTSafeClassInitializer annotation. + // They would have been created as part of running class initializer for custom loaders. + // Objects of such custom loaders get registered with VM in ClassLoader::registerAsAOTCompatibleLoader(). + // Use these objects to load additional classes not yet loaded. + for (int i = 0; i < _all_klasses->length(); i++) { + Klass* k = _all_klasses->at(i); + int flags = _flags->at(i); + if (k->is_instance_klass()) { + InstanceKlass* ik = InstanceKlass::cast(k); Symbol* aot_identity = ik->cl_aot_identity(); - OopHandle* loader_h_ptr = _aot_compatible_loaders_map->get(aot_identity); - if (loader_h_ptr != nullptr) { - OopHandle loader_h = *loader_h_ptr; - Klass* actual = SystemDictionary::resolve_or_fail(ik->name(), Handle(THREAD, loader_h.resolve()), true, CHECK); + if (ik->defined_by_other_loaders() && aot_identity != nullptr) { + OopHandle* loader_h_ptr = _aot_safe_custom_loaders_map->get(aot_identity); + if (loader_h_ptr != nullptr) { + OopHandle loader_h = *loader_h_ptr; + Klass* actual = SystemDictionary::resolve_or_fail(ik->name(), Handle(THREAD, loader_h.resolve()), true, CHECK); + } } } } @@ -431,16 +433,19 @@ void FinalImageRecipes::serialize(SerializeClosure* soc) { soc->do_ptr((void**)&_final_image_recipes); } -void FinalImageRecipes::add_aot_compatible_loader(oop loader, TRAPS) { - ResourceMark rm; - if (_aot_compatible_loaders_map == nullptr) { - _aot_compatible_loaders_map = new (mtClassShared) AOTIdentityToLoaderObjectMap(); - } +void FinalImageRecipes::add_aot_safe_custom_loader(oop loader, TRAPS) { + assert(!SystemDictionary::is_builtin_class_loader(loader), "should not be called for builtin loader"); + if (CDSConfig::supports_custom_loaders()) { + ResourceMark rm; + if (_aot_safe_custom_loaders_map == nullptr) { + _aot_safe_custom_loaders_map = new (mtClassShared) AOTIdentityToLoaderObjectMap(); + } - OopHandle loader_h(Universe::vm_global(), loader); - Klass* klass = loader->klass(); - Symbol* aot_identity = java_lang_String::as_symbol(java_lang_ClassLoader::aotIdentity(loader)); + OopHandle loader_h(Universe::vm_global(), loader); + assert(java_lang_ClassLoader::aotIdentity(loader) != nullptr, "sanity check"); + Symbol* aot_identity = java_lang_String::as_symbol(java_lang_ClassLoader::aotIdentity(loader)); - log_info(cds)("Adding AOT compatible loader with aot identity=%s", aot_identity->as_C_string()); - _aot_compatible_loaders_map->put(aot_identity, loader_h); + log_info(cds)("Adding AOT compatible loader with aot identity=%s", aot_identity->as_C_string()); + _aot_safe_custom_loaders_map->put(aot_identity, loader_h); + } } diff --git a/src/hotspot/share/cds/finalImageRecipes.hpp b/src/hotspot/share/cds/finalImageRecipes.hpp index 29a78c94d76..ec9624abd32 100644 --- a/src/hotspot/share/cds/finalImageRecipes.hpp +++ b/src/hotspot/share/cds/finalImageRecipes.hpp @@ -110,7 +110,7 @@ class FinalImageRecipes { // Called when dumping final image static void apply_recipes(TRAPS); - static void add_aot_compatible_loader(oop loader, TRAPS); + static void add_aot_safe_custom_loader(oop loader, TRAPS); }; #endif // SHARE_CDS_FINALIMAGERECIPES_HPP diff --git a/src/hotspot/share/cds/heapShared.cpp b/src/hotspot/share/cds/heapShared.cpp index 8c808168273..b8399962a65 100644 --- a/src/hotspot/share/cds/heapShared.cpp +++ b/src/hotspot/share/cds/heapShared.cpp @@ -1162,12 +1162,16 @@ void KlassSubGraphInfo::add_subgraph_object_klass(Klass* orig_k) { #ifdef ASSERT InstanceKlass* ik = InstanceKlass::cast(orig_k); if (CDSConfig::is_dumping_method_handles()) { - // -XX:AOTInitTestClass must be used carefully in regression tests to - // include only classes that are safe to aot-initialize. - assert(ik->class_loader() == nullptr || ik->cl_aot_identity() != nullptr || - HeapShared::is_lambda_proxy_klass(ik) || - AOTClassInitializer::has_test_class(), - "we can archive only instances of boot classes or lambda proxy classes"); + // TODO: When custom loaders are supported, the custom loader instance needs to be stored in AOTCache + // which is loaded by app or system class loader. So the assert does not hold true. + if (!CDSConfig::supports_custom_loaders()) { + // -XX:AOTInitTestClass must be used carefully in regression tests to + // include only classes that are safe to aot-initialize. + assert(ik->class_loader() == nullptr || + HeapShared::is_lambda_proxy_klass(ik) || + AOTClassInitializer::has_test_class(), + "we can archive only instances of boot classes or lambda proxy classes"); + } } else { assert(ik->class_loader() == nullptr, "must be boot class"); } @@ -1244,8 +1248,13 @@ void KlassSubGraphInfo::check_allowed_klass(InstanceKlass* ik) { const char* testcls_msg = ""; #endif - if (ik->cl_aot_identity() != nullptr) { - return; + if (CDSConfig::supports_custom_loaders()) { + if (ik->class_loader() == SystemDictionary::java_system_loader()) { + return; + } + if (ik->cl_aot_identity() != nullptr) { + return; + } } ResourceMark rm; @@ -1481,7 +1490,7 @@ void HeapShared::resolve_classes_for_subgraphs(JavaThread* current, ArchivableSt for (int i = 0; fields[i].valid(); i++) { ArchivableStaticFieldInfo* info = &fields[i]; TempNewSymbol klass_name = SymbolTable::new_symbol(info->klass_name); - InstanceKlass* k = SystemDictionaryShared::find_class_in_aot_compatible_dictionary(klass_name); + InstanceKlass* k = SystemDictionaryShared::find_builtin_class(klass_name); assert(k != nullptr && k->defined_by_boot_loader(), "sanity"); resolve_classes_for_subgraph_of(current, k); } @@ -1685,7 +1694,7 @@ HeapShared::resolve_or_init_classes_for_subgraph_of(Klass* k, bool do_init, TRAP void HeapShared::resolve_or_init(const char* klass_name, bool do_init, TRAPS) { TempNewSymbol klass_name_sym = SymbolTable::new_symbol(klass_name); - InstanceKlass* k = SystemDictionaryShared::find_class_in_aot_compatible_dictionary(klass_name_sym); + InstanceKlass* k = SystemDictionaryShared::find_builtin_class(klass_name_sym); if (k == nullptr) { return; } diff --git a/src/hotspot/share/classfile/classLoaderData.cpp b/src/hotspot/share/classfile/classLoaderData.cpp index 0806c287234..2f31ba0fda0 100644 --- a/src/hotspot/share/classfile/classLoaderData.cpp +++ b/src/hotspot/share/classfile/classLoaderData.cpp @@ -90,7 +90,7 @@ void ClassLoaderData::init_null_class_loader_data() { _the_null_class_loader_data = new ClassLoaderData(Handle(), false); ClassLoaderDataGraph::_head = _the_null_class_loader_data; assert(_the_null_class_loader_data->is_the_null_class_loader_data(), "Must be"); - _the_null_class_loader_data->_aot_identity = SymbolTable::new_symbol("BOOT"); + //_the_null_class_loader_data->_aot_identity = SymbolTable::new_symbol("BOOT"); LogTarget(Trace, class, loader, data) lt; if (lt.is_enabled()) { ResourceMark rm; @@ -181,6 +181,11 @@ ClassLoaderData::ClassLoaderData(Handle h_class_loader, bool has_class_mirror_ho _dictionary = create_dictionary(); } #if 0 + /* This happens at a place where safepoint is not allowed. See NoSafepointVerifier in ClassLoaderDataGraph::add_to_graph(). + * However, call to restore_archived_data() may create a new Module and acquire Module_lock which allows safepoint. + * So we hit assertion in JavaThread::check_possible_safepoint(): + * assert(false, "Possible safepoint reached by thread that does not allow it"); + */ if (_aot_identity != nullptr && CDSConfig::is_using_full_module_graph()) { ClassLoaderDataShared::restore_archived_data(this); } diff --git a/src/hotspot/share/classfile/classLoaderDataShared.cpp b/src/hotspot/share/classfile/classLoaderDataShared.cpp index e11f4e425f4..59de4c8829c 100644 --- a/src/hotspot/share/classfile/classLoaderDataShared.cpp +++ b/src/hotspot/share/classfile/classLoaderDataShared.cpp @@ -79,11 +79,9 @@ class ArchivedClassLoaderData { void clear_archived_oops(); }; -#if 0 static ArchivedClassLoaderData _archived_boot_loader_data; static ArchivedClassLoaderData _archived_platform_loader_data; static ArchivedClassLoaderData _archived_system_loader_data; -#endif static ModuleEntry* _archived_javabase_moduleEntry = nullptr; static int _platform_loader_root_index = -1; @@ -93,8 +91,8 @@ static bool archived_cld_map_equals_fn(ArchivedClassLoaderData* archived_cld, Sy return archived_cld->aot_identity()->equals(sym); } -using ArchivedCLDataMap = OffsetCompactHashtable; -static ArchivedCLDataMap _archived_cld_map; +using ArchivedAOTIdToCLDataMap = OffsetCompactHashtable; +static ArchivedAOTIdToCLDataMap _aotid_to_archived_cld_map; void ArchivedClassLoaderData::iterate_symbols(ClassLoaderData* loader_data, MetaspaceClosure* closure) { assert(CDSConfig::is_dumping_full_module_graph(), "must be"); @@ -119,7 +117,9 @@ void ArchivedClassLoaderData::allocate(ClassLoaderData* loader_data) { _modules = loader_data->modules(false)->allocate_archived_entries(); } _unnamed_module = loader_data->unnamed_module()->allocate_archived_entry(); - _aot_identity = loader_data->aot_identity(); + if (!loader_data->is_builtin_class_loader_data()) { + _aot_identity = loader_data->aot_identity(); + } ArchivePtrMarker::mark_pointer((address*)&_packages); ArchivePtrMarker::mark_pointer((address*)&_modules); ArchivePtrMarker::mark_pointer((address*)&_unnamed_module); @@ -136,8 +136,10 @@ void ArchivedClassLoaderData::init_archived_entries(ClassLoaderData* loader_data loader_data->modules(false) ->init_archived_entries(_modules); } _unnamed_module->init_as_archived_entry(); - _aot_identity = ArchiveBuilder::get_buffered_symbol(_aot_identity); - assert(_aot_identity != nullptr, "sanity check"); + if (!loader_data->is_builtin_class_loader_data()) { + _aot_identity = ArchiveBuilder::get_buffered_symbol(_aot_identity); + assert(_aot_identity != nullptr, "sanity check"); + } } } @@ -179,8 +181,8 @@ void ArchivedClassLoaderData::clear_archived_oops() { // ------------------------------ -void ClassLoaderDataShared::load_archived_platform_and_system_class_loaders(Handle h_platform_loader, Handle h_system_loader) { - +void ClassLoaderDataShared::load_archived_platform_and_system_class_loaders() { + #if INCLUDE_CDS_JAVA_HEAP // The streaming object loader prefers loading the class loader related objects before // the CLD constructor which has a NoSafepointVerifier. @@ -197,24 +199,12 @@ void ClassLoaderDataShared::load_archived_platform_and_system_class_loaders(Hand } // When using the full module graph, we need to load unnamed modules too. - - Symbol* aot_identity_sym = nullptr; - oop aot_identity = java_lang_ClassLoader::aotIdentity(h_platform_loader()); - assert(aot_identity != nullptr, "sanity check"); - aot_identity_sym = java_lang_String::as_symbol(aot_identity); - unsigned int hash = Symbol::symbol_hash(aot_identity_sym); - ArchivedClassLoaderData* archived_platform_cld = _archived_cld_map.lookup(aot_identity_sym, hash, 0); - ModuleEntry* platform_loader_module_entry = archived_platform_cld->unnamed_module(); + ModuleEntry* platform_loader_module_entry = _archived_platform_loader_data.unnamed_module(); if (platform_loader_module_entry != nullptr) { platform_loader_module_entry->preload_archived_oops(); } - aot_identity = java_lang_ClassLoader::aotIdentity(h_system_loader()); - assert(aot_identity != nullptr, "sanity check"); - aot_identity_sym = java_lang_String::as_symbol(aot_identity); - hash = Symbol::symbol_hash(aot_identity_sym); - ArchivedClassLoaderData* archived_system_cld = _archived_cld_map.lookup(aot_identity_sym, hash, 0); - ModuleEntry* system_loader_module_entry = archived_system_cld->unnamed_module(); + ModuleEntry* system_loader_module_entry = _archived_system_loader_data.unnamed_module(); if (system_loader_module_entry != nullptr) { system_loader_module_entry->preload_archived_oops(); } @@ -226,7 +216,7 @@ static ClassLoaderData* null_class_loader_data() { assert(loader_data != nullptr, "must be"); return loader_data; } -#if 0 + static ClassLoaderData* java_platform_loader_data_or_null() { return ClassLoaderData::class_loader_data_or_null(SystemDictionary::java_platform_loader()); } @@ -234,7 +224,6 @@ static ClassLoaderData* java_platform_loader_data_or_null() { static ClassLoaderData* java_system_loader_data_or_null() { return ClassLoaderData::class_loader_data_or_null(SystemDictionary::java_system_loader()); } -#endif // ModuleEntryTables (even if empty) are required for iterate_symbols() to scan the // platform/system loaders inside the CDS safepoint, but the tables can be created only @@ -268,50 +257,48 @@ class CLDSymbolsIterator : public CLDClosure { void ClassLoaderDataShared::iterate_symbols(MetaspaceClosure* closure) { assert(CDSConfig::is_dumping_full_module_graph(), "must be"); - CLDSymbolsIterator sym_iterator(closure); - ClassLoaderDataGraph::cld_do(&sym_iterator); -#if 0 _archived_boot_loader_data.iterate_symbols (null_class_loader_data(), closure); _archived_platform_loader_data.iterate_symbols(java_platform_loader_data_or_null(), closure); _archived_system_loader_data.iterate_symbols (java_system_loader_data_or_null(), closure); -#endif + + CLDSymbolsIterator sym_iterator(closure); + ClassLoaderDataGraph::cld_do(&sym_iterator); } using ArchivedCLDToCLDMap = HashTable; -ArchivedCLDToCLDMap* _aot_compatible_loaders = nullptr; +ArchivedCLDToCLDMap* _archived_cld_to_runtime_cld_map = nullptr; class ArchiveAOTCompatibleLoaders : public CLDClosure { public: void do_cld(ClassLoaderData* cld) { - if (cld->aot_identity() != nullptr) { + if (!cld->is_builtin_class_loader_data() && cld->aot_identity() != nullptr) { ArchivedClassLoaderData* archived_entry = (ArchivedClassLoaderData*)ArchiveBuilder::rw_region_alloc(sizeof(ArchivedClassLoaderData)); archived_entry->allocate(cld); - _aot_compatible_loaders->put(archived_entry, cld); + _archived_cld_to_runtime_cld_map->put(archived_entry, cld); } } }; void ClassLoaderDataShared::allocate_archived_tables() { assert(CDSConfig::is_dumping_full_module_graph(), "must be"); - _aot_compatible_loaders = new (mtClassShared) ArchivedCLDToCLDMap(); - ArchiveAOTCompatibleLoaders archive_closure; - ClassLoaderDataGraph::cld_do(&archive_closure); - -#if 0 _archived_boot_loader_data.allocate (null_class_loader_data()); _archived_platform_loader_data.allocate(java_platform_loader_data_or_null()); _archived_system_loader_data.allocate (java_system_loader_data_or_null()); -#endif + + _archived_cld_to_runtime_cld_map = new (mtClassShared) ArchivedCLDToCLDMap(); + if (CDSConfig::supports_custom_loaders()) { + ArchiveAOTCompatibleLoaders archive_closure; + ClassLoaderDataGraph::cld_do(&archive_closure); + } } void ClassLoaderDataShared::init_archived_tables() { assert(CDSConfig::is_dumping_full_module_graph(), "must be"); -#if 0 _archived_boot_loader_data.init_archived_entries (null_class_loader_data()); _archived_platform_loader_data.init_archived_entries(java_platform_loader_data_or_null()); _archived_system_loader_data.init_archived_entries (java_system_loader_data_or_null()); -#endif - _aot_compatible_loaders->iterate_all([&](ArchivedClassLoaderData* &archived_cld, ClassLoaderData* &cld) { + + _archived_cld_to_runtime_cld_map->iterate_all([&](ArchivedClassLoaderData* &archived_cld, ClassLoaderData* &cld) { archived_cld->init_archived_entries(cld); }); @@ -323,9 +310,9 @@ void ClassLoaderDataShared::init_archived_tables() { void ClassLoaderDataShared::write_cld_table() { CompactHashtableStats stats; - _archived_cld_map.reset(); - CompactHashtableWriter writer(_aot_compatible_loaders->number_of_entries(), &stats); - _aot_compatible_loaders->iterate_all([&](ArchivedClassLoaderData* &archived_cld, ClassLoaderData* &cld) { + _aotid_to_archived_cld_map.reset(); + CompactHashtableWriter writer(_archived_cld_to_runtime_cld_map->number_of_entries(), &stats); + _archived_cld_to_runtime_cld_map->iterate_all([&](ArchivedClassLoaderData* &archived_cld, ClassLoaderData* &cld) { ResourceMark rm; Symbol* aot_id = cld->aot_identity(); assert(aot_id != nullptr, "sanity check"); @@ -338,16 +325,14 @@ void ClassLoaderDataShared::write_cld_table() { log_trace(aot, hashtables)("archived cld dictionary: %s", aot_id->as_C_string()); } }); - writer.dump(&_archived_cld_map, "aot_cld_map dictionary"); + writer.dump(&_aotid_to_archived_cld_map, "aot_cld_map dictionary"); } void ClassLoaderDataShared::serialize(SerializeClosure* f) { -#if 0 _archived_boot_loader_data.serialize(f); _archived_platform_loader_data.serialize(f); _archived_system_loader_data.serialize(f); -#endif - _archived_cld_map.serialize_header(f); + _aotid_to_archived_cld_map.serialize_header(f); f->do_ptr(&_archived_javabase_moduleEntry); f->do_int(&_platform_loader_root_index); f->do_int(&_system_loader_root_index); @@ -355,11 +340,14 @@ void ClassLoaderDataShared::serialize(SerializeClosure* f) { ModuleEntry* ClassLoaderDataShared::archived_boot_unnamed_module() { if (CDSConfig::is_using_full_module_graph()) { - Symbol* boot_loader_aot_identity = SymbolTable::new_symbol("BOOT"); + return _archived_boot_loader_data.unnamed_module(); +#if 0 + Symbol* boot_loader_aot_identity = SymbolTable::new_symbol("BOOT"); unsigned int hash = Symbol::symbol_hash(boot_loader_aot_identity); - ArchivedClassLoaderData* archived_cld = _archived_cld_map.lookup(boot_loader_aot_identity, hash, 0); + ArchivedClassLoaderData* archived_cld = _aotid_to_archived_cld_map.lookup(boot_loader_aot_identity, hash, 0); assert(archived_cld != nullptr, "sanity check"); return archived_cld->unnamed_module(); +#endif } else { return nullptr; } @@ -368,36 +356,34 @@ ModuleEntry* ClassLoaderDataShared::archived_boot_unnamed_module() { ModuleEntry* ClassLoaderDataShared::archived_unnamed_module(ClassLoaderData* loader_data) { ModuleEntry* archived_module = nullptr; - //if (!Universe::is_module_initialized() && CDSConfig::is_using_full_module_graph()) { - if (CDSConfig::is_using_full_module_graph() && loader_data->aot_identity() != nullptr) { - precond(_platform_loader_root_index >= 0); - precond(_system_loader_root_index >= 0); - -#if 0 - if (loader_data->class_loader() == HeapShared::get_root(_platform_loader_root_index)) { - archived_module = _archived_platform_loader_data.unnamed_module(); - } else if (loader_data->class_loader() == HeapShared::get_root(_system_loader_root_index)) { - archived_module = _archived_system_loader_data.unnamed_module(); - } -#endif - unsigned int hash = Symbol::symbol_hash(loader_data->aot_identity()); - ArchivedClassLoaderData* archived_cld = _archived_cld_map.lookup(loader_data->aot_identity(), hash, 0); - if (archived_cld != nullptr) { - archived_module = archived_cld->unnamed_module(); + if (CDSConfig::is_using_full_module_graph()) { + if (!Universe::is_module_initialized()) { + precond(_platform_loader_root_index >= 0); + precond(_system_loader_root_index >= 0); + + if (loader_data->class_loader() == HeapShared::get_root(_platform_loader_root_index)) { + archived_module = _archived_platform_loader_data.unnamed_module(); + } else if (loader_data->class_loader() == HeapShared::get_root(_system_loader_root_index)) { + archived_module = _archived_system_loader_data.unnamed_module(); + } + } else if (loader_data->aot_identity() != nullptr) { + unsigned int hash = Symbol::symbol_hash(loader_data->aot_identity()); + ArchivedClassLoaderData* archived_cld = _aotid_to_archived_cld_map.lookup(loader_data->aot_identity(), hash, 0); + if (archived_cld != nullptr) { + archived_module = archived_cld->unnamed_module(); + } } } return archived_module; } - void ClassLoaderDataShared::clear_archived_oops() { assert(!CDSConfig::is_using_full_module_graph(), "must be"); -#if 0 _archived_boot_loader_data.clear_archived_oops(); _archived_platform_loader_data.clear_archived_oops(); _archived_system_loader_data.clear_archived_oops(); -#endif - _archived_cld_map.iterate([&](ArchivedClassLoaderData* archived_cld) { + + _aotid_to_archived_cld_map.iterate([&](ArchivedClassLoaderData* archived_cld) { archived_cld->clear_archived_oops(); }); if (_platform_loader_root_index >= 0) { @@ -409,10 +395,10 @@ void ClassLoaderDataShared::clear_archived_oops() { // Must be done before ClassLoader::create_javabase() void ClassLoaderDataShared::restore_archived_entries_for_null_class_loader_data() { precond(CDSConfig::is_using_full_module_graph()); - unsigned int hash = Symbol::symbol_hash(null_class_loader_data()->aot_identity()); - ArchivedClassLoaderData* archived_cld = _archived_cld_map.lookup(null_class_loader_data()->aot_identity(), hash, 0); - archived_cld->restore(null_class_loader_data(), true, false); - //_archived_boot_loader_data.restore(null_class_loader_data(), true, false); + //unsigned int hash = Symbol::symbol_hash(null_class_loader_data()->aot_identity()); + //ArchivedClassLoaderData* archived_cld = _aotid_to_archived_cld_map.lookup(null_class_loader_data()->aot_identity(), hash, 0); + //archived_cld->restore(null_class_loader_data(), true, false); + _archived_boot_loader_data.restore(null_class_loader_data(), true, false); ModuleEntryTable::set_javabase_moduleEntry(_archived_javabase_moduleEntry); aot_log_info(aot)("use_full_module_graph = true; java.base = " INTPTR_FORMAT, p2i(_archived_javabase_moduleEntry)); @@ -420,31 +406,31 @@ void ClassLoaderDataShared::restore_archived_entries_for_null_class_loader_data( oop ClassLoaderDataShared::restore_archived_oops_for_null_class_loader_data() { assert(CDSConfig::is_using_full_module_graph(), "must be"); - unsigned int hash = Symbol::symbol_hash(null_class_loader_data()->aot_identity()); - ArchivedClassLoaderData* archived_cld = _archived_cld_map.lookup(null_class_loader_data()->aot_identity(), hash, 0); - archived_cld->restore(null_class_loader_data(), false, true); - //_archived_boot_loader_data.restore(null_class_loader_data(), false, true); - null_class_loader_data()->set_restored(true); + //unsigned int hash = Symbol::symbol_hash(null_class_loader_data()->aot_identity()); + //ArchivedClassLoaderData* archived_cld = _aotid_to_archived_cld_map.lookup(null_class_loader_data()->aot_identity(), hash, 0); + //archived_cld->restore(null_class_loader_data(), false, true); + _archived_boot_loader_data.restore(null_class_loader_data(), false, true); + //null_class_loader_data()->set_restored(true); return _archived_javabase_moduleEntry->module_oop(); } void ClassLoaderDataShared::restore_java_platform_loader_from_archive(ClassLoaderData* loader_data) { assert(CDSConfig::is_using_full_module_graph(), "must be"); - unsigned int hash = Symbol::symbol_hash(loader_data->aot_identity()); - ArchivedClassLoaderData* archived_cld = _archived_cld_map.lookup(loader_data->aot_identity(), hash, 0); - assert(archived_cld != nullptr, "ArchivedClassLoaderData for platform loader not found"); - archived_cld->restore(loader_data, true, true); - //_archived_platform_loader_data.restore(loader_data, true, true); + //unsigned int hash = Symbol::symbol_hash(loader_data->aot_identity()); + //ArchivedClassLoaderData* archived_cld = _aotid_to_archived_cld_map.lookup(loader_data->aot_identity(), hash, 0); + //assert(archived_cld != nullptr, "ArchivedClassLoaderData for platform loader not found"); + //archived_cld->restore(loader_data, true, true); + _archived_platform_loader_data.restore(loader_data, true, true); loader_data->set_restored(true); } void ClassLoaderDataShared::restore_java_system_loader_from_archive(ClassLoaderData* loader_data) { assert(CDSConfig::is_using_full_module_graph(), "must be"); - unsigned int hash = Symbol::symbol_hash(loader_data->aot_identity()); - ArchivedClassLoaderData* archived_cld = _archived_cld_map.lookup(loader_data->aot_identity(), hash, 0); - assert(archived_cld != nullptr, "ArchivedClassLoaderData for system loader not found"); - archived_cld->restore(loader_data, true, true); - //_archived_system_loader_data.restore(loader_data, true, true); + //unsigned int hash = Symbol::symbol_hash(loader_data->aot_identity()); + //ArchivedClassLoaderData* archived_cld = _aotid_to_archived_cld_map.lookup(loader_data->aot_identity(), hash, 0); + //assert(archived_cld != nullptr, "ArchivedClassLoaderData for system loader not found"); + //archived_cld->restore(loader_data, true, true); + _archived_system_loader_data.restore(loader_data, true, true); _full_module_graph_loaded = true; loader_data->set_restored(true); } @@ -462,7 +448,8 @@ void ClassLoaderDataShared::restore_archived_modules_for_preloading_classes(Java Modules::init_archived_modules(current, h_platform_loader, h_system_loader); } -void ClassLoaderDataShared::restore_archived_data(ClassLoaderData* loader_data) { +void ClassLoaderDataShared::restore_custom_loader_archived_data(ClassLoaderData* loader_data) { + assert(!loader_data->is_builtin_class_loader_data(), "should not be called for built-in loaders"); //if already restored, nothing to do if (loader_data->restored()) { return; @@ -472,7 +459,7 @@ void ClassLoaderDataShared::restore_archived_data(ClassLoaderData* loader_data) Symbol* aot_id = loader_data->aot_identity(); assert(aot_id != nullptr, "sanity check"); unsigned int hash = Symbol::symbol_hash(aot_id); - ArchivedClassLoaderData* archived_cld = _archived_cld_map.lookup(aot_id, hash, 0); + ArchivedClassLoaderData* archived_cld = _aotid_to_archived_cld_map.lookup(aot_id, hash, 0); assert(archived_cld != nullptr, "ArchivedClassLoaderData for loader with aot_id=%s not found", aot_id->as_C_string()); archived_cld->restore(loader_data, true, true); loader_data->set_restored(true); diff --git a/src/hotspot/share/classfile/classLoaderDataShared.hpp b/src/hotspot/share/classfile/classLoaderDataShared.hpp index 7316de105b4..9de92e6c0d2 100644 --- a/src/hotspot/share/classfile/classLoaderDataShared.hpp +++ b/src/hotspot/share/classfile/classLoaderDataShared.hpp @@ -38,7 +38,7 @@ class ClassLoaderDataShared : AllStatic { static bool _full_module_graph_loaded; CDS_JAVA_HEAP_ONLY(static void ensure_module_entry_table_exists(oop class_loader);) public: - static void load_archived_platform_and_system_class_loaders(Handle h_platform_loader, Handle h_system_loader) NOT_CDS_JAVA_HEAP_RETURN; + static void load_archived_platform_and_system_class_loaders() NOT_CDS_JAVA_HEAP_RETURN; static void restore_archived_modules_for_preloading_classes(JavaThread* current) NOT_CDS_JAVA_HEAP_RETURN; #if INCLUDE_CDS_JAVA_HEAP static void ensure_module_entry_tables_exist(); @@ -56,7 +56,7 @@ class ClassLoaderDataShared : AllStatic { #endif // INCLUDE_CDS_JAVA_HEAP static bool is_full_module_graph_loaded() { return _full_module_graph_loaded; } static void write_cld_table(); - static void restore_archived_data(ClassLoaderData* loader_data); + static void restore_custom_loader_archived_data(ClassLoaderData* loader_data); }; #endif // SHARE_CLASSFILE_CLASSLOADERDATASHARED_HPP diff --git a/src/hotspot/share/classfile/moduleEntry.cpp b/src/hotspot/share/classfile/moduleEntry.cpp index 344f794366b..696a2d06c71 100644 --- a/src/hotspot/share/classfile/moduleEntry.cpp +++ b/src/hotspot/share/classfile/moduleEntry.cpp @@ -417,8 +417,7 @@ static int _num_inited_module_entries = 0; #endif bool ModuleEntry::should_be_archived() const { - //return SystemDictionaryShared::is_builtin_loader(loader_data()); - return loader_data()->aot_identity() != nullptr; + return SystemDictionaryShared::is_builtin_loader(loader_data()) || loader_data()->aot_identity() != nullptr; } ModuleEntry* ModuleEntry::allocate_archived_entry() const { diff --git a/src/hotspot/share/classfile/modules.cpp b/src/hotspot/share/classfile/modules.cpp index aa18423f345..4d1724f10cd 100644 --- a/src/hotspot/share/classfile/modules.cpp +++ b/src/hotspot/share/classfile/modules.cpp @@ -505,8 +505,7 @@ void Modules::check_archived_module_oop(oop orig_module_obj) { // We only archive the default module graph, which should contain only java.lang.Module oops // for the 3 built-in loaders (boot/platform/system) ClassLoaderData* loader_data = orig_module_ent->loader_data(); - //assert(loader_data->is_builtin_class_loader_data(), "must be"); - assert(loader_data->aot_identity() != nullptr, "must be"); + assert(loader_data->is_builtin_class_loader_data() || loader_data->aot_identity() != nullptr, "must be"); if (orig_module_ent->name() != nullptr) { // For each named module, we archive both the java.lang.Module oop and the ModuleEntry. @@ -742,7 +741,7 @@ void Modules::init_archived_modules(JavaThread* current, Handle h_platform_loade ModuleEntryTable::patch_javabase_entries(current, java_base_module); } - ClassLoaderDataShared::load_archived_platform_and_system_class_loaders(h_platform_loader, h_system_loader); + ClassLoaderDataShared::load_archived_platform_and_system_class_loaders(); ClassLoaderData* platform_loader_data = SystemDictionary::register_loader(h_platform_loader); SystemDictionary::set_platform_loader(platform_loader_data); diff --git a/src/hotspot/share/classfile/systemDictionary.cpp b/src/hotspot/share/classfile/systemDictionary.cpp index f3e58cd0620..7f8369f0b91 100644 --- a/src/hotspot/share/classfile/systemDictionary.cpp +++ b/src/hotspot/share/classfile/systemDictionary.cpp @@ -1290,7 +1290,12 @@ InstanceKlass* SystemDictionary::load_instance_class_impl(Symbol* class_name, Ha if (CDSConfig::is_using_archive()) { PerfTraceElapsedTime vmtimer(ClassLoader::perf_shared_classload_time()); - InstanceKlass* ik = SystemDictionaryShared::find_class_in_aot_compatible_dictionary(class_name); + InstanceKlass* ik = nullptr; + if (SystemDictionary::is_builtin_class_loader(class_loader())) { + ik = SystemDictionaryShared::find_builtin_class(class_name); + } else if (CDSConfig::supports_custom_loaders()) { + ik = SystemDictionaryShared::find_class_in_aot_safe_custom_loader_dict(class_name); + } if (ik != nullptr && ik->defined_by_boot_loader() && !ik->shared_loading_failed()) { SharedClassLoadingMark slm(THREAD, ik); k = load_shared_class(ik, class_loader, Handle(), nullptr, pkg_entry, CHECK_NULL); diff --git a/src/hotspot/share/classfile/systemDictionaryShared.cpp b/src/hotspot/share/classfile/systemDictionaryShared.cpp index 1bfeb9e8d45..fcd407a3f10 100644 --- a/src/hotspot/share/classfile/systemDictionaryShared.cpp +++ b/src/hotspot/share/classfile/systemDictionaryShared.cpp @@ -99,10 +99,31 @@ static void check_klass_after_loading(const Klass* k) { } #endif -InstanceKlass* SystemDictionaryShared::load_shared_class_for_aot_compatible_loader( +InstanceKlass* SystemDictionaryShared::load_shared_class_for_builtin_loader( Symbol* class_name, Handle class_loader, TRAPS) { assert(CDSConfig::is_using_archive(), "must be"); - InstanceKlass* ik = find_class_in_aot_compatible_dictionary(class_name); + InstanceKlass* ik = find_builtin_class(class_name); + + if (ik != nullptr && !ik->shared_loading_failed()) { + if ((SystemDictionary::is_system_class_loader(class_loader()) && ik->defined_by_app_loader()) || + (SystemDictionary::is_platform_class_loader(class_loader()) && ik->defined_by_platform_loader())) { + SharedClassLoadingMark slm(THREAD, ik); + PackageEntry* pkg_entry = CDSProtectionDomain::get_package_entry_from_class(ik, class_loader); + 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; +} + +InstanceKlass* SystemDictionaryShared::load_shared_class_for_aot_safe_custom_loader( + Symbol* class_name, Handle class_loader, TRAPS) { + assert(CDSConfig::is_using_archive(), "must be"); + InstanceKlass* ik = find_class_in_aot_safe_custom_loader_dict(class_name); if (ik != nullptr && !ik->shared_loading_failed()) { oop aot_identity = java_lang_ClassLoader::aotIdentity(class_loader()); @@ -141,8 +162,8 @@ InstanceKlass* SystemDictionaryShared::lookup_from_stream(Symbol* class_name, return nullptr; } - const RunTimeClassInfo* record = find_record(&_static_archive._aot_incompatible_loader_dict, - &_dynamic_archive._aot_incompatible_loader_dict, + const RunTimeClassInfo* record = find_record(&_static_archive._unregistered_dictionary, + &_dynamic_archive._unregistered_dictionary, class_name); if (record == nullptr) { return nullptr; @@ -630,17 +651,33 @@ InstanceKlass* SystemDictionaryShared::find_or_load_shared_class( if (!has_platform_or_app_classes()) { return nullptr; } + if (SystemDictionary::is_system_class_loader(class_loader()) || + SystemDictionary::is_platform_class_loader(class_loader())) { + ClassLoaderData *loader_data = register_loader(class_loader); + Dictionary* dictionary = loader_data->dictionary(); - if (java_lang_ClassLoader::aotIdentity(class_loader()) != nullptr) { + // Note: currently, find_or_load_shared_class is called for PlatformClassLoader and AppClassLoader, + // which are parallel-capable loaders, so a lock here is NOT taken. + assert(get_loader_lock_or_null(class_loader) == nullptr, "ObjectLocker not required"); + { + MutexLocker mu(THREAD, SystemDictionary_lock); + InstanceKlass* check = dictionary->find_class(THREAD, name); + if (check != nullptr) { + return check; + } + } + + k = load_shared_class_for_builtin_loader(name, class_loader, THREAD); + } else if (CDSConfig::supports_custom_loaders() && java_lang_ClassLoader::aotIdentity(class_loader()) != nullptr) { ClassLoaderData *loader_data = register_loader(class_loader); if (CDSConfig::is_using_full_module_graph()) { - ClassLoaderDataShared::restore_archived_data(loader_data); + ClassLoaderDataShared::restore_custom_loader_archived_data(loader_data); } Dictionary* dictionary = loader_data->dictionary(); - // Note: currently, find_or_load_shared_class is called only from - // JVM_FindLoadedClass and used for PlatformClassLoader and AppClassLoader, - // which are parallel-capable loaders, so a lock here is NOT taken. + // TODO: find_or_load_shared_class is called for + // aot safe custom loader, which may or may not be parallel-capable loader. + // Should we acquire loader lock or not? //assert(get_loader_lock_or_null(class_loader) == nullptr, "ObjectLocker not required"); { MutexLocker mu(THREAD, SystemDictionary_lock); @@ -650,14 +687,14 @@ InstanceKlass* SystemDictionaryShared::find_or_load_shared_class( } } - k = load_shared_class_for_aot_compatible_loader(name, class_loader, THREAD); - if (k != nullptr) { - SharedClassLoadingMark slm(THREAD, k); - k = find_or_define_instance_class(name, class_loader, k, CHECK_NULL); - } + k = load_shared_class_for_aot_safe_custom_loader(name, class_loader, THREAD); } } + if (k != nullptr) { + SharedClassLoadingMark slm(THREAD, k); + k = find_or_define_instance_class(name, class_loader, k, CHECK_NULL); + } DEBUG_ONLY(check_klass_after_loading(k);) return k; @@ -703,7 +740,7 @@ void SystemDictionaryShared::copy_unregistered_class_size_and_crc32(InstanceKlas precond(klass->in_aot_cache()); // A shared class must have a RunTimeClassInfo record - const RunTimeClassInfo* record = find_record(&_static_archive._aot_incompatible_loader_dict, + const RunTimeClassInfo* record = find_record(&_static_archive._unregistered_dictionary, nullptr, klass->name()); precond(record != nullptr); precond(record->klass() == klass); @@ -1319,58 +1356,70 @@ unsigned int SystemDictionaryShared::hash_for_shared_dictionary(address ptr) { class CopySharedClassInfoToArchive : StackObj { CompactHashtableWriter* _writer; - bool _is_aot_compatible_loader; + bool _is_builtin; + bool _is_defined_by_aot_safe_custom_loader; ArchiveBuilder *_builder; public: CopySharedClassInfoToArchive(CompactHashtableWriter* writer, - bool is_aot_compatible_loader) - : _writer(writer), _is_aot_compatible_loader(is_aot_compatible_loader), _builder(ArchiveBuilder::current()) {} + bool is_builtin, + bool is_defined_by_aot_safe_custom_loader) + : _writer(writer), _is_builtin(is_builtin), _is_defined_by_aot_safe_custom_loader(is_defined_by_aot_safe_custom_loader), _builder(ArchiveBuilder::current()) + { + assert(!_is_builtin || !_is_defined_by_aot_safe_custom_loader, "both conditions cannot be true"); + } void do_entry(InstanceKlass* k, DumpTimeClassInfo& info) { - if (!info.is_excluded() && k->is_aot_compatible_loader() == _is_aot_compatible_loader) { - size_t byte_size = info.runtime_info_bytesize(); - RunTimeClassInfo* record; - record = (RunTimeClassInfo*)ArchiveBuilder::ro_region_alloc(byte_size); - record->init(info); - - unsigned int hash; - Symbol* name = info._klass->name(); - name = ArchiveBuilder::current()->get_buffered_addr(name); - hash = SystemDictionaryShared::hash_for_shared_dictionary((address)name); - u4 delta = _builder->buffer_to_offset_u4((address)record); - if (_is_aot_compatible_loader && info._klass->is_hidden()) { - // skip - } else { - _writer->add(hash, delta); - } - if (log_is_enabled(Trace, aot, hashtables)) { - ResourceMark rm; - log_trace(aot, hashtables)("%s dictionary: %s", (_is_aot_compatible_loader ? "aot_compatible_loader" : "aot_incompatible_loader"), info._klass->external_name()); - } + if (!info.is_excluded()) { + if ((info.is_builtin() && _is_builtin) || (k->is_defined_by_aot_safe_custom_loader() && _is_defined_by_aot_safe_custom_loader) || + (!info.is_builtin() && !_is_builtin && !k->is_defined_by_aot_safe_custom_loader() && !_is_defined_by_aot_safe_custom_loader)) { + size_t byte_size = info.runtime_info_bytesize(); + RunTimeClassInfo* record; + record = (RunTimeClassInfo*)ArchiveBuilder::ro_region_alloc(byte_size); + record->init(info); + + unsigned int hash; + Symbol* name = info._klass->name(); + name = ArchiveBuilder::current()->get_buffered_addr(name); + hash = SystemDictionaryShared::hash_for_shared_dictionary((address)name); + u4 delta = _builder->buffer_to_offset_u4((address)record); + if (_is_builtin && info._klass->is_hidden()) { + // skip + } else { + _writer->add(hash, delta); + } + if (log_is_enabled(Trace, aot, hashtables)) { + ResourceMark rm; + const char* dict_name = _is_builtin ? "builtin" : _is_defined_by_aot_safe_custom_loader ? "aot_safe_custom_loader_dict" : "unregistered"; + log_trace(aot, hashtables)("%s dictionary: %s", dict_name, info._klass->external_name()); + } - // Save this for quick runtime lookup of InstanceKlass* -> RunTimeClassInfo* - InstanceKlass* buffered_klass = ArchiveBuilder::current()->get_buffered_addr(info._klass); - RunTimeClassInfo::set_for(buffered_klass, record); + // Save this for quick runtime lookup of InstanceKlass* -> RunTimeClassInfo* + InstanceKlass* buffered_klass = ArchiveBuilder::current()->get_buffered_addr(info._klass); + RunTimeClassInfo::set_for(buffered_klass, record); + } } } }; void SystemDictionaryShared::write_dictionary(RunTimeSharedDictionary* dictionary, - bool is_aot_compatible_loader) { + bool is_builtin, + bool is_defined_by_aot_safe_custom_loader) { CompactHashtableStats stats; dictionary->reset(); - CompactHashtableWriter writer(_dumptime_table->count_of(is_aot_compatible_loader), &stats); - CopySharedClassInfoToArchive copy(&writer, is_aot_compatible_loader); + CompactHashtableWriter writer(_dumptime_table->count_of(is_builtin, is_defined_by_aot_safe_custom_loader), &stats); + CopySharedClassInfoToArchive copy(&writer, is_builtin, is_defined_by_aot_safe_custom_loader); assert_lock_strong(DumpTimeTable_lock); _dumptime_table->iterate_all_live_classes(©); - writer.dump(dictionary, is_aot_compatible_loader? "aot_compatible_loader dictionary" : "aot_incompatible_loader dictionary"); + const char* dict_name = is_builtin ? "builtin" : is_defined_by_aot_safe_custom_loader ? "aot_safe_custom_loader_dict" : "unregistered"; + writer.dump(dictionary, dict_name); } void SystemDictionaryShared::write_to_archive(bool is_static_archive) { ArchiveInfo* archive = get_archive(is_static_archive); - write_dictionary(&archive->_aot_compatible_loader_dict, true); - write_dictionary(&archive->_aot_incompatible_loader_dict, false); + write_dictionary(&archive->_builtin_dictionary, true, false); + write_dictionary(&archive->_aot_safe_custom_loader_dict, false, true); + write_dictionary(&archive->_unregistered_dictionary, false, false); if (CDSConfig::is_dumping_lambdas_in_legacy_mode()) { LambdaProxyClassDictionary::write_dictionary(is_static_archive); } else { @@ -1382,8 +1431,9 @@ void SystemDictionaryShared::serialize_dictionary_headers(SerializeClosure* soc, bool is_static_archive) { ArchiveInfo* archive = get_archive(is_static_archive); - archive->_aot_compatible_loader_dict.serialize_header(soc); - archive->_aot_incompatible_loader_dict.serialize_header(soc); + archive->_builtin_dictionary.serialize_header(soc); + archive->_aot_safe_custom_loader_dict.serialize_header(soc); + archive->_unregistered_dictionary.serialize_header(soc); LambdaProxyClassDictionary::serialize(soc, is_static_archive); } @@ -1426,9 +1476,27 @@ SystemDictionaryShared::find_record(RunTimeSharedDictionary* static_dict, RunTim return record; } -InstanceKlass* SystemDictionaryShared::find_class_in_aot_compatible_dictionary(Symbol* name) { - const RunTimeClassInfo* record = find_record(&_static_archive._aot_compatible_loader_dict, - &_dynamic_archive._aot_compatible_loader_dict, +InstanceKlass* SystemDictionaryShared::find_builtin_class(Symbol* name) { + const RunTimeClassInfo* record = find_record(&_static_archive._builtin_dictionary, + &_dynamic_archive._builtin_dictionary, + name); + if (record != nullptr) { + assert(!record->klass()->is_hidden(), "hidden class cannot be looked up by name"); + DEBUG_ONLY(check_klass_after_loading(record->klass());) + // We did not save the classfile data of the generated LambdaForm invoker classes, + // so we cannot support CLFH for such classes. + if (record->klass()->is_aot_generated_class() && JvmtiExport::should_post_class_file_load_hook()) { + return nullptr; + } + return record->klass(); + } else { + return nullptr; + } +} + +InstanceKlass* SystemDictionaryShared::find_class_in_aot_safe_custom_loader_dict(Symbol* name) { + const RunTimeClassInfo* record = find_record(&_static_archive._aot_safe_custom_loader_dict, + &_dynamic_archive._aot_safe_custom_loader_dict, name); if (record != nullptr) { assert(!record->klass()->is_hidden(), "hidden class cannot be looked up by name"); @@ -1469,11 +1537,11 @@ const char* SystemDictionaryShared::loader_type_for_shared_class(Klass* k) { } void SystemDictionaryShared::get_all_archived_classes(bool is_static_archive, GrowableArray* classes) { - get_archive(is_static_archive)->_aot_compatible_loader_dict.iterate([&] (const RunTimeClassInfo* record) { + get_archive(is_static_archive)->_aot_safe_custom_loader_dict.iterate([&] (const RunTimeClassInfo* record) { classes->append(record->klass()); }); - get_archive(is_static_archive)->_aot_incompatible_loader_dict.iterate([&] (const RunTimeClassInfo* record) { + get_archive(is_static_archive)->_unregistered_dictionary.iterate([&] (const RunTimeClassInfo* record) { classes->append(record->klass()); }); } @@ -1502,9 +1570,9 @@ void SystemDictionaryShared::ArchiveInfo::print_on(const char* prefix, st->print_cr("%sShared Dictionary", prefix); SharedDictionaryPrinter p(st); st->print_cr("%sShared AOT Compatible Loaders Dictionary", prefix); - _aot_compatible_loader_dict.iterate(&p); + _aot_safe_custom_loader_dict.iterate(&p); st->print_cr("%sShared AOT Incompatible Loaders Dictionary", prefix); - _aot_incompatible_loader_dict.iterate(&p); + _unregistered_dictionary.iterate(&p); LambdaProxyClassDictionary::print_on(prefix, st, p.index(), is_static_archive); } @@ -1512,8 +1580,8 @@ void SystemDictionaryShared::ArchiveInfo::print_table_statistics(const char* pre outputStream* st, bool is_static_archive) { st->print_cr("%sArchve Statistics", prefix); - _aot_compatible_loader_dict.print_table_statistics(st, "AOT Compatible Loaders Dictionary"); - _aot_incompatible_loader_dict.print_table_statistics(st, "AOT Incompatible Loaders Dictionary"); + _aot_safe_custom_loader_dict.print_table_statistics(st, "AOT Compatible Loaders Dictionary"); + _unregistered_dictionary.print_table_statistics(st, "AOT Incompatible Loaders Dictionary"); LambdaProxyClassDictionary::print_statistics(st, is_static_archive); } @@ -1546,7 +1614,7 @@ void SystemDictionaryShared::print_table_statistics(outputStream* st) { bool SystemDictionaryShared::is_dumptime_table_empty() { assert_lock_strong(DumpTimeTable_lock); _dumptime_table->update_counts(); - if (_dumptime_table->count_of(true) == 0 && _dumptime_table->count_of(false) == 0){ + if (_dumptime_table->count_of(true, false) == 0 && _dumptime_table->count_of(false, true) == 0 && _dumptime_table->count_of(false, false) == 0) { return true; } return false; diff --git a/src/hotspot/share/classfile/systemDictionaryShared.hpp b/src/hotspot/share/classfile/systemDictionaryShared.hpp index 89d14a049c2..1177dc244f3 100644 --- a/src/hotspot/share/classfile/systemDictionaryShared.hpp +++ b/src/hotspot/share/classfile/systemDictionaryShared.hpp @@ -139,10 +139,9 @@ class SystemDictionaryShared: public SystemDictionary { friend class LambdaProxyClassDictionary; struct ArchiveInfo { - //RunTimeSharedDictionary _builtin_dictionary; - //RunTimeSharedDictionary _unregistered_dictionary; - RunTimeSharedDictionary _aot_compatible_loader_dict; - RunTimeSharedDictionary _aot_incompatible_loader_dict; + RunTimeSharedDictionary _builtin_dictionary; + RunTimeSharedDictionary _aot_safe_custom_loader_dict; + RunTimeSharedDictionary _unregistered_dictionary; void print_on(const char* prefix, outputStream* st, bool is_static_archive); void print_table_statistics(const char* prefix, outputStream* st, bool is_static_archive); @@ -160,7 +159,11 @@ class SystemDictionaryShared: public SystemDictionary { return is_static_archive ? &_static_archive : &_dynamic_archive; } - static InstanceKlass* load_shared_class_for_aot_compatible_loader( + static InstanceKlass* load_shared_class_for_builtin_loader( + Symbol* class_name, + Handle class_loader, + TRAPS); + static InstanceKlass* load_shared_class_for_aot_safe_custom_loader( Symbol* class_name, Handle class_loader, TRAPS); @@ -173,7 +176,7 @@ class SystemDictionaryShared: public SystemDictionary { static void write_dictionary(RunTimeSharedDictionary* dictionary, - bool is_builtin); + bool is_builtin, bool is_defined_by_aot_safe_custom_loader); static bool is_jfr_event_class(InstanceKlass *k); static void link_all_exclusion_check_candidates(InstanceKlass* ik); static bool should_be_excluded_impl(InstanceKlass* k, DumpTimeClassInfo* info); @@ -211,7 +214,8 @@ class SystemDictionaryShared: public SystemDictionary { static bool has_archived_enum_objs(InstanceKlass* ik); static void set_has_archived_enum_objs(InstanceKlass* ik); - static InstanceKlass* find_class_in_aot_compatible_dictionary(Symbol* class_name); + static InstanceKlass* find_builtin_class(Symbol* name); + static InstanceKlass* find_class_in_aot_safe_custom_loader_dict(Symbol* class_name); static const RunTimeClassInfo* find_record(RunTimeSharedDictionary* static_dict, RunTimeSharedDictionary* dynamic_dict, diff --git a/src/hotspot/share/oops/klass.hpp b/src/hotspot/share/oops/klass.hpp index c3b4c085ec1..82a6ed77fad 100644 --- a/src/hotspot/share/oops/klass.hpp +++ b/src/hotspot/share/oops/klass.hpp @@ -25,6 +25,7 @@ #ifndef SHARE_OOPS_KLASS_HPP #define SHARE_OOPS_KLASS_HPP +#include "cds/cdsConfig.hpp" #include "classfile/classLoaderData.hpp" #include "oops/klassFlags.hpp" #include "oops/markWord.hpp" @@ -315,13 +316,13 @@ class Klass : public Metadata { ClassLoaderData* class_loader_data() const { return _class_loader_data; } void set_class_loader_data(ClassLoaderData* loader_data) { _class_loader_data = loader_data; - if (loader_data != nullptr) { + if (CDSConfig::supports_custom_loaders() && loader_data != nullptr) { _cl_aot_identity = loader_data->aot_identity(); } } Symbol* cl_aot_identity() const { return _cl_aot_identity; } - bool is_aot_compatible_loader() const { return _cl_aot_identity != nullptr; } + bool is_defined_by_aot_safe_custom_loader() const { return _cl_aot_identity != nullptr; } s2 shared_classpath_index() const { return _shared_class_path_index; diff --git a/src/hotspot/share/prims/jvm.cpp b/src/hotspot/share/prims/jvm.cpp index 487b0a53f81..f9503e626cb 100644 --- a/src/hotspot/share/prims/jvm.cpp +++ b/src/hotspot/share/prims/jvm.cpp @@ -2284,7 +2284,7 @@ JVM_ENTRY_PROF(jobject, JVM_AssertionStatusDirectives, JVM_AssertionStatusDirect JVM_END JVM_ENTRY_PROF(void, JVM_RegisterAsAOTCompatibleLoader, JVM_RegisterAsAOTCompatibleLoader(JNIEnv *env, jobject loader)) - FinalImageRecipes::add_aot_compatible_loader(JNIHandles::resolve_non_null(loader), CHECK); + FinalImageRecipes::add_aot_safe_custom_loader(JNIHandles::resolve_non_null(loader), CHECK); JVM_END // Verification //////////////////////////////////////////////////////////////////////////////// diff --git a/src/java.base/share/classes/java/lang/ClassLoader.java b/src/java.base/share/classes/java/lang/ClassLoader.java index b66243862eb..48e81ae4265 100644 --- a/src/java.base/share/classes/java/lang/ClassLoader.java +++ b/src/java.base/share/classes/java/lang/ClassLoader.java @@ -340,7 +340,7 @@ void addClass(Class c) { /** * Returns a named package for the given module. * - * @param pn + * @param pn * package name * @param m * module @@ -2667,7 +2667,7 @@ ProtectionDomain getDefaultProtectionDomain() { /** * Set unique and repeatable ID of the classloader * - * @param id + * @param id * unique id of the classloader object. */ protected void setAOTIdentity(String id) { diff --git a/src/java.base/share/classes/jdk/internal/loader/ClassLoaders.java b/src/java.base/share/classes/jdk/internal/loader/ClassLoaders.java index 1f12b6d69ee..1a61d4b1522 100644 --- a/src/java.base/share/classes/jdk/internal/loader/ClassLoaders.java +++ b/src/java.base/share/classes/jdk/internal/loader/ClassLoaders.java @@ -131,7 +131,6 @@ public static ClassLoader appClassLoader() { private static class BootClassLoader extends BuiltinClassLoader { BootClassLoader(URLClassPath bcp) { super(null, null, bcp); - setAOTIdentity("BOOT"); } @Override @@ -152,7 +151,6 @@ private static class PlatformClassLoader extends BuiltinClassLoader { PlatformClassLoader(BootClassLoader parent) { super("platform", parent, null); - setAOTIdentity("PLATFORM"); } } @@ -168,7 +166,6 @@ private static class AppClassLoader extends BuiltinClassLoader { AppClassLoader(BuiltinClassLoader parent, URLClassPath ucp) { super("app", parent, ucp); - setAOTIdentity("APP"); } /** From 0a782896027c7ba176235844b8abb4fd8a27ca7a Mon Sep 17 00:00:00 2001 From: Ashutosh Mehra Date: Thu, 22 Jan 2026 00:39:04 -0500 Subject: [PATCH 03/30] Prelinking of classes loaded by aot-safe custom loaders Signed-off-by: Ashutosh Mehra --- src/hotspot/share/cds/aotArtifactFinder.cpp | 2 +- src/hotspot/share/cds/aotClassLinker.cpp | 106 +++++++++++++++++- src/hotspot/share/cds/aotClassLinker.hpp | 18 +++ .../share/cds/aotConstantPoolResolver.cpp | 2 +- .../share/cds/aotLinkedClassBulkLoader.cpp | 66 +++++++++++ .../share/cds/aotLinkedClassBulkLoader.hpp | 1 + src/hotspot/share/cds/aotMetaspace.cpp | 2 + src/hotspot/share/cds/archiveUtils.hpp | 8 +- src/hotspot/share/cds/archiveUtils.inline.hpp | 4 +- src/hotspot/share/cds/finalImageRecipes.cpp | 10 ++ .../share/classfile/classLoaderDataShared.cpp | 55 ++++++--- .../share/classfile/classLoaderDataShared.hpp | 1 + .../share/classfile/systemDictionary.cpp | 4 +- 13 files changed, 252 insertions(+), 27 deletions(-) diff --git a/src/hotspot/share/cds/aotArtifactFinder.cpp b/src/hotspot/share/cds/aotArtifactFinder.cpp index 5f346e832a8..233e5c85075 100644 --- a/src/hotspot/share/cds/aotArtifactFinder.cpp +++ b/src/hotspot/share/cds/aotArtifactFinder.cpp @@ -253,7 +253,7 @@ void AOTArtifactFinder::add_cached_instance_class(InstanceKlass* ik) { add_cached_instance_class(nest_host); } - if (CDSConfig::is_dumping_final_static_archive() && ik->defined_by_other_loaders()) { + if (CDSConfig::is_dumping_final_static_archive() && ik->defined_by_other_loaders() && !ik->is_defined_by_aot_safe_custom_loader() ) { // The following are not appliable to unregistered classes return; } diff --git a/src/hotspot/share/cds/aotClassLinker.cpp b/src/hotspot/share/cds/aotClassLinker.cpp index 0ed17dd6746..8940be1d91e 100644 --- a/src/hotspot/share/cds/aotClassLinker.cpp +++ b/src/hotspot/share/cds/aotClassLinker.cpp @@ -45,6 +45,12 @@ AOTClassLinker::ClassesTable* AOTClassLinker::_vm_classes = nullptr; AOTClassLinker::ClassesTable* AOTClassLinker::_candidates = nullptr; GrowableArrayCHeap* AOTClassLinker::_sorted_candidates = nullptr; +static const unsigned INITIAL_TABLE_SIZE = 997; // prime number +static const unsigned MAX_TABLE_SIZE = 10000; +typedef GrowableArrayCHeap ClassList; +typedef ResizeableHashTable ClassLoaderIdToPrelinkedTable; +ClassLoaderIdToPrelinkedTable* _custom_loader_prelinked_table; + #ifdef ASSERT bool AOTClassLinker::is_initialized() { assert(CDSConfig::is_dumping_archive(), "AOTClassLinker is for CDS dumping only"); @@ -59,6 +65,8 @@ void AOTClassLinker::initialize() { _candidates = new (mtClass)ClassesTable(); _sorted_candidates = new GrowableArrayCHeap(1000); + _custom_loader_prelinked_table = new (mtClass) ClassLoaderIdToPrelinkedTable(INITIAL_TABLE_SIZE, MAX_TABLE_SIZE); + for (auto id : EnumRange{}) { add_vm_class(vmClasses::klass_at(id)); } @@ -113,6 +121,19 @@ void AOTClassLinker::add_new_candidate(InstanceKlass* ik) { _candidates->put_when_absent(ik, true); _sorted_candidates->append(ik); + Symbol* loader_id; + loader_id = ik->cl_aot_identity(); + if (loader_id != nullptr) { + ClassList** class_list_ptr = _custom_loader_prelinked_table->get(loader_id); + ClassList* class_list = nullptr; + if (class_list_ptr != nullptr) { + class_list = *class_list_ptr; + } else { + class_list = new ClassList(1000); + _custom_loader_prelinked_table->put(loader_id, class_list); + } + class_list->append(ik); + } if (log_is_enabled(Info, aot, link)) { ResourceMark rm; log_info(aot, link)("%s %s %p", class_category_name(ik), ik->external_name(), ik); @@ -127,7 +148,7 @@ bool AOTClassLinker::try_add_candidate(InstanceKlass* ik) { assert(is_initialized(), "sanity"); assert(CDSConfig::is_dumping_aot_linked_classes(), "sanity"); - if (!SystemDictionaryShared::is_builtin(ik)) { + if (!SystemDictionaryShared::is_builtin(ik) && ik->cl_aot_identity() == nullptr) { // not loaded by a class loader which we know about return false; } @@ -190,6 +211,67 @@ void AOTClassLinker::add_candidates() { } } +static inline bool prelinked_table_equals(AOTLinkedClassTableForCustomLoader* table, Symbol* loader_id, int len_unused) { + return table->loader_id()->equals(loader_id); +} + +class ArchivedCustomLoaderPrelinkedTable : public OffsetCompactHashtable {}; +ArchivedCustomLoaderPrelinkedTable _archived_custom_loader_prelinked_table; + +AOTLinkedClassTableForCustomLoader* AOTClassLinker::get_prelinked_table(Symbol* aot_id) { + unsigned int hash = Symbol::symbol_hash(aot_id); + return _archived_custom_loader_prelinked_table.lookup(aot_id, hash, 0 /* ignored */); +} + +void AOTClassLinker::all_symbols_do(MetaspaceClosure* it) { + _custom_loader_prelinked_table->iterate_all([&](Symbol* loader_id, ClassList* class_list) { + it->push(&loader_id); + }); +} + +void AOTClassLinker::serialize_prelinked_table_header(SerializeClosure* soc) { + _archived_custom_loader_prelinked_table.serialize_header(soc); +} + +void AOTClassLinker::print_archived_custom_loader_prelinked_table() { + if (log_is_enabled(Info, aot, link)) { + ResourceMark rm; + _archived_custom_loader_prelinked_table.iterate([&](AOTLinkedClassTableForCustomLoader* table) { + Array* class_list = table->class_list(); + log_info(aot, link)("Class loader \"%s\" has %d classes in prelinked table", table->loader_id()->as_C_string(), class_list->length()); + for (int i = 0; i < class_list->length(); i++) { + InstanceKlass* ik = class_list->at(i); + log_info(aot, link)(" %s", ik->external_name()); + } + }); + } +} + +class CopyPrelinkTableToArchive : StackObj { +private: + CompactHashtableWriter* _writer; + ArchiveBuilder* _builder; +public: + CopyPrelinkTableToArchive(CompactHashtableWriter* writer) : _writer(writer), + _builder(ArchiveBuilder::current()) + {} + + bool do_entry(Symbol* loader_id, ClassList* class_list) { + AOTLinkedClassTableForCustomLoader* tableForLoader = (AOTLinkedClassTableForCustomLoader*)ArchiveBuilder::ro_region_alloc(sizeof(AOTLinkedClassTableForCustomLoader)); + assert(_builder->has_been_archived(loader_id), "must be"); + Symbol* buffered_sym = _builder->get_buffered_addr(loader_id); + tableForLoader->set_loader_id(buffered_sym); + tableForLoader->set_class_list(ArchiveUtils::archive_array(class_list)); + ArchivePtrMarker::mark_pointer(tableForLoader->loader_id_addr()); + ArchivePtrMarker::mark_pointer(tableForLoader->class_list_addr()); + unsigned int hash = Symbol::symbol_hash(loader_id); + u4 delta = _builder->buffer_to_offset_u4((address)tableForLoader); + _writer->add(hash, delta); + return true; + } +}; + void AOTClassLinker::write_to_archive() { assert(is_initialized(), "sanity"); assert_at_safepoint(); @@ -200,6 +282,24 @@ void AOTClassLinker::write_to_archive() { 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)); + + CompactHashtableStats stats; + CompactHashtableWriter writer(_custom_loader_prelinked_table->number_of_entries(), &stats); + CopyPrelinkTableToArchive archiver(&writer); + _custom_loader_prelinked_table->iterate(&archiver); + writer.dump(&_archived_custom_loader_prelinked_table, "archived prelinked table"); + + if (log_is_enabled(Info, aot, link)) { + ResourceMark rm; + _custom_loader_prelinked_table->iterate_all([&](Symbol* loader_id, ClassList* class_list) { + log_info(aot, link)("Class loader \"%s\" has %d classes in prelinked table", loader_id->as_C_string(), class_list->length()); + for (int i = 0; i < class_list->length(); i++) { + InstanceKlass* ik = class_list->at(i); + log_info(aot, link)(" %s", ik->external_name()); + } + return true; + }); + } } } @@ -228,6 +328,8 @@ Array* AOTClassLinker::write_classes(oop class_loader, bool is_j } } + + int AOTClassLinker::num_platform_initiated_classes() { if (CDSConfig::is_dumping_aot_linked_classes()) { // AOTLinkedClassBulkLoader will initiate loading of all public boot classes in the platform loader. @@ -281,6 +383,8 @@ const char* AOTClassLinker::class_category_name(Klass* k) { return "plat"; } else if (loader == SystemDictionary::java_system_loader()) { return "app"; + } else if (k->cl_aot_identity() != nullptr) { + return "aotsafe_custom_lodaer"; } else { return "unreg"; } diff --git a/src/hotspot/share/cds/aotClassLinker.hpp b/src/hotspot/share/cds/aotClassLinker.hpp index 7bbc9ce8138..6a35592b8e6 100644 --- a/src/hotspot/share/cds/aotClassLinker.hpp +++ b/src/hotspot/share/cds/aotClassLinker.hpp @@ -40,6 +40,19 @@ class SerializeClosure; template class Array; enum class AOTLinkedClassCategory : int; +class AOTLinkedClassTableForCustomLoader { +private: + Symbol* _loader_id; + Array* _prelinked_classes; +public: + Symbol* loader_id() const { return _loader_id; } + address* loader_id_addr() const { return (address*)&_loader_id; } + Array* class_list() const { return _prelinked_classes; } + address* class_list_addr() const { return (address*)&_prelinked_classes; } + void set_loader_id(Symbol* id) { _loader_id = id; } + void set_class_list(Array* list) { _prelinked_classes = list; } +}; + // AOTClassLinker is used during the AOTCache Assembly Phase. // It links eligible classes before they are written into the AOTCache // @@ -111,6 +124,11 @@ class AOTClassLinker : AllStatic { static int num_app_initiated_classes(); static int num_platform_initiated_classes(); + static void all_symbols_do(MetaspaceClosure* it); + static void serialize_prelinked_table_header(SerializeClosure* soc); + static void print_archived_custom_loader_prelinked_table(); + static AOTLinkedClassTableForCustomLoader* get_prelinked_table(Symbol* aot_id); + // Used in logging: "boot1", "boot2", "plat", "app" and "unreg"; static const char* class_category_name(AOTLinkedClassCategory category); static const char* class_category_name(Klass* k); diff --git a/src/hotspot/share/cds/aotConstantPoolResolver.cpp b/src/hotspot/share/cds/aotConstantPoolResolver.cpp index 4757619cdb1..bfc7b941e38 100644 --- a/src/hotspot/share/cds/aotConstantPoolResolver.cpp +++ b/src/hotspot/share/cds/aotConstantPoolResolver.cpp @@ -219,7 +219,7 @@ void AOTConstantPoolResolver::resolve_string(constantPoolHandle cp, int cp_index #endif void AOTConstantPoolResolver::preresolve_class_cp_entries(JavaThread* current, InstanceKlass* ik, GrowableArray* preresolve_list) { - if (!SystemDictionaryShared::is_builtin_loader(ik->class_loader_data())) { + if (!SystemDictionaryShared::is_builtin_loader(ik->class_loader_data()) && ik->cl_aot_identity() == nullptr) { return; } diff --git a/src/hotspot/share/cds/aotLinkedClassBulkLoader.cpp b/src/hotspot/share/cds/aotLinkedClassBulkLoader.cpp index 9a8776f4a27..579ee3fffa7 100644 --- a/src/hotspot/share/cds/aotLinkedClassBulkLoader.cpp +++ b/src/hotspot/share/cds/aotLinkedClassBulkLoader.cpp @@ -31,6 +31,7 @@ #include "cds/heapShared.hpp" #include "classfile/classLoaderData.hpp" #include "classfile/classLoaderDataShared.hpp" +#include "classfile/dictionary.hpp" #include "classfile/javaClasses.hpp" #include "classfile/systemDictionary.hpp" #include "classfile/systemDictionaryShared.hpp" @@ -46,6 +47,7 @@ #include "runtime/perfData.inline.hpp" #include "runtime/serviceThread.hpp" #include "utilities/growableArray.hpp" +#include "utilities/resizableHashTable.hpp" static PerfCounter* _perf_classes_preloaded = nullptr; static PerfTickCounters* _perf_class_preload_counters = nullptr; @@ -95,6 +97,70 @@ void AOTLinkedClassBulkLoader::preload_classes_impl(TRAPS) { initiate_loading(THREAD, "app", h_system_loader, table->boot2()); initiate_loading(THREAD, "app", h_system_loader, table->platform()); preload_classes_in_table(table->app(), "app", h_system_loader, CHECK); + + // For each loader load all its classes, and then mark it as the initiating loader + // for the all the classes loaded by its ancentar loaders + GrowableArray custom_loader_objs; + ClassLoaderDataShared::get_archived_custom_loader_objs(custom_loader_objs); + for (int i = 0; i < custom_loader_objs.length(); i++) { + ResourceMark rm; + Handle h_loader = custom_loader_objs.at(i); + Symbol* aot_id = java_lang_ClassLoader::loader_data(h_loader())->aot_identity(); + AOTLinkedClassTableForCustomLoader* table = AOTClassLinker::get_prelinked_table(aot_id); + preload_classes_in_table(table->class_list(), aot_id->as_C_string(), h_loader, CHECK); + } + + ResizeableHashTable processed(10, 100); + MonitorLocker mu1(SystemDictionary_lock); + for (int i = 0; i < custom_loader_objs.length(); i++) { + ResourceMark rm; + Handle h_loader = custom_loader_objs.at(i); + mark_initiating_loader(THREAD, h_loader(), processed); + } +} + +class DictionaryCopier : public KlassClosure { + private: + Dictionary* _dict; + GrowableArray _klasses; + public: + DictionaryCopier(Dictionary* dict) : _dict(dict) {} + void do_klass(Klass* k) { + assert(k->is_instance_klass(), "must be"); + Symbol* name = k->name(); + _klasses.append(InstanceKlass::cast(k)); + } + GrowableArray* klasses() { + return &_klasses; + } +}; + +void AOTLinkedClassBulkLoader::mark_initiating_loader(JavaThread* currentThread, oop loader, ResizeableHashTable& processed) { + if (SystemDictionary::is_builtin_class_loader(loader)) { + //terminating condition: loader is builtin loader for which this computation has already been done + return; + } + ClassLoaderData* cl_data = java_lang_ClassLoader::loader_data(loader); + if (processed.contains(cl_data)) { + return; + } + oop parent = java_lang_ClassLoader::parent(loader); + assert(parent != nullptr, "custom loader's parent loader cannot be null"); + mark_initiating_loader(currentThread, parent, processed); + ClassLoaderData* parent_cl_data = java_lang_ClassLoader::loader_data(parent); + Dictionary* parent_dict = parent_cl_data->dictionary(); + Dictionary* loader_dict = cl_data->dictionary(); + DictionaryCopier copier(loader_dict); + parent_dict->all_entries_do(&copier); + GrowableArray* klasses = copier.klasses(); + for (int i = 0; i < klasses->length(); i++) { + Symbol* name = klasses->at(i)->name(); + if (loader_dict->find_class(currentThread, name) == nullptr) { + loader_dict->add_klass(currentThread, name, klasses->at(i)); + } + } + bool created; + processed.put_if_absent(cl_data, &created); } void AOTLinkedClassBulkLoader::preload_classes_in_table(Array* classes, diff --git a/src/hotspot/share/cds/aotLinkedClassBulkLoader.hpp b/src/hotspot/share/cds/aotLinkedClassBulkLoader.hpp index 07727782674..6d298d052c6 100644 --- a/src/hotspot/share/cds/aotLinkedClassBulkLoader.hpp +++ b/src/hotspot/share/cds/aotLinkedClassBulkLoader.hpp @@ -76,6 +76,7 @@ class AOTLinkedClassBulkLoader : AllStatic { static void replay_training_at_init_for_preloaded_classes(TRAPS) NOT_CDS_RETURN; static void print_counters_on(outputStream* st) NOT_CDS_RETURN; + static void mark_initiating_loader(JavaThread* currentThread, oop loader, ResizeableHashTable& processed); }; #endif // SHARE_CDS_AOTLINKEDCLASSBULKLOADER_HPP diff --git a/src/hotspot/share/cds/aotMetaspace.cpp b/src/hotspot/share/cds/aotMetaspace.cpp index 884ba5bb7a5..f53820a8198 100644 --- a/src/hotspot/share/cds/aotMetaspace.cpp +++ b/src/hotspot/share/cds/aotMetaspace.cpp @@ -530,6 +530,7 @@ void AOTMetaspace::serialize(SerializeClosure* soc) { HeapShared::serialize_tables(soc); SystemDictionaryShared::serialize_dictionary_headers(soc); AOTLinkedClassBulkLoader::serialize(soc); + AOTClassLinker::serialize_prelinked_table_header(soc); FinalImageRecipes::serialize(soc); TrainingData::serialize(soc); InstanceMirrorKlass::serialize_offsets(soc); @@ -717,6 +718,7 @@ class StaticArchiveBuilder : public ArchiveBuilder { for (int i = 0; i < _pending_method_handle_intrinsics->length(); i++) { it->push(_pending_method_handle_intrinsics->adr_at(i)); } + AOTClassLinker::all_symbols_do(it); } }; diff --git a/src/hotspot/share/cds/archiveUtils.hpp b/src/hotspot/share/cds/archiveUtils.hpp index e5cea656a0f..59e39e1ce18 100644 --- a/src/hotspot/share/cds/archiveUtils.hpp +++ b/src/hotspot/share/cds/archiveUtils.hpp @@ -262,8 +262,8 @@ class ReadClosure : public SerializeClosure { }; class ArchiveUtils { - template static Array* archive_non_ptr_array(GrowableArray* tmp_array); - template static Array* archive_ptr_array(GrowableArray* tmp_array); + template static Array* archive_non_ptr_array(GrowableArrayView* tmp_array); + template static Array* archive_ptr_array(GrowableArrayView* tmp_array); public: static const uintx MAX_SHARED_DELTA = 0x7FFFFFFF; @@ -271,12 +271,12 @@ class ArchiveUtils { static bool has_aot_initialized_mirror(InstanceKlass* src_ik); template ::value)> - static Array* archive_array(GrowableArray* tmp_array) { + static Array* archive_array(GrowableArrayView* tmp_array) { return archive_non_ptr_array(tmp_array); } template ::value)> - static Array* archive_array(GrowableArray* tmp_array) { + static Array* archive_array(GrowableArrayView* tmp_array) { return archive_ptr_array(tmp_array); } diff --git a/src/hotspot/share/cds/archiveUtils.inline.hpp b/src/hotspot/share/cds/archiveUtils.inline.hpp index 6f635d01745..75e998fccf0 100644 --- a/src/hotspot/share/cds/archiveUtils.inline.hpp +++ b/src/hotspot/share/cds/archiveUtils.inline.hpp @@ -55,7 +55,7 @@ inline bool SharedDataRelocator::do_bit(size_t offset) { // Returns the address of an Array that's allocated in the ArchiveBuilder "buffer" space. template -Array* ArchiveUtils::archive_non_ptr_array(GrowableArray* tmp_array) { +Array* ArchiveUtils::archive_non_ptr_array(GrowableArrayView* tmp_array) { ArchiveBuilder* builder = ArchiveBuilder::current(); Array* archived_array = ArchiveBuilder::new_ro_array(tmp_array->length()); @@ -72,7 +72,7 @@ Array* ArchiveUtils::archive_non_ptr_array(GrowableArray* tmp_array) { // - a source object that has been archived; or // - (only when dumping dynamic archive) an object in the static archive. template -Array* ArchiveUtils::archive_ptr_array(GrowableArray* tmp_array) { +Array* ArchiveUtils::archive_ptr_array(GrowableArrayView* tmp_array) { ArchiveBuilder* builder = ArchiveBuilder::current(); const bool is_dynamic_dump = CDSConfig::is_dumping_dynamic_archive(); diff --git a/src/hotspot/share/cds/finalImageRecipes.cpp b/src/hotspot/share/cds/finalImageRecipes.cpp index 5d52c3675d9..59cef4751a8 100644 --- a/src/hotspot/share/cds/finalImageRecipes.cpp +++ b/src/hotspot/share/cds/finalImageRecipes.cpp @@ -23,6 +23,7 @@ */ #include "cds/aotConstantPoolResolver.hpp" +#include "cds/aotLinkedClassBulkLoader.hpp" #include "cds/archiveBuilder.hpp" #include "cds/archiveUtils.inline.hpp" #include "cds/cdsConfig.hpp" @@ -38,6 +39,7 @@ #include "oops/symbol.hpp" #include "runtime/handles.inline.hpp" #include "runtime/mutexLocker.hpp" +#include "utilities/resizableHashTable.hpp" GrowableArray* FinalImageRecipes::_tmp_reflect_klasses = nullptr; GrowableArray* FinalImageRecipes::_tmp_reflect_flags = nullptr; @@ -305,6 +307,14 @@ void FinalImageRecipes::load_all_classes(TRAPS) { } } } + ResizeableHashTable processed(10, 100); + MonitorLocker mu1(SystemDictionary_lock); + _aot_safe_custom_loaders_map->iterate([&](Symbol* aot_id, OopHandle h_loader) { + ResourceMark rm; + oop loader = h_loader.resolve();; + AOTLinkedClassBulkLoader::mark_initiating_loader(THREAD, loader, processed); + return true; + }); } } diff --git a/src/hotspot/share/classfile/classLoaderDataShared.cpp b/src/hotspot/share/classfile/classLoaderDataShared.cpp index 59de4c8829c..8764216d77d 100644 --- a/src/hotspot/share/classfile/classLoaderDataShared.cpp +++ b/src/hotspot/share/classfile/classLoaderDataShared.cpp @@ -44,12 +44,26 @@ bool ClassLoaderDataShared::_full_module_graph_loaded = false; +static ClassLoaderData* null_class_loader_data() { + ClassLoaderData* loader_data = ClassLoaderData::the_null_class_loader_data(); + assert(loader_data != nullptr, "must be"); + return loader_data; +} + +static ClassLoaderData* java_platform_loader_data_or_null() { + return ClassLoaderData::class_loader_data_or_null(SystemDictionary::java_platform_loader()); +} + +static ClassLoaderData* java_system_loader_data_or_null() { + return ClassLoaderData::class_loader_data_or_null(SystemDictionary::java_system_loader()); +} + class ArchivedClassLoaderData { Array* _packages; Array* _modules; ModuleEntry* _unnamed_module; Symbol* _aot_identity; - + int _loader_obj_index; void assert_valid(ClassLoaderData* loader_data) { // loader_data may be null if the boot layer has loaded no modules for the platform or // system loaders (e.g., if you create a custom JDK image with only java.base). @@ -59,7 +73,7 @@ class ArchivedClassLoaderData { } } public: - ArchivedClassLoaderData() : _packages(nullptr), _modules(nullptr), _unnamed_module(nullptr) {} + ArchivedClassLoaderData() : _packages(nullptr), _modules(nullptr), _unnamed_module(nullptr), _aot_identity(nullptr), _loader_obj_index(-1) {} void iterate_symbols(ClassLoaderData* loader_data, MetaspaceClosure* closure); void allocate(ClassLoaderData* loader_data); @@ -68,11 +82,14 @@ class ArchivedClassLoaderData { return _unnamed_module; } Symbol* aot_identity() { return _aot_identity; } + int loader_obj_index() { return _loader_obj_index; } void serialize(SerializeClosure* f) { f->do_ptr(&_packages); f->do_ptr(&_modules); f->do_ptr(&_unnamed_module); + f->do_ptr(&_aot_identity); + f->do_int(&_loader_obj_index); } void restore(ClassLoaderData* loader_data, bool do_entries, bool do_oops); @@ -120,6 +137,12 @@ void ArchivedClassLoaderData::allocate(ClassLoaderData* loader_data) { if (!loader_data->is_builtin_class_loader_data()) { _aot_identity = loader_data->aot_identity(); } + if (!loader_data->class_loader_handle().is_empty()) { + _loader_obj_index = HeapShared::append_root(loader_data->class_loader_handle().resolve()); + } else { + assert(loader_data == null_class_loader_data(), "sanity check"); + _loader_obj_index = -1; + } ArchivePtrMarker::mark_pointer((address*)&_packages); ArchivePtrMarker::mark_pointer((address*)&_modules); ArchivePtrMarker::mark_pointer((address*)&_unnamed_module); @@ -211,20 +234,6 @@ void ClassLoaderDataShared::load_archived_platform_and_system_class_loaders() { #endif } -static ClassLoaderData* null_class_loader_data() { - ClassLoaderData* loader_data = ClassLoaderData::the_null_class_loader_data(); - assert(loader_data != nullptr, "must be"); - return loader_data; -} - -static ClassLoaderData* java_platform_loader_data_or_null() { - return ClassLoaderData::class_loader_data_or_null(SystemDictionary::java_platform_loader()); -} - -static ClassLoaderData* java_system_loader_data_or_null() { - return ClassLoaderData::class_loader_data_or_null(SystemDictionary::java_system_loader()); -} - // ModuleEntryTables (even if empty) are required for iterate_symbols() to scan the // platform/system loaders inside the CDS safepoint, but the tables can be created only // when outside of safepoints. Let's do that now. @@ -446,6 +455,12 @@ void ClassLoaderDataShared::restore_archived_modules_for_preloading_classes(Java 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); + + _aotid_to_archived_cld_map.iterate([&](ArchivedClassLoaderData* archived_cld) { + Handle h_loader(current, HeapShared::get_root(archived_cld->loader_obj_index())); + ClassLoaderData* runtime_cld = SystemDictionary::register_loader(h_loader); + ClassLoaderDataShared::restore_custom_loader_archived_data(runtime_cld); + }); } void ClassLoaderDataShared::restore_custom_loader_archived_data(ClassLoaderData* loader_data) { @@ -465,4 +480,12 @@ void ClassLoaderDataShared::restore_custom_loader_archived_data(ClassLoaderData* loader_data->set_restored(true); } +void ClassLoaderDataShared::get_archived_custom_loader_objs(GrowableArray& loader_obj_list) { + // TODO: assert that it is called in production run only + _aotid_to_archived_cld_map.iterate([&](ArchivedClassLoaderData* archived_cld) { + Handle h_loader(Thread::current(), HeapShared::get_root(archived_cld->loader_obj_index())); + loader_obj_list.append(h_loader); + }); +} + #endif // INCLUDE_CDS_JAVA_HEAP diff --git a/src/hotspot/share/classfile/classLoaderDataShared.hpp b/src/hotspot/share/classfile/classLoaderDataShared.hpp index 9de92e6c0d2..bbe27aa81f0 100644 --- a/src/hotspot/share/classfile/classLoaderDataShared.hpp +++ b/src/hotspot/share/classfile/classLoaderDataShared.hpp @@ -57,6 +57,7 @@ class ClassLoaderDataShared : AllStatic { static bool is_full_module_graph_loaded() { return _full_module_graph_loaded; } static void write_cld_table(); static void restore_custom_loader_archived_data(ClassLoaderData* loader_data); + static void get_archived_custom_loader_objs(GrowableArray& loader_obj_list); }; #endif // SHARE_CLASSFILE_CLASSLOADERDATASHARED_HPP diff --git a/src/hotspot/share/classfile/systemDictionary.cpp b/src/hotspot/share/classfile/systemDictionary.cpp index 7f8369f0b91..328b7a4481b 100644 --- a/src/hotspot/share/classfile/systemDictionary.cpp +++ b/src/hotspot/share/classfile/systemDictionary.cpp @@ -1174,7 +1174,7 @@ void SystemDictionary::load_shared_class_misc(InstanceKlass* ik, ClassLoaderData void SystemDictionary::preload_class(Handle class_loader, InstanceKlass* ik, TRAPS) { precond(Universe::is_bootstrapping()); precond(java_platform_loader() != nullptr && java_system_loader() != nullptr); - precond(class_loader() == nullptr || class_loader() == java_platform_loader() ||class_loader() == java_system_loader()); + precond(class_loader() == nullptr || class_loader() == java_platform_loader() ||class_loader() == java_system_loader() || ik->cl_aot_identity() != nullptr); precond(CDSConfig::is_using_aot_linked_classes()); precond(AOTMetaspace::in_aot_cache_static_region((void*)ik)); precond(!ik->is_loaded()); @@ -1196,7 +1196,7 @@ void SystemDictionary::preload_class(Handle class_loader, InstanceKlass* ik, TRA ClassLoaderData* loader_data = ClassLoaderData::class_loader_data(class_loader()); oop java_mirror = ik->archived_java_mirror(); precond(java_mirror != nullptr); - assert(java_lang_Class::module(java_mirror) != nullptr, "must have been archived"); + //assert(java_lang_Class::module(java_mirror) != nullptr, "must have been archived"); Handle pd(THREAD, java_lang_Class::protection_domain(java_mirror)); PackageEntry* pkg_entry = ik->package(); From 1e6e86695e1cf4f942b57a91d191ed50adbaa4ab Mon Sep 17 00:00:00 2001 From: Ashutosh Mehra Date: Wed, 28 Jan 2026 18:15:08 -0500 Subject: [PATCH 04/30] Use CDS$UnregisteredClassLoader to load aot-safe custom loader classes in the assembly phase Signed-off-by: Ashutosh Mehra --- src/hotspot/share/cds/aotClassLinker.cpp | 3 +- src/hotspot/share/cds/aotClassLinker.hpp | 10 ++++- .../share/cds/aotConstantPoolResolver.cpp | 5 ++- .../share/cds/aotLinkedClassBulkLoader.cpp | 43 +++++++++++++++++++ .../share/cds/aotLinkedClassBulkLoader.hpp | 4 ++ src/hotspot/share/cds/aotMetaspace.cpp | 2 + src/hotspot/share/cds/finalImageRecipes.cpp | 9 ++++ src/hotspot/share/cds/heapShared.cpp | 4 +- src/hotspot/share/cds/unregisteredClasses.cpp | 4 ++ src/hotspot/share/cds/unregisteredClasses.hpp | 1 + .../share/classfile/classFileParser.cpp | 3 ++ .../share/classfile/classLoaderDataGraph.hpp | 2 +- .../classfile/classLoaderDataGraph.inline.hpp | 4 +- .../share/classfile/classLoaderDataShared.cpp | 16 ++++--- src/hotspot/share/classfile/javaClasses.cpp | 4 +- .../share/classfile/systemDictionary.cpp | 36 +++++++++++----- .../classfile/systemDictionaryShared.cpp | 32 ++++++++------ src/hotspot/share/oops/klass.hpp | 4 +- 18 files changed, 144 insertions(+), 42 deletions(-) diff --git a/src/hotspot/share/cds/aotClassLinker.cpp b/src/hotspot/share/cds/aotClassLinker.cpp index 8940be1d91e..7e379979e9b 100644 --- a/src/hotspot/share/cds/aotClassLinker.cpp +++ b/src/hotspot/share/cds/aotClassLinker.cpp @@ -261,8 +261,7 @@ class CopyPrelinkTableToArchive : StackObj { AOTLinkedClassTableForCustomLoader* tableForLoader = (AOTLinkedClassTableForCustomLoader*)ArchiveBuilder::ro_region_alloc(sizeof(AOTLinkedClassTableForCustomLoader)); assert(_builder->has_been_archived(loader_id), "must be"); Symbol* buffered_sym = _builder->get_buffered_addr(loader_id); - tableForLoader->set_loader_id(buffered_sym); - tableForLoader->set_class_list(ArchiveUtils::archive_array(class_list)); + tableForLoader->init(buffered_sym, ArchiveUtils::archive_array(class_list)); ArchivePtrMarker::mark_pointer(tableForLoader->loader_id_addr()); ArchivePtrMarker::mark_pointer(tableForLoader->class_list_addr()); unsigned int hash = Symbol::symbol_hash(loader_id); diff --git a/src/hotspot/share/cds/aotClassLinker.hpp b/src/hotspot/share/cds/aotClassLinker.hpp index 6a35592b8e6..cee46b0dad6 100644 --- a/src/hotspot/share/cds/aotClassLinker.hpp +++ b/src/hotspot/share/cds/aotClassLinker.hpp @@ -44,13 +44,19 @@ class AOTLinkedClassTableForCustomLoader { private: Symbol* _loader_id; Array* _prelinked_classes; + bool _loaded; public: + void init(Symbol* aot_id, Array* class_list) { + _loader_id = aot_id; + _prelinked_classes = class_list; + _loaded = false; + } Symbol* loader_id() const { return _loader_id; } address* loader_id_addr() const { return (address*)&_loader_id; } Array* class_list() const { return _prelinked_classes; } address* class_list_addr() const { return (address*)&_prelinked_classes; } - void set_loader_id(Symbol* id) { _loader_id = id; } - void set_class_list(Array* list) { _prelinked_classes = list; } + void set_loaded(bool value) { _loaded = value; } + bool is_loaded() const { return _loaded; } }; // AOTClassLinker is used during the AOTCache Assembly Phase. diff --git a/src/hotspot/share/cds/aotConstantPoolResolver.cpp b/src/hotspot/share/cds/aotConstantPoolResolver.cpp index bfc7b941e38..aa49fd3ba6a 100644 --- a/src/hotspot/share/cds/aotConstantPoolResolver.cpp +++ b/src/hotspot/share/cds/aotConstantPoolResolver.cpp @@ -33,6 +33,7 @@ #include "cds/finalImageRecipes.hpp" #include "cds/heapShared.hpp" #include "cds/lambdaFormInvokers.inline.hpp" +#include "cds/unregisteredClasses.hpp" #include "classfile/classLoader.hpp" #include "classfile/dictionary.hpp" #include "classfile/symbolTable.hpp" @@ -190,7 +191,9 @@ Klass* AOTConstantPoolResolver::find_loaded_class(Thread* current, oop class_loa if (k != nullptr) { return k; } - if (h_loader() == SystemDictionary::java_system_loader()) { + if (h_loader() == UnregisteredClasses::unregistered_class_loader(current)()) { + return find_loaded_class(current, SystemDictionary::java_system_loader(), name); + } else if (h_loader() == SystemDictionary::java_system_loader()) { return find_loaded_class(current, SystemDictionary::java_platform_loader(), name); } else if (h_loader() == SystemDictionary::java_platform_loader()) { return find_loaded_class(current, nullptr, name); diff --git a/src/hotspot/share/cds/aotLinkedClassBulkLoader.cpp b/src/hotspot/share/cds/aotLinkedClassBulkLoader.cpp index 579ee3fffa7..a753453cd09 100644 --- a/src/hotspot/share/cds/aotLinkedClassBulkLoader.cpp +++ b/src/hotspot/share/cds/aotLinkedClassBulkLoader.cpp @@ -98,6 +98,7 @@ void AOTLinkedClassBulkLoader::preload_classes_impl(TRAPS) { initiate_loading(THREAD, "app", h_system_loader, table->platform()); preload_classes_in_table(table->app(), "app", h_system_loader, CHECK); +#if 0 // For each loader load all its classes, and then mark it as the initiating loader // for the all the classes loaded by its ancentar loaders GrowableArray custom_loader_objs; @@ -117,6 +118,29 @@ void AOTLinkedClassBulkLoader::preload_classes_impl(TRAPS) { Handle h_loader = custom_loader_objs.at(i); mark_initiating_loader(THREAD, h_loader(), processed); } +#endif +} + +void AOTLinkedClassBulkLoader::preload_classes_for_loader(ClassLoaderData* loader_data, TRAPS) { + Handle h_loader(THREAD, loader_data->class_loader_handle().resolve()); + preload_classes_for_loader_impl(h_loader, CHECK); +} + +void AOTLinkedClassBulkLoader::preload_classes_for_loader_impl(Handle loader_obj, TRAPS) { + if (SystemDictionary::is_builtin_class_loader(loader_obj())) { + // classes for builtin loaders should have already been loaded during startup + return; + } + Symbol* aot_id = java_lang_ClassLoader::loader_data(loader_obj())->aot_identity(); + AOTLinkedClassTableForCustomLoader* table = AOTClassLinker::get_prelinked_table(aot_id); + if (table->is_loaded()) { + return; + } + oop parent = java_lang_ClassLoader::parent(loader_obj()); + preload_classes_for_loader_impl(Handle(THREAD, parent), CHECK); + preload_classes_in_table(table->class_list(), aot_id->as_C_string(), loader_obj, CHECK); + MonitorLocker mu1(SystemDictionary_lock); + mark_initiating_loader(THREAD, loader_obj()); } class DictionaryCopier : public KlassClosure { @@ -135,6 +159,25 @@ class DictionaryCopier : public KlassClosure { } }; +void AOTLinkedClassBulkLoader::mark_initiating_loader(JavaThread* currentThread, oop loader) { + assert(!SystemDictionary::is_builtin_class_loader(loader), "does not work for built-in loaders"); + ClassLoaderData* cl_data = java_lang_ClassLoader::loader_data(loader); + oop parent = java_lang_ClassLoader::parent(loader); + assert(parent != nullptr, "custom loader's parent loader cannot be null"); + ClassLoaderData* parent_cl_data = java_lang_ClassLoader::loader_data(parent); + Dictionary* parent_dict = parent_cl_data->dictionary(); + Dictionary* loader_dict = cl_data->dictionary(); + DictionaryCopier copier(loader_dict); + parent_dict->all_entries_do(&copier); + GrowableArray* klasses = copier.klasses(); + for (int i = 0; i < klasses->length(); i++) { + Symbol* name = klasses->at(i)->name(); + if (loader_dict->find_class(currentThread, name) == nullptr) { + loader_dict->add_klass(currentThread, name, klasses->at(i)); + } + } +} + void AOTLinkedClassBulkLoader::mark_initiating_loader(JavaThread* currentThread, oop loader, ResizeableHashTable& processed) { if (SystemDictionary::is_builtin_class_loader(loader)) { //terminating condition: loader is builtin loader for which this computation has already been done diff --git a/src/hotspot/share/cds/aotLinkedClassBulkLoader.hpp b/src/hotspot/share/cds/aotLinkedClassBulkLoader.hpp index 6d298d052c6..53188b8cb75 100644 --- a/src/hotspot/share/cds/aotLinkedClassBulkLoader.hpp +++ b/src/hotspot/share/cds/aotLinkedClassBulkLoader.hpp @@ -77,6 +77,10 @@ class AOTLinkedClassBulkLoader : AllStatic { static void replay_training_at_init_for_preloaded_classes(TRAPS) NOT_CDS_RETURN; static void print_counters_on(outputStream* st) NOT_CDS_RETURN; static void mark_initiating_loader(JavaThread* currentThread, oop loader, ResizeableHashTable& processed); + + static void preload_classes_for_loader(ClassLoaderData* loader_data, TRAPS); + static void preload_classes_for_loader_impl(Handle loader_obj, TRAPS); + static void mark_initiating_loader(JavaThread* currentThread, oop loader); }; #endif // SHARE_CDS_AOTLINKEDCLASSBULKLOADER_HPP diff --git a/src/hotspot/share/cds/aotMetaspace.cpp b/src/hotspot/share/cds/aotMetaspace.cpp index f53820a8198..e28849df03e 100644 --- a/src/hotspot/share/cds/aotMetaspace.cpp +++ b/src/hotspot/share/cds/aotMetaspace.cpp @@ -737,9 +737,11 @@ char* VM_PopulateDumpSharedSpace::dump_early_read_only_tables() { char* VM_PopulateDumpSharedSpace::dump_read_only_tables(AOTClassLocationConfig*& cl_config) { ArchiveBuilder::OtherROAllocMark mark; +#if 0 if (CDSConfig::is_dumping_full_module_graph()) { ClassLoaderDataShared::write_cld_table(); } +#endif SystemDictionaryShared::write_to_archive(); cl_config = AOTClassLocationConfig::dumptime()->write_to_archive(); AOTClassLinker::write_to_archive(); diff --git a/src/hotspot/share/cds/finalImageRecipes.cpp b/src/hotspot/share/cds/finalImageRecipes.cpp index 59cef4751a8..3018a1022ae 100644 --- a/src/hotspot/share/cds/finalImageRecipes.cpp +++ b/src/hotspot/share/cds/finalImageRecipes.cpp @@ -28,6 +28,7 @@ #include "cds/archiveUtils.inline.hpp" #include "cds/cdsConfig.hpp" #include "cds/finalImageRecipes.hpp" +#include "cds/unregisteredClasses.hpp" #include "classfile/classLoader.hpp" #include "classfile/javaClasses.hpp" #include "classfile/systemDictionary.hpp" @@ -257,6 +258,7 @@ void FinalImageRecipes::record_recipes_for_dynamic_proxies() { void FinalImageRecipes::load_all_classes(TRAPS) { assert(CDSConfig::is_dumping_final_static_archive(), "sanity"); + UnregisteredClasses::initialize(CHECK); Handle class_loader(THREAD, SystemDictionary::java_system_loader()); for (int i = 0; i < _all_klasses->length(); i++) { Klass* k = _all_klasses->at(i); @@ -283,10 +285,16 @@ void FinalImageRecipes::load_all_classes(TRAPS) { //assert(ik->class_loader() == nullptr, "supported only for boot classes for now"); ik->initialize(CHECK); } + } else if (k->is_defined_by_aot_safe_custom_loader()) { + // Use UnregisteredClassLoader to load these classes + Handle unreg_class_loader = UnregisteredClasses::unregistered_class_loader(THREAD); + assert(unreg_class_loader.not_null(), "must be"); + Klass* actual = SystemDictionary::resolve_or_fail(ik->name(), unreg_class_loader, true, CHECK); } } } +#if 0 if (CDSConfig::supports_custom_loaders()) { // Custom Loaders must be marked with AOTSafeClassInitializer annotation. // They would have been created as part of running class initializer for custom loaders. @@ -316,6 +324,7 @@ void FinalImageRecipes::load_all_classes(TRAPS) { return true; }); } +#endif } void FinalImageRecipes::apply_recipes_for_reflection_data(JavaThread* current) { diff --git a/src/hotspot/share/cds/heapShared.cpp b/src/hotspot/share/cds/heapShared.cpp index b8399962a65..26e42c7ac7a 100644 --- a/src/hotspot/share/cds/heapShared.cpp +++ b/src/hotspot/share/cds/heapShared.cpp @@ -696,8 +696,8 @@ bool HeapShared::archive_object(oop obj, oop referrer, KlassSubGraphInfo* subgra ResourceMark rm; LogTarget(Debug, aot, heap) log; LogStream out(log); - out.print("Archived heap object " PTR_FORMAT " : %s ", - p2i(obj), obj->klass()->external_name()); + out.print("Archived heap object " PTR_FORMAT " : %s, referrer: " PTR_FORMAT, + p2i(obj), obj->klass()->external_name(), p2i(referrer)); if (java_lang_Class::is_instance(obj)) { Klass* k = java_lang_Class::as_Klass(obj); if (k != nullptr) { diff --git a/src/hotspot/share/cds/unregisteredClasses.cpp b/src/hotspot/share/cds/unregisteredClasses.cpp index 51b35899599..9de0abfd214 100644 --- a/src/hotspot/share/cds/unregisteredClasses.cpp +++ b/src/hotspot/share/cds/unregisteredClasses.cpp @@ -63,6 +63,10 @@ void UnregisteredClasses::initialize(TRAPS) { _unregistered_class_loader = OopHandle(Universe::vm_global(), cl()); } +Handle UnregisteredClasses::unregistered_class_loader(Thread* current) { + return Handle(current, _unregistered_class_loader.resolve()); +} + // Load the class of the given name from the location given by path. The path is specified by // the "source:" in the class list file (see classListParser.cpp), and can be a directory or // a JAR file. diff --git a/src/hotspot/share/cds/unregisteredClasses.hpp b/src/hotspot/share/cds/unregisteredClasses.hpp index d61c3462504..28b3af400c0 100644 --- a/src/hotspot/share/cds/unregisteredClasses.hpp +++ b/src/hotspot/share/cds/unregisteredClasses.hpp @@ -37,6 +37,7 @@ class UnregisteredClasses: AllStatic { static void initialize(TRAPS); // Returns true if the class is loaded internally for dumping unregistered classes. static bool check_for_exclusion(const InstanceKlass* k); + static Handle unregistered_class_loader(Thread* current); }; #endif // SHARE_CDS_UNREGISTEREDCLASSES_HPP diff --git a/src/hotspot/share/classfile/classFileParser.cpp b/src/hotspot/share/classfile/classFileParser.cpp index 1da22ec8d60..8ccbeb41dbb 100644 --- a/src/hotspot/share/classfile/classFileParser.cpp +++ b/src/hotspot/share/classfile/classFileParser.cpp @@ -5067,6 +5067,9 @@ void ClassFileParser::fill_instance_klass(InstanceKlass* ik, // Set name and CLD before adding to CLD ik->set_class_loader_data(_loader_data); + if (CDSConfig::supports_custom_loaders() && _loader_data != nullptr) { + ik->set_cl_aot_identity(_loader_data->aot_identity()); + } ik->set_class_loader_type(); ik->set_name(_class_name); diff --git a/src/hotspot/share/classfile/classLoaderDataGraph.hpp b/src/hotspot/share/classfile/classLoaderDataGraph.hpp index 803f227dcf3..3f2cb82fa25 100644 --- a/src/hotspot/share/classfile/classLoaderDataGraph.hpp +++ b/src/hotspot/share/classfile/classLoaderDataGraph.hpp @@ -57,7 +57,7 @@ class ClassLoaderDataGraph : public AllStatic { static ClassLoaderData* add_to_graph(Handle class_loader, bool has_class_mirror_holder); public: - static ClassLoaderData* find_or_create(Handle class_loader); + static ClassLoaderData* find_or_create(Handle class_loader, bool& created); static ClassLoaderData* add(Handle class_loader, bool has_class_mirror_holder); static void clean_module_and_package_info(); static void purge(bool at_safepoint); diff --git a/src/hotspot/share/classfile/classLoaderDataGraph.inline.hpp b/src/hotspot/share/classfile/classLoaderDataGraph.inline.hpp index 767db20a8f0..3c846cd222c 100644 --- a/src/hotspot/share/classfile/classLoaderDataGraph.inline.hpp +++ b/src/hotspot/share/classfile/classLoaderDataGraph.inline.hpp @@ -32,12 +32,14 @@ #include "runtime/atomicAccess.hpp" #include "runtime/orderAccess.hpp" -inline ClassLoaderData *ClassLoaderDataGraph::find_or_create(Handle loader) { +inline ClassLoaderData *ClassLoaderDataGraph::find_or_create(Handle loader, bool& created) { guarantee(loader() != nullptr && oopDesc::is_oop(loader()), "Loader must be oop"); // Gets the class loader data out of the java/lang/ClassLoader object, if non-null // it's already in the loader_data, so no need to add + created = true; ClassLoaderData* loader_data = java_lang_ClassLoader::loader_data_acquire(loader()); if (loader_data) { + created = false; return loader_data; } return ClassLoaderDataGraph::add(loader, false); diff --git a/src/hotspot/share/classfile/classLoaderDataShared.cpp b/src/hotspot/share/classfile/classLoaderDataShared.cpp index 8764216d77d..55e626a0bb0 100644 --- a/src/hotspot/share/classfile/classLoaderDataShared.cpp +++ b/src/hotspot/share/classfile/classLoaderDataShared.cpp @@ -294,11 +294,13 @@ void ClassLoaderDataShared::allocate_archived_tables() { _archived_platform_loader_data.allocate(java_platform_loader_data_or_null()); _archived_system_loader_data.allocate (java_system_loader_data_or_null()); +#if 0 _archived_cld_to_runtime_cld_map = new (mtClassShared) ArchivedCLDToCLDMap(); if (CDSConfig::supports_custom_loaders()) { ArchiveAOTCompatibleLoaders archive_closure; ClassLoaderDataGraph::cld_do(&archive_closure); } +#endif } void ClassLoaderDataShared::init_archived_tables() { @@ -306,11 +308,11 @@ void ClassLoaderDataShared::init_archived_tables() { _archived_boot_loader_data.init_archived_entries (null_class_loader_data()); _archived_platform_loader_data.init_archived_entries(java_platform_loader_data_or_null()); _archived_system_loader_data.init_archived_entries (java_system_loader_data_or_null()); - +#if 0 _archived_cld_to_runtime_cld_map->iterate_all([&](ArchivedClassLoaderData* &archived_cld, ClassLoaderData* &cld) { archived_cld->init_archived_entries(cld); }); - +#endif _archived_javabase_moduleEntry = ModuleEntry::get_archived_entry(ModuleEntryTable::javabase_moduleEntry()); _platform_loader_root_index = HeapShared::append_root(SystemDictionary::java_platform_loader()); @@ -341,7 +343,7 @@ void ClassLoaderDataShared::serialize(SerializeClosure* f) { _archived_boot_loader_data.serialize(f); _archived_platform_loader_data.serialize(f); _archived_system_loader_data.serialize(f); - _aotid_to_archived_cld_map.serialize_header(f); + //_aotid_to_archived_cld_map.serialize_header(f); f->do_ptr(&_archived_javabase_moduleEntry); f->do_int(&_platform_loader_root_index); f->do_int(&_system_loader_root_index); @@ -375,12 +377,14 @@ ModuleEntry* ClassLoaderDataShared::archived_unnamed_module(ClassLoaderData* loa } else if (loader_data->class_loader() == HeapShared::get_root(_system_loader_root_index)) { archived_module = _archived_system_loader_data.unnamed_module(); } +#if 0 } else if (loader_data->aot_identity() != nullptr) { unsigned int hash = Symbol::symbol_hash(loader_data->aot_identity()); ArchivedClassLoaderData* archived_cld = _aotid_to_archived_cld_map.lookup(loader_data->aot_identity(), hash, 0); if (archived_cld != nullptr) { archived_module = archived_cld->unnamed_module(); } +#endif } } return archived_module; @@ -391,10 +395,11 @@ void ClassLoaderDataShared::clear_archived_oops() { _archived_boot_loader_data.clear_archived_oops(); _archived_platform_loader_data.clear_archived_oops(); _archived_system_loader_data.clear_archived_oops(); - +#if 0 _aotid_to_archived_cld_map.iterate([&](ArchivedClassLoaderData* archived_cld) { archived_cld->clear_archived_oops(); }); +#endif if (_platform_loader_root_index >= 0) { HeapShared::clear_root(_platform_loader_root_index); HeapShared::clear_root(_system_loader_root_index); @@ -455,12 +460,13 @@ void ClassLoaderDataShared::restore_archived_modules_for_preloading_classes(Java 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); - +#if 0 _aotid_to_archived_cld_map.iterate([&](ArchivedClassLoaderData* archived_cld) { Handle h_loader(current, HeapShared::get_root(archived_cld->loader_obj_index())); ClassLoaderData* runtime_cld = SystemDictionary::register_loader(h_loader); ClassLoaderDataShared::restore_custom_loader_archived_data(runtime_cld); }); +#endif } void ClassLoaderDataShared::restore_custom_loader_archived_data(ClassLoaderData* loader_data) { diff --git a/src/hotspot/share/classfile/javaClasses.cpp b/src/hotspot/share/classfile/javaClasses.cpp index 6ce3f727076..cab2c72c8fc 100644 --- a/src/hotspot/share/classfile/javaClasses.cpp +++ b/src/hotspot/share/classfile/javaClasses.cpp @@ -1195,8 +1195,8 @@ void java_lang_Class::create_mirror(Klass* k, Handle class_loader, // latter may contain dumptime-specific information that cannot be archived // (e.g., ClassLoaderData*, or static fields that are modified by Java code execution). void java_lang_Class::create_scratch_mirror(Klass* k, TRAPS) { - if (k->cl_aot_identity() == nullptr && - k->class_loader() != nullptr && + //if (k->cl_aot_identity() == nullptr && + if (k->class_loader() != nullptr && k->class_loader() != SystemDictionary::java_platform_loader() && k->class_loader() != SystemDictionary::java_system_loader()) { // We only archive the mirrors of classes loaded by the built-in loaders diff --git a/src/hotspot/share/classfile/systemDictionary.cpp b/src/hotspot/share/classfile/systemDictionary.cpp index 328b7a4481b..9c009066ffb 100644 --- a/src/hotspot/share/classfile/systemDictionary.cpp +++ b/src/hotspot/share/classfile/systemDictionary.cpp @@ -23,6 +23,7 @@ */ #include "cds/aotClassLocation.hpp" +#include "cds/aotLinkedClassBulkLoader.hpp" #include "cds/cdsConfig.hpp" #include "cds/heapShared.hpp" #include "classfile/classFileParser.hpp" @@ -191,8 +192,13 @@ ClassLoaderData* SystemDictionary::register_loader(Handle class_loader, bool cre // Add a new class loader data to the graph. return ClassLoaderDataGraph::add(class_loader, true); } else { - return (class_loader() == nullptr) ? ClassLoaderData::the_null_class_loader_data() : - ClassLoaderDataGraph::find_or_create(class_loader); + bool created = false; + ClassLoaderData* loader_data = (class_loader() == nullptr) ? ClassLoaderData::the_null_class_loader_data() : + ClassLoaderDataGraph::find_or_create(class_loader, created); + if (created && CDSConfig::is_using_aot_linked_classes() && CDSConfig::supports_custom_loaders()) { + AOTLinkedClassBulkLoader::preload_classes_for_loader(loader_data, JavaThread::current()); + } + return loader_data; } } @@ -1172,7 +1178,7 @@ void SystemDictionary::load_shared_class_misc(InstanceKlass* ik, ClassLoaderData // - There's no need to call java.lang.ClassLoader::load_class() because the boot/platform/app // loaders are well-behaved void SystemDictionary::preload_class(Handle class_loader, InstanceKlass* ik, TRAPS) { - precond(Universe::is_bootstrapping()); + //precond(Universe::is_bootstrapping()); precond(java_platform_loader() != nullptr && java_system_loader() != nullptr); precond(class_loader() == nullptr || class_loader() == java_platform_loader() ||class_loader() == java_system_loader() || ik->cl_aot_identity() != nullptr); precond(CDSConfig::is_using_aot_linked_classes()); @@ -1194,14 +1200,22 @@ void SystemDictionary::preload_class(Handle class_loader, InstanceKlass* ik, TRA #endif ClassLoaderData* loader_data = ClassLoaderData::class_loader_data(class_loader()); - oop java_mirror = ik->archived_java_mirror(); - precond(java_mirror != nullptr); - //assert(java_lang_Class::module(java_mirror) != nullptr, "must have been archived"); - - 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"); + Handle pd; + PackageEntry* pkg_entry = nullptr; + if (ik->has_archived_mirror_index()) { + oop java_mirror = ik->archived_java_mirror(); + precond(java_mirror != nullptr); + //assert(java_lang_Class::module(java_mirror) != nullptr, "must have been archived"); + + pd = Handle(THREAD, java_lang_Class::protection_domain(java_mirror)); + pkg_entry = ik->package(); + if (is_builtin_class_loader(class_loader())) { + assert(pkg_entry != nullptr || ClassLoader::package_from_class_name(ik->name()) == nullptr, + "non-empty packages for builtin loaders must have been archived"); + } else if (ik->is_defined_by_aot_safe_custom_loader()) { + assert(pkg_entry == nullptr, "packages for aot-safe custom loaders are not archived"); + } + } // TODO: the following assert requires JDK-8365580 // assert(is_shared_class_visible(ik->name(), ik, pkg_entry, class_loader), "must be"); diff --git a/src/hotspot/share/classfile/systemDictionaryShared.cpp b/src/hotspot/share/classfile/systemDictionaryShared.cpp index fcd407a3f10..1dd382f73ae 100644 --- a/src/hotspot/share/classfile/systemDictionaryShared.cpp +++ b/src/hotspot/share/classfile/systemDictionaryShared.cpp @@ -126,19 +126,24 @@ InstanceKlass* SystemDictionaryShared::load_shared_class_for_aot_safe_custom_loa InstanceKlass* ik = find_class_in_aot_safe_custom_loader_dict(class_name); if (ik != nullptr && !ik->shared_loading_failed()) { - oop aot_identity = java_lang_ClassLoader::aotIdentity(class_loader()); - assert(aot_identity != nullptr, "must be"); - Symbol* aot_identity_sym = java_lang_String::as_symbol(aot_identity); - if (aot_identity_sym->equals(ik->cl_aot_identity())) { - SharedClassLoadingMark slm(THREAD, ik); - PackageEntry* pkg_entry = CDSProtectionDomain::get_package_entry_from_class(ik, class_loader); - 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); + // In assembly phase the classes loaded by aot-safe loaders are loaded by CDS$UnregisteredClassLoader + // which doesn't have aot identity. So skip the aot-identity check. + if (class_loader() != UnregisteredClasses::unregistered_class_loader(THREAD)()) { + oop aot_identity = java_lang_ClassLoader::aotIdentity(class_loader()); + assert(aot_identity != nullptr, "must be"); + Symbol* aot_identity_sym = java_lang_String::as_symbol(aot_identity); + if (!aot_identity_sym->equals(ik->cl_aot_identity())) { + return nullptr; } - return load_shared_class(ik, class_loader, protection_domain, nullptr, pkg_entry, THREAD); } + SharedClassLoadingMark slm(THREAD, ik); + PackageEntry* pkg_entry = CDSProtectionDomain::get_package_entry_from_class(ik, class_loader); + 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; } @@ -668,11 +673,14 @@ InstanceKlass* SystemDictionaryShared::find_or_load_shared_class( } k = load_shared_class_for_builtin_loader(name, class_loader, THREAD); - } else if (CDSConfig::supports_custom_loaders() && java_lang_ClassLoader::aotIdentity(class_loader()) != nullptr) { + } else if (CDSConfig::supports_custom_loaders() && + (class_loader() == UnregisteredClasses::unregistered_class_loader(THREAD)() || java_lang_ClassLoader::aotIdentity(class_loader()) != nullptr)) { ClassLoaderData *loader_data = register_loader(class_loader); +#if 0 if (CDSConfig::is_using_full_module_graph()) { ClassLoaderDataShared::restore_custom_loader_archived_data(loader_data); } +#endif Dictionary* dictionary = loader_data->dictionary(); // TODO: find_or_load_shared_class is called for diff --git a/src/hotspot/share/oops/klass.hpp b/src/hotspot/share/oops/klass.hpp index 82a6ed77fad..51714b2114a 100644 --- a/src/hotspot/share/oops/klass.hpp +++ b/src/hotspot/share/oops/klass.hpp @@ -316,12 +316,10 @@ class Klass : public Metadata { ClassLoaderData* class_loader_data() const { return _class_loader_data; } void set_class_loader_data(ClassLoaderData* loader_data) { _class_loader_data = loader_data; - if (CDSConfig::supports_custom_loaders() && loader_data != nullptr) { - _cl_aot_identity = loader_data->aot_identity(); - } } Symbol* cl_aot_identity() const { return _cl_aot_identity; } + void set_cl_aot_identity(Symbol* aot_id) { _cl_aot_identity = aot_id; } bool is_defined_by_aot_safe_custom_loader() const { return _cl_aot_identity != nullptr; } s2 shared_classpath_index() const { From a83dd49472a319160d0e2b97b011aac2e4918c3b Mon Sep 17 00:00:00 2001 From: Ashutosh Mehra Date: Wed, 28 Jan 2026 21:30:37 -0500 Subject: [PATCH 05/30] Remove code for archiving packages and modules tables for custom loaders Signed-off-by: Ashutosh Mehra --- .../share/cds/aotLinkedClassBulkLoader.cpp | 1 + src/hotspot/share/cds/aotMappedHeapWriter.cpp | 4 +- src/hotspot/share/cds/finalImageRecipes.cpp | 52 ----- src/hotspot/share/cds/finalImageRecipes.hpp | 1 - .../share/classfile/classFileParser.cpp | 2 +- .../share/classfile/classLoaderData.cpp | 16 +- .../share/classfile/classLoaderData.hpp | 5 +- .../share/classfile/classLoaderDataShared.cpp | 211 ++---------------- .../classfile/systemDictionaryShared.cpp | 7 +- src/hotspot/share/prims/jvm.cpp | 3 +- 10 files changed, 31 insertions(+), 271 deletions(-) diff --git a/src/hotspot/share/cds/aotLinkedClassBulkLoader.cpp b/src/hotspot/share/cds/aotLinkedClassBulkLoader.cpp index a753453cd09..de91bbd79aa 100644 --- a/src/hotspot/share/cds/aotLinkedClassBulkLoader.cpp +++ b/src/hotspot/share/cds/aotLinkedClassBulkLoader.cpp @@ -141,6 +141,7 @@ void AOTLinkedClassBulkLoader::preload_classes_for_loader_impl(Handle loader_obj preload_classes_in_table(table->class_list(), aot_id->as_C_string(), loader_obj, CHECK); MonitorLocker mu1(SystemDictionary_lock); mark_initiating_loader(THREAD, loader_obj()); + table->set_loaded(true); } class DictionaryCopier : public KlassClosure { diff --git a/src/hotspot/share/cds/aotMappedHeapWriter.cpp b/src/hotspot/share/cds/aotMappedHeapWriter.cpp index 644a1d02eab..f360486586e 100644 --- a/src/hotspot/share/cds/aotMappedHeapWriter.cpp +++ b/src/hotspot/share/cds/aotMappedHeapWriter.cpp @@ -549,9 +549,9 @@ size_t AOTMappedHeapWriter::copy_one_source_obj_to_buffer(oop src_obj) { update_buffered_object_field(to, java_lang_Module::module_entry_offset(), nullptr); } else if (java_lang_ClassLoader::is_instance(src_obj)) { #ifdef ASSERT + // We only archive these loaders if (src_obj != SystemDictionary::java_platform_loader() && - src_obj != SystemDictionary::java_system_loader() && - java_lang_ClassLoader::aotIdentity(src_obj) == nullptr) { + src_obj != SystemDictionary::java_system_loader()) { assert(src_obj->klass()->name()->equals("jdk/internal/loader/ClassLoaders$BootClassLoader"), "must be"); } #endif diff --git a/src/hotspot/share/cds/finalImageRecipes.cpp b/src/hotspot/share/cds/finalImageRecipes.cpp index 3018a1022ae..e08f6852cc2 100644 --- a/src/hotspot/share/cds/finalImageRecipes.cpp +++ b/src/hotspot/share/cds/finalImageRecipes.cpp @@ -47,9 +47,6 @@ GrowableArray* FinalImageRecipes::_tmp_reflect_flags = nullptr; GrowableArray* FinalImageRecipes::_tmp_dynamic_proxy_classes = nullptr; static FinalImageRecipes* _final_image_recipes = nullptr; -using AOTIdentityToLoaderObjectMap = HashTable; -static AOTIdentityToLoaderObjectMap* _aot_safe_custom_loaders_map = nullptr; - void* FinalImageRecipes::operator new(size_t size) throw() { return ArchiveBuilder::current()->ro_region_alloc(size); } @@ -293,38 +290,6 @@ void FinalImageRecipes::load_all_classes(TRAPS) { } } } - -#if 0 - if (CDSConfig::supports_custom_loaders()) { - // Custom Loaders must be marked with AOTSafeClassInitializer annotation. - // They would have been created as part of running class initializer for custom loaders. - // Objects of such custom loaders get registered with VM in ClassLoader::registerAsAOTCompatibleLoader(). - // Use these objects to load additional classes not yet loaded. - for (int i = 0; i < _all_klasses->length(); i++) { - Klass* k = _all_klasses->at(i); - int flags = _flags->at(i); - if (k->is_instance_klass()) { - InstanceKlass* ik = InstanceKlass::cast(k); - Symbol* aot_identity = ik->cl_aot_identity(); - if (ik->defined_by_other_loaders() && aot_identity != nullptr) { - OopHandle* loader_h_ptr = _aot_safe_custom_loaders_map->get(aot_identity); - if (loader_h_ptr != nullptr) { - OopHandle loader_h = *loader_h_ptr; - Klass* actual = SystemDictionary::resolve_or_fail(ik->name(), Handle(THREAD, loader_h.resolve()), true, CHECK); - } - } - } - } - ResizeableHashTable processed(10, 100); - MonitorLocker mu1(SystemDictionary_lock); - _aot_safe_custom_loaders_map->iterate([&](Symbol* aot_id, OopHandle h_loader) { - ResourceMark rm; - oop loader = h_loader.resolve();; - AOTLinkedClassBulkLoader::mark_initiating_loader(THREAD, loader, processed); - return true; - }); - } -#endif } void FinalImageRecipes::apply_recipes_for_reflection_data(JavaThread* current) { @@ -451,20 +416,3 @@ void FinalImageRecipes::apply_recipes_impl(TRAPS) { void FinalImageRecipes::serialize(SerializeClosure* soc) { soc->do_ptr((void**)&_final_image_recipes); } - -void FinalImageRecipes::add_aot_safe_custom_loader(oop loader, TRAPS) { - assert(!SystemDictionary::is_builtin_class_loader(loader), "should not be called for builtin loader"); - if (CDSConfig::supports_custom_loaders()) { - ResourceMark rm; - if (_aot_safe_custom_loaders_map == nullptr) { - _aot_safe_custom_loaders_map = new (mtClassShared) AOTIdentityToLoaderObjectMap(); - } - - OopHandle loader_h(Universe::vm_global(), loader); - assert(java_lang_ClassLoader::aotIdentity(loader) != nullptr, "sanity check"); - Symbol* aot_identity = java_lang_String::as_symbol(java_lang_ClassLoader::aotIdentity(loader)); - - log_info(cds)("Adding AOT compatible loader with aot identity=%s", aot_identity->as_C_string()); - _aot_safe_custom_loaders_map->put(aot_identity, loader_h); - } -} diff --git a/src/hotspot/share/cds/finalImageRecipes.hpp b/src/hotspot/share/cds/finalImageRecipes.hpp index ec9624abd32..9638fc954bc 100644 --- a/src/hotspot/share/cds/finalImageRecipes.hpp +++ b/src/hotspot/share/cds/finalImageRecipes.hpp @@ -110,7 +110,6 @@ class FinalImageRecipes { // Called when dumping final image static void apply_recipes(TRAPS); - static void add_aot_safe_custom_loader(oop loader, TRAPS); }; #endif // SHARE_CDS_FINALIMAGERECIPES_HPP diff --git a/src/hotspot/share/classfile/classFileParser.cpp b/src/hotspot/share/classfile/classFileParser.cpp index 8ccbeb41dbb..08a49decd1d 100644 --- a/src/hotspot/share/classfile/classFileParser.cpp +++ b/src/hotspot/share/classfile/classFileParser.cpp @@ -1902,7 +1902,7 @@ AnnotationCollector::annotation_index(const ClassLoaderData* loader_data, } case VM_SYMBOL_ENUM_NAME(jdk_internal_vm_annotation_AOTSafeClassInitializer_signature): { if (_location != _in_class) break; // only allow for classes - //if (!privileged) break; // only allow in privileged code + if (!privileged) break; // only allow in privileged code return _jdk_internal_vm_annotation_AOTSafeClassInitializer; } case VM_SYMBOL_ENUM_NAME(jdk_internal_vm_annotation_AOTRuntimeSetup_signature): { diff --git a/src/hotspot/share/classfile/classLoaderData.cpp b/src/hotspot/share/classfile/classLoaderData.cpp index 2f31ba0fda0..db09b447fd0 100644 --- a/src/hotspot/share/classfile/classLoaderData.cpp +++ b/src/hotspot/share/classfile/classLoaderData.cpp @@ -151,7 +151,7 @@ ClassLoaderData::ClassLoaderData(Handle h_class_loader, bool has_class_mirror_ho _next(nullptr), _unloading_next(nullptr), _class_loader_klass(nullptr), _name(nullptr), _name_and_id(nullptr), - _aot_identity(nullptr), _restored(false) { + _aot_identity(nullptr) { if (!h_class_loader.is_null()) { _class_loader = _handles.add(h_class_loader()); @@ -180,16 +180,6 @@ ClassLoaderData::ClassLoaderData(Handle h_class_loader, bool has_class_mirror_ho } _dictionary = create_dictionary(); } -#if 0 - /* This happens at a place where safepoint is not allowed. See NoSafepointVerifier in ClassLoaderDataGraph::add_to_graph(). - * However, call to restore_archived_data() may create a new Module and acquire Module_lock which allows safepoint. - * So we hit assertion in JavaThread::check_possible_safepoint(): - * assert(false, "Possible safepoint reached by thread that does not allow it"); - */ - if (_aot_identity != nullptr && CDSConfig::is_using_full_module_graph()) { - ClassLoaderDataShared::restore_archived_data(this); - } -#endif NOT_PRODUCT(_dependency_count = 0); // number of class loader dependencies @@ -648,11 +638,11 @@ void ClassLoaderData::unload() { } } -ModuleEntryTable* ClassLoaderData::modules(bool create_if_null) { +ModuleEntryTable* ClassLoaderData::modules() { // Lazily create the module entry table at first request. // Lock-free access requires load_acquire. ModuleEntryTable* modules = AtomicAccess::load_acquire(&_modules); - if (modules == nullptr && create_if_null) { + if (modules == nullptr) { MutexLocker m1(Module_lock); // Check if _modules got allocated while we were waiting for this lock. if ((modules = _modules) == nullptr) { diff --git a/src/hotspot/share/classfile/classLoaderData.hpp b/src/hotspot/share/classfile/classLoaderData.hpp index 1ab76b7d772..1e0a595552c 100644 --- a/src/hotspot/share/classfile/classLoaderData.hpp +++ b/src/hotspot/share/classfile/classLoaderData.hpp @@ -175,7 +175,6 @@ class ClassLoaderData : public CHeapObj { Symbol* _name; Symbol* _name_and_id; Symbol* _aot_identity; - bool _restored; JFR_ONLY(DEFINE_TRACE_ID_FIELD;) void set_next(ClassLoaderData* next); @@ -331,7 +330,7 @@ class ClassLoaderData : public CHeapObj { void record_dependency(const Klass* to); PackageEntryTable* packages() { return _packages; } ModuleEntry* unnamed_module() { return _unnamed_module; } - ModuleEntryTable* modules(bool create_if_null = true); + ModuleEntryTable* modules(); bool modules_defined() { return (_modules != nullptr); } // Offsets @@ -361,8 +360,6 @@ class ClassLoaderData : public CHeapObj { const char* loader_name_and_id() const; Symbol* name_and_id() const { return _name_and_id; } Symbol* aot_identity() const { return _aot_identity; } - bool restored() const { return _restored; } - void set_restored(bool v) { _restored = v; } unsigned identity_hash() const { return (unsigned)((uintptr_t)this >> LogBytesPerWord); diff --git a/src/hotspot/share/classfile/classLoaderDataShared.cpp b/src/hotspot/share/classfile/classLoaderDataShared.cpp index 55e626a0bb0..f201e44b36b 100644 --- a/src/hotspot/share/classfile/classLoaderDataShared.cpp +++ b/src/hotspot/share/classfile/classLoaderDataShared.cpp @@ -23,19 +23,15 @@ */ #include "cds/aotLogging.hpp" -#include "cds/archiveBuilder.hpp" #include "cds/cdsConfig.hpp" #include "cds/heapShared.inline.hpp" #include "cds/serializeClosure.hpp" #include "classfile/classLoaderData.inline.hpp" -#include "classfile/classLoaderDataGraph.hpp" #include "classfile/classLoaderDataShared.hpp" #include "classfile/moduleEntry.hpp" #include "classfile/modules.hpp" #include "classfile/packageEntry.hpp" -#include "classfile/symbolTable.hpp" #include "classfile/systemDictionary.hpp" -#include "classfile/systemDictionaryShared.hpp" #include "logging/log.hpp" #include "runtime/handles.inline.hpp" #include "runtime/safepoint.hpp" @@ -44,26 +40,11 @@ bool ClassLoaderDataShared::_full_module_graph_loaded = false; -static ClassLoaderData* null_class_loader_data() { - ClassLoaderData* loader_data = ClassLoaderData::the_null_class_loader_data(); - assert(loader_data != nullptr, "must be"); - return loader_data; -} - -static ClassLoaderData* java_platform_loader_data_or_null() { - return ClassLoaderData::class_loader_data_or_null(SystemDictionary::java_platform_loader()); -} - -static ClassLoaderData* java_system_loader_data_or_null() { - return ClassLoaderData::class_loader_data_or_null(SystemDictionary::java_system_loader()); -} - class ArchivedClassLoaderData { Array* _packages; Array* _modules; ModuleEntry* _unnamed_module; - Symbol* _aot_identity; - int _loader_obj_index; + void assert_valid(ClassLoaderData* loader_data) { // loader_data may be null if the boot layer has loaded no modules for the platform or // system loaders (e.g., if you create a custom JDK image with only java.base). @@ -73,7 +54,7 @@ class ArchivedClassLoaderData { } } public: - ArchivedClassLoaderData() : _packages(nullptr), _modules(nullptr), _unnamed_module(nullptr), _aot_identity(nullptr), _loader_obj_index(-1) {} + ArchivedClassLoaderData() : _packages(nullptr), _modules(nullptr), _unnamed_module(nullptr) {} void iterate_symbols(ClassLoaderData* loader_data, MetaspaceClosure* closure); void allocate(ClassLoaderData* loader_data); @@ -81,15 +62,11 @@ class ArchivedClassLoaderData { ModuleEntry* unnamed_module() { return _unnamed_module; } - Symbol* aot_identity() { return _aot_identity; } - int loader_obj_index() { return _loader_obj_index; } void serialize(SerializeClosure* f) { f->do_ptr(&_packages); f->do_ptr(&_modules); f->do_ptr(&_unnamed_module); - f->do_ptr(&_aot_identity); - f->do_int(&_loader_obj_index); } void restore(ClassLoaderData* loader_data, bool do_entries, bool do_oops); @@ -99,18 +76,10 @@ class ArchivedClassLoaderData { static ArchivedClassLoaderData _archived_boot_loader_data; static ArchivedClassLoaderData _archived_platform_loader_data; static ArchivedClassLoaderData _archived_system_loader_data; - static ModuleEntry* _archived_javabase_moduleEntry = nullptr; static int _platform_loader_root_index = -1; static int _system_loader_root_index = -1; -static bool archived_cld_map_equals_fn(ArchivedClassLoaderData* archived_cld, Symbol* sym, int len) { - return archived_cld->aot_identity()->equals(sym); -} - -using ArchivedAOTIdToCLDataMap = OffsetCompactHashtable; -static ArchivedAOTIdToCLDataMap _aotid_to_archived_cld_map; - void ArchivedClassLoaderData::iterate_symbols(ClassLoaderData* loader_data, MetaspaceClosure* closure) { assert(CDSConfig::is_dumping_full_module_graph(), "must be"); assert_valid(loader_data); @@ -130,23 +99,8 @@ void ArchivedClassLoaderData::allocate(ClassLoaderData* loader_data) { // So we store the packages/modules in Arrays. At runtime, we create // the hashtables using these arrays. _packages = loader_data->packages()->allocate_archived_entries(); - if (loader_data->modules(false) != nullptr) { - _modules = loader_data->modules(false)->allocate_archived_entries(); - } + _modules = loader_data->modules()->allocate_archived_entries(); _unnamed_module = loader_data->unnamed_module()->allocate_archived_entry(); - if (!loader_data->is_builtin_class_loader_data()) { - _aot_identity = loader_data->aot_identity(); - } - if (!loader_data->class_loader_handle().is_empty()) { - _loader_obj_index = HeapShared::append_root(loader_data->class_loader_handle().resolve()); - } else { - assert(loader_data == null_class_loader_data(), "sanity check"); - _loader_obj_index = -1; - } - ArchivePtrMarker::mark_pointer((address*)&_packages); - ArchivePtrMarker::mark_pointer((address*)&_modules); - ArchivePtrMarker::mark_pointer((address*)&_unnamed_module); - ArchivePtrMarker::mark_pointer((address*)&_aot_identity); } } @@ -155,14 +109,8 @@ void ArchivedClassLoaderData::init_archived_entries(ClassLoaderData* loader_data assert_valid(loader_data); if (loader_data != nullptr) { loader_data->packages()->init_archived_entries(_packages); - if (loader_data->modules(false) != nullptr) { - loader_data->modules(false) ->init_archived_entries(_modules); - } + loader_data->modules() ->init_archived_entries(_modules); _unnamed_module->init_as_archived_entry(); - if (!loader_data->is_builtin_class_loader_data()) { - _aot_identity = ArchiveBuilder::get_buffered_symbol(_aot_identity); - assert(_aot_identity != nullptr, "sanity check"); - } } } @@ -234,6 +182,20 @@ void ClassLoaderDataShared::load_archived_platform_and_system_class_loaders() { #endif } +static ClassLoaderData* null_class_loader_data() { + ClassLoaderData* loader_data = ClassLoaderData::the_null_class_loader_data(); + assert(loader_data != nullptr, "must be"); + return loader_data; +} + +static ClassLoaderData* java_platform_loader_data_or_null() { + return ClassLoaderData::class_loader_data_or_null(SystemDictionary::java_platform_loader()); +} + +static ClassLoaderData* java_system_loader_data_or_null() { + return ClassLoaderData::class_loader_data_or_null(SystemDictionary::java_system_loader()); +} + // ModuleEntryTables (even if empty) are required for iterate_symbols() to scan the // platform/system loaders inside the CDS safepoint, but the tables can be created only // when outside of safepoints. Let's do that now. @@ -249,101 +211,37 @@ void ClassLoaderDataShared::ensure_module_entry_table_exists(oop class_loader) { assert(met != nullptr, "sanity"); } -class CLDSymbolsIterator : public CLDClosure { - MetaspaceClosure* _closure; - public: - CLDSymbolsIterator(MetaspaceClosure* closure) : _closure(closure) {} - void do_cld(ClassLoaderData* cld) { - if (cld->aot_identity() != nullptr) { - cld->packages()->iterate_symbols(_closure); - if (cld->modules(false) != nullptr) { - cld->modules(false)->iterate_symbols(_closure); - } - cld->unnamed_module()->iterate_symbols(_closure); - } - } -}; - void ClassLoaderDataShared::iterate_symbols(MetaspaceClosure* closure) { assert(CDSConfig::is_dumping_full_module_graph(), "must be"); _archived_boot_loader_data.iterate_symbols (null_class_loader_data(), closure); _archived_platform_loader_data.iterate_symbols(java_platform_loader_data_or_null(), closure); _archived_system_loader_data.iterate_symbols (java_system_loader_data_or_null(), closure); - - CLDSymbolsIterator sym_iterator(closure); - ClassLoaderDataGraph::cld_do(&sym_iterator); } -using ArchivedCLDToCLDMap = HashTable; -ArchivedCLDToCLDMap* _archived_cld_to_runtime_cld_map = nullptr; - -class ArchiveAOTCompatibleLoaders : public CLDClosure { - public: - void do_cld(ClassLoaderData* cld) { - if (!cld->is_builtin_class_loader_data() && cld->aot_identity() != nullptr) { - ArchivedClassLoaderData* archived_entry = (ArchivedClassLoaderData*)ArchiveBuilder::rw_region_alloc(sizeof(ArchivedClassLoaderData)); - archived_entry->allocate(cld); - _archived_cld_to_runtime_cld_map->put(archived_entry, cld); - } - } -}; - void ClassLoaderDataShared::allocate_archived_tables() { assert(CDSConfig::is_dumping_full_module_graph(), "must be"); _archived_boot_loader_data.allocate (null_class_loader_data()); _archived_platform_loader_data.allocate(java_platform_loader_data_or_null()); _archived_system_loader_data.allocate (java_system_loader_data_or_null()); - -#if 0 - _archived_cld_to_runtime_cld_map = new (mtClassShared) ArchivedCLDToCLDMap(); - if (CDSConfig::supports_custom_loaders()) { - ArchiveAOTCompatibleLoaders archive_closure; - ClassLoaderDataGraph::cld_do(&archive_closure); - } -#endif } void ClassLoaderDataShared::init_archived_tables() { assert(CDSConfig::is_dumping_full_module_graph(), "must be"); + _archived_boot_loader_data.init_archived_entries (null_class_loader_data()); _archived_platform_loader_data.init_archived_entries(java_platform_loader_data_or_null()); _archived_system_loader_data.init_archived_entries (java_system_loader_data_or_null()); -#if 0 - _archived_cld_to_runtime_cld_map->iterate_all([&](ArchivedClassLoaderData* &archived_cld, ClassLoaderData* &cld) { - archived_cld->init_archived_entries(cld); - }); -#endif + _archived_javabase_moduleEntry = ModuleEntry::get_archived_entry(ModuleEntryTable::javabase_moduleEntry()); _platform_loader_root_index = HeapShared::append_root(SystemDictionary::java_platform_loader()); _system_loader_root_index = HeapShared::append_root(SystemDictionary::java_system_loader()); } -void ClassLoaderDataShared::write_cld_table() { - CompactHashtableStats stats; - _aotid_to_archived_cld_map.reset(); - CompactHashtableWriter writer(_archived_cld_to_runtime_cld_map->number_of_entries(), &stats); - _archived_cld_to_runtime_cld_map->iterate_all([&](ArchivedClassLoaderData* &archived_cld, ClassLoaderData* &cld) { - ResourceMark rm; - Symbol* aot_id = cld->aot_identity(); - assert(aot_id != nullptr, "sanity check"); - ArchiveBuilder* builder = ArchiveBuilder::current(); - assert(builder->get_buffered_addr(aot_id) != nullptr, "Symbol %s is not in the buffer", aot_id->as_C_string()); - unsigned int hash = Symbol::symbol_hash(aot_id); - u4 delta = builder->buffer_to_offset_u4((address)archived_cld); - writer.add(hash, delta); - if (log_is_enabled(Trace, aot, hashtables)) { - log_trace(aot, hashtables)("archived cld dictionary: %s", aot_id->as_C_string()); - } - }); - writer.dump(&_aotid_to_archived_cld_map, "aot_cld_map dictionary"); -} - void ClassLoaderDataShared::serialize(SerializeClosure* f) { _archived_boot_loader_data.serialize(f); _archived_platform_loader_data.serialize(f); _archived_system_loader_data.serialize(f); - //_aotid_to_archived_cld_map.serialize_header(f); f->do_ptr(&_archived_javabase_moduleEntry); f->do_int(&_platform_loader_root_index); f->do_int(&_system_loader_root_index); @@ -352,13 +250,6 @@ void ClassLoaderDataShared::serialize(SerializeClosure* f) { ModuleEntry* ClassLoaderDataShared::archived_boot_unnamed_module() { if (CDSConfig::is_using_full_module_graph()) { return _archived_boot_loader_data.unnamed_module(); -#if 0 - Symbol* boot_loader_aot_identity = SymbolTable::new_symbol("BOOT"); - unsigned int hash = Symbol::symbol_hash(boot_loader_aot_identity); - ArchivedClassLoaderData* archived_cld = _aotid_to_archived_cld_map.lookup(boot_loader_aot_identity, hash, 0); - assert(archived_cld != nullptr, "sanity check"); - return archived_cld->unnamed_module(); -#endif } else { return nullptr; } @@ -377,14 +268,6 @@ ModuleEntry* ClassLoaderDataShared::archived_unnamed_module(ClassLoaderData* loa } else if (loader_data->class_loader() == HeapShared::get_root(_system_loader_root_index)) { archived_module = _archived_system_loader_data.unnamed_module(); } -#if 0 - } else if (loader_data->aot_identity() != nullptr) { - unsigned int hash = Symbol::symbol_hash(loader_data->aot_identity()); - ArchivedClassLoaderData* archived_cld = _aotid_to_archived_cld_map.lookup(loader_data->aot_identity(), hash, 0); - if (archived_cld != nullptr) { - archived_module = archived_cld->unnamed_module(); - } -#endif } } return archived_module; @@ -395,11 +278,6 @@ void ClassLoaderDataShared::clear_archived_oops() { _archived_boot_loader_data.clear_archived_oops(); _archived_platform_loader_data.clear_archived_oops(); _archived_system_loader_data.clear_archived_oops(); -#if 0 - _aotid_to_archived_cld_map.iterate([&](ArchivedClassLoaderData* archived_cld) { - archived_cld->clear_archived_oops(); - }); -#endif if (_platform_loader_root_index >= 0) { HeapShared::clear_root(_platform_loader_root_index); HeapShared::clear_root(_system_loader_root_index); @@ -409,9 +287,6 @@ void ClassLoaderDataShared::clear_archived_oops() { // Must be done before ClassLoader::create_javabase() void ClassLoaderDataShared::restore_archived_entries_for_null_class_loader_data() { precond(CDSConfig::is_using_full_module_graph()); - //unsigned int hash = Symbol::symbol_hash(null_class_loader_data()->aot_identity()); - //ArchivedClassLoaderData* archived_cld = _aotid_to_archived_cld_map.lookup(null_class_loader_data()->aot_identity(), hash, 0); - //archived_cld->restore(null_class_loader_data(), true, false); _archived_boot_loader_data.restore(null_class_loader_data(), true, false); ModuleEntryTable::set_javabase_moduleEntry(_archived_javabase_moduleEntry); aot_log_info(aot)("use_full_module_graph = true; java.base = " INTPTR_FORMAT, @@ -420,33 +295,19 @@ void ClassLoaderDataShared::restore_archived_entries_for_null_class_loader_data( oop ClassLoaderDataShared::restore_archived_oops_for_null_class_loader_data() { assert(CDSConfig::is_using_full_module_graph(), "must be"); - //unsigned int hash = Symbol::symbol_hash(null_class_loader_data()->aot_identity()); - //ArchivedClassLoaderData* archived_cld = _aotid_to_archived_cld_map.lookup(null_class_loader_data()->aot_identity(), hash, 0); - //archived_cld->restore(null_class_loader_data(), false, true); _archived_boot_loader_data.restore(null_class_loader_data(), false, true); - //null_class_loader_data()->set_restored(true); return _archived_javabase_moduleEntry->module_oop(); } void ClassLoaderDataShared::restore_java_platform_loader_from_archive(ClassLoaderData* loader_data) { assert(CDSConfig::is_using_full_module_graph(), "must be"); - //unsigned int hash = Symbol::symbol_hash(loader_data->aot_identity()); - //ArchivedClassLoaderData* archived_cld = _aotid_to_archived_cld_map.lookup(loader_data->aot_identity(), hash, 0); - //assert(archived_cld != nullptr, "ArchivedClassLoaderData for platform loader not found"); - //archived_cld->restore(loader_data, true, true); _archived_platform_loader_data.restore(loader_data, true, true); - loader_data->set_restored(true); } void ClassLoaderDataShared::restore_java_system_loader_from_archive(ClassLoaderData* loader_data) { assert(CDSConfig::is_using_full_module_graph(), "must be"); - //unsigned int hash = Symbol::symbol_hash(loader_data->aot_identity()); - //ArchivedClassLoaderData* archived_cld = _aotid_to_archived_cld_map.lookup(loader_data->aot_identity(), hash, 0); - //assert(archived_cld != nullptr, "ArchivedClassLoaderData for system loader not found"); - //archived_cld->restore(loader_data, true, true); _archived_system_loader_data.restore(loader_data, true, true); _full_module_graph_loaded = true; - loader_data->set_restored(true); } // This is called before AOTLinkedClassBulkLoader starts preloading classes. It makes sure that @@ -460,38 +321,6 @@ void ClassLoaderDataShared::restore_archived_modules_for_preloading_classes(Java 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); -#if 0 - _aotid_to_archived_cld_map.iterate([&](ArchivedClassLoaderData* archived_cld) { - Handle h_loader(current, HeapShared::get_root(archived_cld->loader_obj_index())); - ClassLoaderData* runtime_cld = SystemDictionary::register_loader(h_loader); - ClassLoaderDataShared::restore_custom_loader_archived_data(runtime_cld); - }); -#endif -} - -void ClassLoaderDataShared::restore_custom_loader_archived_data(ClassLoaderData* loader_data) { - assert(!loader_data->is_builtin_class_loader_data(), "should not be called for built-in loaders"); - //if already restored, nothing to do - if (loader_data->restored()) { - return; - } - ResourceMark rm; - assert(CDSConfig::is_using_full_module_graph(), "must be"); - Symbol* aot_id = loader_data->aot_identity(); - assert(aot_id != nullptr, "sanity check"); - unsigned int hash = Symbol::symbol_hash(aot_id); - ArchivedClassLoaderData* archived_cld = _aotid_to_archived_cld_map.lookup(aot_id, hash, 0); - assert(archived_cld != nullptr, "ArchivedClassLoaderData for loader with aot_id=%s not found", aot_id->as_C_string()); - archived_cld->restore(loader_data, true, true); - loader_data->set_restored(true); -} - -void ClassLoaderDataShared::get_archived_custom_loader_objs(GrowableArray& loader_obj_list) { - // TODO: assert that it is called in production run only - _aotid_to_archived_cld_map.iterate([&](ArchivedClassLoaderData* archived_cld) { - Handle h_loader(Thread::current(), HeapShared::get_root(archived_cld->loader_obj_index())); - loader_obj_list.append(h_loader); - }); } #endif // INCLUDE_CDS_JAVA_HEAP diff --git a/src/hotspot/share/classfile/systemDictionaryShared.cpp b/src/hotspot/share/classfile/systemDictionaryShared.cpp index 1dd382f73ae..1141033e3ae 100644 --- a/src/hotspot/share/classfile/systemDictionaryShared.cpp +++ b/src/hotspot/share/classfile/systemDictionaryShared.cpp @@ -105,7 +105,7 @@ InstanceKlass* SystemDictionaryShared::load_shared_class_for_builtin_loader( InstanceKlass* ik = find_builtin_class(class_name); if (ik != nullptr && !ik->shared_loading_failed()) { - if ((SystemDictionary::is_system_class_loader(class_loader()) && ik->defined_by_app_loader()) || + if ((SystemDictionary::is_system_class_loader(class_loader()) && ik->defined_by_app_loader()) || (SystemDictionary::is_platform_class_loader(class_loader()) && ik->defined_by_platform_loader())) { SharedClassLoadingMark slm(THREAD, ik); PackageEntry* pkg_entry = CDSProtectionDomain::get_package_entry_from_class(ik, class_loader); @@ -676,11 +676,6 @@ InstanceKlass* SystemDictionaryShared::find_or_load_shared_class( } else if (CDSConfig::supports_custom_loaders() && (class_loader() == UnregisteredClasses::unregistered_class_loader(THREAD)() || java_lang_ClassLoader::aotIdentity(class_loader()) != nullptr)) { ClassLoaderData *loader_data = register_loader(class_loader); -#if 0 - if (CDSConfig::is_using_full_module_graph()) { - ClassLoaderDataShared::restore_custom_loader_archived_data(loader_data); - } -#endif Dictionary* dictionary = loader_data->dictionary(); // TODO: find_or_load_shared_class is called for diff --git a/src/hotspot/share/prims/jvm.cpp b/src/hotspot/share/prims/jvm.cpp index f9503e626cb..3a40a2a14ce 100644 --- a/src/hotspot/share/prims/jvm.cpp +++ b/src/hotspot/share/prims/jvm.cpp @@ -2284,7 +2284,8 @@ JVM_ENTRY_PROF(jobject, JVM_AssertionStatusDirectives, JVM_AssertionStatusDirect JVM_END JVM_ENTRY_PROF(void, JVM_RegisterAsAOTCompatibleLoader, JVM_RegisterAsAOTCompatibleLoader(JNIEnv *env, jobject loader)) - FinalImageRecipes::add_aot_safe_custom_loader(JNIHandles::resolve_non_null(loader), CHECK); + //FinalImageRecipes::add_aot_safe_custom_loader(JNIHandles::resolve_non_null(loader), CHECK); + //Nothing to do here for now JVM_END // Verification //////////////////////////////////////////////////////////////////////////////// From d5973b101814f10e391f098dea5717308e5b35b2 Mon Sep 17 00:00:00 2001 From: Ashutosh Mehra Date: Thu, 29 Jan 2026 00:49:42 -0500 Subject: [PATCH 06/30] Prelink classes loaded by aot-safe custom loaders Signed-off-by: Ashutosh Mehra --- .../share/cds/aotLinkedClassBulkLoader.cpp | 47 ++++++++++--------- .../share/cds/aotLinkedClassBulkLoader.hpp | 2 + .../share/classfile/classLoaderData.cpp | 16 +++++++ .../share/classfile/classLoaderData.hpp | 1 + .../share/classfile/classLoaderDataGraph.cpp | 1 + .../share/classfile/systemDictionary.cpp | 3 -- 6 files changed, 45 insertions(+), 25 deletions(-) diff --git a/src/hotspot/share/cds/aotLinkedClassBulkLoader.cpp b/src/hotspot/share/cds/aotLinkedClassBulkLoader.cpp index de91bbd79aa..623a5fc3875 100644 --- a/src/hotspot/share/cds/aotLinkedClassBulkLoader.cpp +++ b/src/hotspot/share/cds/aotLinkedClassBulkLoader.cpp @@ -97,28 +97,6 @@ void AOTLinkedClassBulkLoader::preload_classes_impl(TRAPS) { initiate_loading(THREAD, "app", h_system_loader, table->boot2()); initiate_loading(THREAD, "app", h_system_loader, table->platform()); preload_classes_in_table(table->app(), "app", h_system_loader, CHECK); - -#if 0 - // For each loader load all its classes, and then mark it as the initiating loader - // for the all the classes loaded by its ancentar loaders - GrowableArray custom_loader_objs; - ClassLoaderDataShared::get_archived_custom_loader_objs(custom_loader_objs); - for (int i = 0; i < custom_loader_objs.length(); i++) { - ResourceMark rm; - Handle h_loader = custom_loader_objs.at(i); - Symbol* aot_id = java_lang_ClassLoader::loader_data(h_loader())->aot_identity(); - AOTLinkedClassTableForCustomLoader* table = AOTClassLinker::get_prelinked_table(aot_id); - preload_classes_in_table(table->class_list(), aot_id->as_C_string(), h_loader, CHECK); - } - - ResizeableHashTable processed(10, 100); - MonitorLocker mu1(SystemDictionary_lock); - for (int i = 0; i < custom_loader_objs.length(); i++) { - ResourceMark rm; - Handle h_loader = custom_loader_objs.at(i); - mark_initiating_loader(THREAD, h_loader(), processed); - } -#endif } void AOTLinkedClassBulkLoader::preload_classes_for_loader(ClassLoaderData* loader_data, TRAPS) { @@ -132,6 +110,9 @@ void AOTLinkedClassBulkLoader::preload_classes_for_loader_impl(Handle loader_obj return; } Symbol* aot_id = java_lang_ClassLoader::loader_data(loader_obj())->aot_identity(); + if (aot_id == nullptr) { + return; + } AOTLinkedClassTableForCustomLoader* table = AOTClassLinker::get_prelinked_table(aot_id); if (table->is_loaded()) { return; @@ -262,6 +243,28 @@ void AOTLinkedClassBulkLoader::link_classes_impl(TRAPS) { link_classes_in_table(table->app(), CHECK); } +void AOTLinkedClassBulkLoader::link_classes_for_loader(ClassLoaderData* loader_data, TRAPS) { + Handle h_loader(THREAD, loader_data->class_loader_handle().resolve()); + link_classes_for_loader_impl(h_loader, CHECK); +} + +void AOTLinkedClassBulkLoader::link_classes_for_loader_impl(Handle loader_obj, TRAPS) { + precond(CDSConfig::is_using_aot_linked_classes()); + if (SystemDictionary::is_builtin_class_loader(loader_obj())) { + // classes for builtin loaders should have already been loaded during startup + return; + } + + Symbol* aot_id = java_lang_ClassLoader::loader_data(loader_obj())->aot_identity(); + if (aot_id == nullptr) { + return; + } + AOTLinkedClassTableForCustomLoader* table = AOTClassLinker::get_prelinked_table(aot_id); + oop parent = java_lang_ClassLoader::parent(loader_obj()); + link_classes_for_loader_impl(Handle(THREAD, parent), CHECK); + link_classes_in_table(table->class_list(), CHECK); +} + void AOTLinkedClassBulkLoader::link_classes_in_table(Array* classes, TRAPS) { if (classes != nullptr) { for (int i = 0; i < classes->length(); i++) { diff --git a/src/hotspot/share/cds/aotLinkedClassBulkLoader.hpp b/src/hotspot/share/cds/aotLinkedClassBulkLoader.hpp index 53188b8cb75..2a47b72f27b 100644 --- a/src/hotspot/share/cds/aotLinkedClassBulkLoader.hpp +++ b/src/hotspot/share/cds/aotLinkedClassBulkLoader.hpp @@ -81,6 +81,8 @@ class AOTLinkedClassBulkLoader : AllStatic { static void preload_classes_for_loader(ClassLoaderData* loader_data, TRAPS); static void preload_classes_for_loader_impl(Handle loader_obj, TRAPS); static void mark_initiating_loader(JavaThread* currentThread, oop loader); + static void link_classes_for_loader(ClassLoaderData* loader_data, TRAPS); + static void link_classes_for_loader_impl(Handle loader_obj, TRAPS); }; #endif // SHARE_CDS_AOTLINKEDCLASSBULKLOADER_HPP diff --git a/src/hotspot/share/classfile/classLoaderData.cpp b/src/hotspot/share/classfile/classLoaderData.cpp index db09b447fd0..de8ac27febd 100644 --- a/src/hotspot/share/classfile/classLoaderData.cpp +++ b/src/hotspot/share/classfile/classLoaderData.cpp @@ -46,6 +46,8 @@ // The bootstrap loader (represented by null) also has a ClassLoaderData, // the singleton class the_null_class_loader_data(). +#include "cds/cdsConfig.hpp" +#include "cds/aotLinkedClassBulkLoader.hpp" #include "classfile/classLoaderData.inline.hpp" #include "classfile/classLoaderDataGraph.inline.hpp" #include "classfile/classLoaderDataShared.hpp" @@ -1131,3 +1133,17 @@ bool ClassLoaderData::contains_klass(Klass* klass) { } return false; } + +void ClassLoaderData::preload_classes() { + if (CDSConfig::is_using_aot_linked_classes() && CDSConfig::supports_custom_loaders()) { + JavaThread* thread = JavaThread::current(); + AOTLinkedClassBulkLoader::preload_classes_for_loader(this, thread); + if (thread->has_pending_exception()) { + AOTLinkedClassBulkLoader::exit_on_exception(thread); + } + AOTLinkedClassBulkLoader::link_classes_for_loader(this, thread); + if (thread->has_pending_exception()) { + AOTLinkedClassBulkLoader::exit_on_exception(thread); + } + } +} diff --git a/src/hotspot/share/classfile/classLoaderData.hpp b/src/hotspot/share/classfile/classLoaderData.hpp index 1e0a595552c..5b3b3498811 100644 --- a/src/hotspot/share/classfile/classLoaderData.hpp +++ b/src/hotspot/share/classfile/classLoaderData.hpp @@ -360,6 +360,7 @@ class ClassLoaderData : public CHeapObj { const char* loader_name_and_id() const; Symbol* name_and_id() const { return _name_and_id; } Symbol* aot_identity() const { return _aot_identity; } + void preload_classes(); unsigned identity_hash() const { return (unsigned)((uintptr_t)this >> LogBytesPerWord); diff --git a/src/hotspot/share/classfile/classLoaderDataGraph.cpp b/src/hotspot/share/classfile/classLoaderDataGraph.cpp index b6f7915db71..d519fbeb6a1 100644 --- a/src/hotspot/share/classfile/classLoaderDataGraph.cpp +++ b/src/hotspot/share/classfile/classLoaderDataGraph.cpp @@ -181,6 +181,7 @@ ClassLoaderData* ClassLoaderDataGraph::add_to_graph(Handle loader, bool has_clas ClassLoaderData* ClassLoaderDataGraph::add(Handle loader, bool has_class_mirror_holder) { MutexLocker ml(ClassLoaderDataGraph_lock); ClassLoaderData* loader_data = add_to_graph(loader, has_class_mirror_holder); + loader_data->preload_classes(); return loader_data; } diff --git a/src/hotspot/share/classfile/systemDictionary.cpp b/src/hotspot/share/classfile/systemDictionary.cpp index 9c009066ffb..a981b789303 100644 --- a/src/hotspot/share/classfile/systemDictionary.cpp +++ b/src/hotspot/share/classfile/systemDictionary.cpp @@ -195,9 +195,6 @@ ClassLoaderData* SystemDictionary::register_loader(Handle class_loader, bool cre bool created = false; ClassLoaderData* loader_data = (class_loader() == nullptr) ? ClassLoaderData::the_null_class_loader_data() : ClassLoaderDataGraph::find_or_create(class_loader, created); - if (created && CDSConfig::is_using_aot_linked_classes() && CDSConfig::supports_custom_loaders()) { - AOTLinkedClassBulkLoader::preload_classes_for_loader(loader_data, JavaThread::current()); - } return loader_data; } } From 157f0e7449ccb1d674f79dab6a4bf50dc2bfb6c6 Mon Sep 17 00:00:00 2001 From: Ashutosh Mehra Date: Mon, 2 Feb 2026 15:01:24 -0500 Subject: [PATCH 07/30] Preload classes for aot-safe custom loaders when they are constructed Signed-off-by: Ashutosh Mehra --- src/hotspot/share/cds/aotClassLinker.cpp | 2 +- .../share/cds/aotLinkedClassBulkLoader.cpp | 18 ++---------------- .../share/classfile/classLoaderData.cpp | 14 -------------- .../share/classfile/classLoaderData.hpp | 1 - .../share/classfile/classLoaderDataGraph.cpp | 1 - .../share/classfile/classLoaderDataGraph.hpp | 2 +- .../classfile/classLoaderDataGraph.inline.hpp | 4 +--- .../share/classfile/systemDictionary.cpp | 3 +-- src/hotspot/share/include/jvm.h | 2 +- src/hotspot/share/oops/cpCache.cpp | 4 ++-- src/hotspot/share/prims/jvm.cpp | 11 ++++++++++- .../share/classes/java/lang/ClassLoader.java | 2 +- .../share/native/libjava/ClassLoader.c | 2 +- 13 files changed, 21 insertions(+), 45 deletions(-) diff --git a/src/hotspot/share/cds/aotClassLinker.cpp b/src/hotspot/share/cds/aotClassLinker.cpp index 7e379979e9b..0ab74a8ee66 100644 --- a/src/hotspot/share/cds/aotClassLinker.cpp +++ b/src/hotspot/share/cds/aotClassLinker.cpp @@ -148,7 +148,7 @@ bool AOTClassLinker::try_add_candidate(InstanceKlass* ik) { assert(is_initialized(), "sanity"); assert(CDSConfig::is_dumping_aot_linked_classes(), "sanity"); - if (!SystemDictionaryShared::is_builtin(ik) && ik->cl_aot_identity() == nullptr) { + if (!SystemDictionaryShared::is_builtin(ik) && !ik->is_defined_by_aot_safe_custom_loader()) { // not loaded by a class loader which we know about return false; } diff --git a/src/hotspot/share/cds/aotLinkedClassBulkLoader.cpp b/src/hotspot/share/cds/aotLinkedClassBulkLoader.cpp index 623a5fc3875..69b944e2f1a 100644 --- a/src/hotspot/share/cds/aotLinkedClassBulkLoader.cpp +++ b/src/hotspot/share/cds/aotLinkedClassBulkLoader.cpp @@ -105,24 +105,15 @@ void AOTLinkedClassBulkLoader::preload_classes_for_loader(ClassLoaderData* loade } void AOTLinkedClassBulkLoader::preload_classes_for_loader_impl(Handle loader_obj, TRAPS) { - if (SystemDictionary::is_builtin_class_loader(loader_obj())) { - // classes for builtin loaders should have already been loaded during startup - return; - } + assert(!SystemDictionary::is_builtin_class_loader(loader_obj()), "must not be called for builtin loaders"); Symbol* aot_id = java_lang_ClassLoader::loader_data(loader_obj())->aot_identity(); if (aot_id == nullptr) { return; } AOTLinkedClassTableForCustomLoader* table = AOTClassLinker::get_prelinked_table(aot_id); - if (table->is_loaded()) { - return; - } - oop parent = java_lang_ClassLoader::parent(loader_obj()); - preload_classes_for_loader_impl(Handle(THREAD, parent), CHECK); preload_classes_in_table(table->class_list(), aot_id->as_C_string(), loader_obj, CHECK); MonitorLocker mu1(SystemDictionary_lock); mark_initiating_loader(THREAD, loader_obj()); - table->set_loaded(true); } class DictionaryCopier : public KlassClosure { @@ -250,18 +241,13 @@ void AOTLinkedClassBulkLoader::link_classes_for_loader(ClassLoaderData* loader_d void AOTLinkedClassBulkLoader::link_classes_for_loader_impl(Handle loader_obj, TRAPS) { precond(CDSConfig::is_using_aot_linked_classes()); - if (SystemDictionary::is_builtin_class_loader(loader_obj())) { - // classes for builtin loaders should have already been loaded during startup - return; - } + assert(!SystemDictionary::is_builtin_class_loader(loader_obj()), "must not be called for builtin loaders"); Symbol* aot_id = java_lang_ClassLoader::loader_data(loader_obj())->aot_identity(); if (aot_id == nullptr) { return; } AOTLinkedClassTableForCustomLoader* table = AOTClassLinker::get_prelinked_table(aot_id); - oop parent = java_lang_ClassLoader::parent(loader_obj()); - link_classes_for_loader_impl(Handle(THREAD, parent), CHECK); link_classes_in_table(table->class_list(), CHECK); } diff --git a/src/hotspot/share/classfile/classLoaderData.cpp b/src/hotspot/share/classfile/classLoaderData.cpp index de8ac27febd..46873d2adfa 100644 --- a/src/hotspot/share/classfile/classLoaderData.cpp +++ b/src/hotspot/share/classfile/classLoaderData.cpp @@ -1133,17 +1133,3 @@ bool ClassLoaderData::contains_klass(Klass* klass) { } return false; } - -void ClassLoaderData::preload_classes() { - if (CDSConfig::is_using_aot_linked_classes() && CDSConfig::supports_custom_loaders()) { - JavaThread* thread = JavaThread::current(); - AOTLinkedClassBulkLoader::preload_classes_for_loader(this, thread); - if (thread->has_pending_exception()) { - AOTLinkedClassBulkLoader::exit_on_exception(thread); - } - AOTLinkedClassBulkLoader::link_classes_for_loader(this, thread); - if (thread->has_pending_exception()) { - AOTLinkedClassBulkLoader::exit_on_exception(thread); - } - } -} diff --git a/src/hotspot/share/classfile/classLoaderData.hpp b/src/hotspot/share/classfile/classLoaderData.hpp index 5b3b3498811..1e0a595552c 100644 --- a/src/hotspot/share/classfile/classLoaderData.hpp +++ b/src/hotspot/share/classfile/classLoaderData.hpp @@ -360,7 +360,6 @@ class ClassLoaderData : public CHeapObj { const char* loader_name_and_id() const; Symbol* name_and_id() const { return _name_and_id; } Symbol* aot_identity() const { return _aot_identity; } - void preload_classes(); unsigned identity_hash() const { return (unsigned)((uintptr_t)this >> LogBytesPerWord); diff --git a/src/hotspot/share/classfile/classLoaderDataGraph.cpp b/src/hotspot/share/classfile/classLoaderDataGraph.cpp index d519fbeb6a1..b6f7915db71 100644 --- a/src/hotspot/share/classfile/classLoaderDataGraph.cpp +++ b/src/hotspot/share/classfile/classLoaderDataGraph.cpp @@ -181,7 +181,6 @@ ClassLoaderData* ClassLoaderDataGraph::add_to_graph(Handle loader, bool has_clas ClassLoaderData* ClassLoaderDataGraph::add(Handle loader, bool has_class_mirror_holder) { MutexLocker ml(ClassLoaderDataGraph_lock); ClassLoaderData* loader_data = add_to_graph(loader, has_class_mirror_holder); - loader_data->preload_classes(); return loader_data; } diff --git a/src/hotspot/share/classfile/classLoaderDataGraph.hpp b/src/hotspot/share/classfile/classLoaderDataGraph.hpp index 3f2cb82fa25..803f227dcf3 100644 --- a/src/hotspot/share/classfile/classLoaderDataGraph.hpp +++ b/src/hotspot/share/classfile/classLoaderDataGraph.hpp @@ -57,7 +57,7 @@ class ClassLoaderDataGraph : public AllStatic { static ClassLoaderData* add_to_graph(Handle class_loader, bool has_class_mirror_holder); public: - static ClassLoaderData* find_or_create(Handle class_loader, bool& created); + static ClassLoaderData* find_or_create(Handle class_loader); static ClassLoaderData* add(Handle class_loader, bool has_class_mirror_holder); static void clean_module_and_package_info(); static void purge(bool at_safepoint); diff --git a/src/hotspot/share/classfile/classLoaderDataGraph.inline.hpp b/src/hotspot/share/classfile/classLoaderDataGraph.inline.hpp index 3c846cd222c..767db20a8f0 100644 --- a/src/hotspot/share/classfile/classLoaderDataGraph.inline.hpp +++ b/src/hotspot/share/classfile/classLoaderDataGraph.inline.hpp @@ -32,14 +32,12 @@ #include "runtime/atomicAccess.hpp" #include "runtime/orderAccess.hpp" -inline ClassLoaderData *ClassLoaderDataGraph::find_or_create(Handle loader, bool& created) { +inline ClassLoaderData *ClassLoaderDataGraph::find_or_create(Handle loader) { guarantee(loader() != nullptr && oopDesc::is_oop(loader()), "Loader must be oop"); // Gets the class loader data out of the java/lang/ClassLoader object, if non-null // it's already in the loader_data, so no need to add - created = true; ClassLoaderData* loader_data = java_lang_ClassLoader::loader_data_acquire(loader()); if (loader_data) { - created = false; return loader_data; } return ClassLoaderDataGraph::add(loader, false); diff --git a/src/hotspot/share/classfile/systemDictionary.cpp b/src/hotspot/share/classfile/systemDictionary.cpp index a981b789303..d69b4da1b94 100644 --- a/src/hotspot/share/classfile/systemDictionary.cpp +++ b/src/hotspot/share/classfile/systemDictionary.cpp @@ -192,9 +192,8 @@ ClassLoaderData* SystemDictionary::register_loader(Handle class_loader, bool cre // Add a new class loader data to the graph. return ClassLoaderDataGraph::add(class_loader, true); } else { - bool created = false; ClassLoaderData* loader_data = (class_loader() == nullptr) ? ClassLoaderData::the_null_class_loader_data() : - ClassLoaderDataGraph::find_or_create(class_loader, created); + ClassLoaderDataGraph::find_or_create(class_loader); return loader_data; } } diff --git a/src/hotspot/share/include/jvm.h b/src/hotspot/share/include/jvm.h index 755cea9384c..5455ac0d8ad 100644 --- a/src/hotspot/share/include/jvm.h +++ b/src/hotspot/share/include/jvm.h @@ -756,7 +756,7 @@ JVM_DesiredAssertionStatus(JNIEnv *env, jclass unused, jclass cls); JNIEXPORT jobject JNICALL JVM_AssertionStatusDirectives(JNIEnv *env, jclass unused); -JNIEXPORT void JNICALL +JNIEXPORT jboolean JNICALL JVM_RegisterAsAOTCompatibleLoader(JNIEnv *env, jobject loader); /* diff --git a/src/hotspot/share/oops/cpCache.cpp b/src/hotspot/share/oops/cpCache.cpp index 75cdcb5310a..15cb650563f 100644 --- a/src/hotspot/share/oops/cpCache.cpp +++ b/src/hotspot/share/oops/cpCache.cpp @@ -541,8 +541,8 @@ void ConstantPoolCache::remove_resolved_indy_entries_if_non_deterministic() { bool ConstantPoolCache::can_archive_resolved_method(ConstantPool* src_cp, ResolvedMethodEntry* method_entry) { LogStreamHandle(Trace, aot, resolve) log; InstanceKlass* pool_holder = constant_pool()->pool_holder(); - if (pool_holder->defined_by_other_loaders()) { - // Archiving resolved cp entries for classes from non-builtin loaders + if (pool_holder->defined_by_other_loaders() && !pool_holder->is_defined_by_aot_safe_custom_loader()) { + // Archiving resolved cp entries for classes from non-builtin loaders or non-aot-safe custom loaders // is not yet supported. return false; } diff --git a/src/hotspot/share/prims/jvm.cpp b/src/hotspot/share/prims/jvm.cpp index 3a40a2a14ce..e6e15632bd5 100644 --- a/src/hotspot/share/prims/jvm.cpp +++ b/src/hotspot/share/prims/jvm.cpp @@ -24,6 +24,7 @@ #include "cds/aotClassInitializer.hpp" #include "cds/aotConstantPoolResolver.hpp" +#include "cds/aotLinkedClassBulkLoader.hpp" #include "cds/aotMetaspace.hpp" #include "cds/cdsConfig.hpp" #include "cds/classListParser.hpp" @@ -2283,9 +2284,17 @@ JVM_ENTRY_PROF(jobject, JVM_AssertionStatusDirectives, JVM_AssertionStatusDirect return JNIHandles::make_local(THREAD, asd); JVM_END -JVM_ENTRY_PROF(void, JVM_RegisterAsAOTCompatibleLoader, JVM_RegisterAsAOTCompatibleLoader(JNIEnv *env, jobject loader)) +JVM_ENTRY_PROF(jboolean, JVM_RegisterAsAOTCompatibleLoader, JVM_RegisterAsAOTCompatibleLoader(JNIEnv *env, jobject loader)) //FinalImageRecipes::add_aot_safe_custom_loader(JNIHandles::resolve_non_null(loader), CHECK); //Nothing to do here for now + if (CDSConfig::is_using_aot_linked_classes() && CDSConfig::supports_custom_loaders()) { + Handle h_loader(THREAD, JNIHandles::resolve_non_null(loader)); + ClassLoaderData *loader_data = SystemDictionary::register_loader(h_loader); + AOTLinkedClassBulkLoader::preload_classes_for_loader(loader_data, CHECK_AND_CLEAR_false); + AOTLinkedClassBulkLoader::link_classes_for_loader(loader_data, CHECK_AND_CLEAR_false); + return JNI_TRUE; + } + return JNI_FALSE; JVM_END // Verification //////////////////////////////////////////////////////////////////////////////// diff --git a/src/java.base/share/classes/java/lang/ClassLoader.java b/src/java.base/share/classes/java/lang/ClassLoader.java index 48e81ae4265..581e779d1f9 100644 --- a/src/java.base/share/classes/java/lang/ClassLoader.java +++ b/src/java.base/share/classes/java/lang/ClassLoader.java @@ -2675,7 +2675,7 @@ protected void setAOTIdentity(String id) { registerAsAOTCompatibleLoader(); } - private native void registerAsAOTCompatibleLoader(); + private native boolean registerAsAOTCompatibleLoader(); } /* diff --git a/src/java.base/share/native/libjava/ClassLoader.c b/src/java.base/share/native/libjava/ClassLoader.c index af07e5589c4..4c272559edf 100644 --- a/src/java.base/share/native/libjava/ClassLoader.c +++ b/src/java.base/share/native/libjava/ClassLoader.c @@ -36,7 +36,7 @@ static JNINativeMethod methods[] = { {"retrieveDirectives", "()Ljava/lang/AssertionStatusDirectives;", (void *)&JVM_AssertionStatusDirectives}, - {"registerAsAOTCompatibleLoader", "()V", (void *)&JVM_RegisterAsAOTCompatibleLoader} + {"registerAsAOTCompatibleLoader", "()Z", (void *)&JVM_RegisterAsAOTCompatibleLoader} }; JNIEXPORT void JNICALL From 4ecc907a20f1dc2b642fba5fff971f438ee48eae Mon Sep 17 00:00:00 2001 From: Ashutosh Mehra Date: Thu, 5 Feb 2026 14:38:08 -0500 Subject: [PATCH 08/30] Add support for URLClassLoader Signed-off-by: Ashutosh Mehra --- src/hotspot/share/cds/aotClassLocation.cpp | 78 +++++++++++++++++++ src/hotspot/share/cds/aotClassLocation.hpp | 27 ++++++- src/hotspot/share/cds/finalImageRecipes.cpp | 15 ++++ src/hotspot/share/include/jvm.h | 2 + src/hotspot/share/prims/jvm.cpp | 20 ++++- .../share/classes/java/lang/ClassLoader.java | 24 +++++- .../classes/java/net/URLClassLoader.java | 68 +++++++++++++++- 7 files changed, 227 insertions(+), 7 deletions(-) diff --git a/src/hotspot/share/cds/aotClassLocation.cpp b/src/hotspot/share/cds/aotClassLocation.cpp index 17cf3bfaf24..6a540aad4eb 100644 --- a/src/hotspot/share/cds/aotClassLocation.cpp +++ b/src/hotspot/share/cds/aotClassLocation.cpp @@ -26,6 +26,7 @@ #include "cds/aotLogging.hpp" #include "cds/aotMetaspace.hpp" #include "cds/archiveBuilder.hpp" +#include "cds/archiveUtils.inline.hpp" #include "cds/cdsConfig.hpp" #include "cds/dynamicArchive.hpp" #include "cds/filemap.hpp" @@ -33,6 +34,8 @@ #include "classfile/classLoader.hpp" #include "classfile/classLoaderData.hpp" #include "classfile/javaClasses.hpp" +#include "classfile/compactHashtable.hpp" +#include "classfile/symbolTable.hpp" #include "logging/log.hpp" #include "logging/logStream.hpp" #include "memory/metadataFactory.hpp" @@ -43,6 +46,7 @@ #include "runtime/arguments.hpp" #include "utilities/classpathStream.hpp" #include "utilities/formatBuffer.hpp" +#include "utilities/resizableHashTable.hpp" #include "utilities/stringUtils.hpp" #include @@ -156,6 +160,13 @@ class AllClassLocationStreams { } }; +class URLClassLoaderClassLocationStream : public ClassLocationStream { +public: + URLClassLoaderClassLocationStream(const char* classpath) : ClassLocationStream() { + add_paths_in_classpath(classpath); + } +}; + static bool has_jar_suffix(const char* filename) { // In jdk.internal.module.ModulePath.readModule(), it checks for the ".jar" suffix. // Performing the same check here. @@ -1087,3 +1098,70 @@ void AOTClassLocationConfig::print_on(outputStream* st) const { st->print_cr("(%-6s) [%d] = %s", type, i, path); } } + +typedef ResizeableHashTable AOTIdToURLLoaderClasspath; +static AOTIdToURLLoaderClasspath* _aot_id_to_classpath = nullptr; + +Symbol* URLClassLoaderClasspathSupport::classpath_to_aotid(const char* classpath) { + char* str = os::strdup(classpath, mtInternal); + return SymbolTable::new_symbol(str); +} + +void URLClassLoaderClasspathSupport::add_urlclassloader_classpath(ClassLoaderData* loader_data, const char* classpath) { + if (_aot_id_to_classpath == nullptr) { + _aot_id_to_classpath = new (mtClass) AOTIdToURLLoaderClasspath(11, 1000); + } + Symbol* aot_id = loader_data->aot_identity(); + if (_aot_id_to_classpath->contains(aot_id)) { + // cannot allow aot_id clash; return without doing anything + return; + } + GrowableClassLocationArray* locations = new GrowableClassLocationArray(10); + URLClassLoaderClassLocationStream css(classpath); + for (css.start(); css.has_next(); ) { + const char* path = css.get_next(); + AOTClassLocation* cs = AOTClassLocation::allocate(JavaThread::current(), path, locations->length(), AOTClassLocation::Group::URLCLASSLOADER_CLASSPATH, false); + log_info(class, path)("path [%d] = %s", locations->length(), path); + locations->append(cs); + } + _aot_id_to_classpath->put(aot_id, locations); +} + +static inline bool ucc_symbol_equals(URLClassLoaderClasspath* ucc, Symbol* loader_id, int len_unused) { + return ucc->loader_id()->equals(loader_id); +} + +class ArchivedAOTIdToClasspathMap : public OffsetCompactHashtable {}; +static ArchivedAOTIdToClasspathMap _archived_aot_id_to_classpath; + +class URLClassLoaderClasspathArchiver : StackObj { +private: + CompactHashtableWriter* _writer; + ArchiveBuilder* _builder; +public: + URLClassLoaderClasspathArchiver(CompactHashtableWriter* writer) : _writer(writer), + _builder(ArchiveBuilder::current()) + {} + + bool do_entry(Symbol* loader_id, GrowableClassLocationArray* class_locations) { + URLClassLoaderClasspath* ucc = (URLClassLoaderClasspath*)ArchiveBuilder::ro_region_alloc(sizeof(URLClassLoaderClasspath)); + assert(_builder->has_been_archived(loader_id), "must be"); + Symbol* buffered_sym = _builder->get_buffered_addr(loader_id); + ucc->init(buffered_sym, ArchiveUtils::archive_array(class_locations)); + ArchivePtrMarker::mark_pointer(ucc->loader_id_addr()); + ArchivePtrMarker::mark_pointer(ucc->class_locations_addr()); + unsigned int hash = Symbol::symbol_hash(loader_id); + u4 delta = _builder->buffer_to_offset_u4((address)ucc); + _writer->add(hash, delta); + return true; + } +}; + +void URLClassLoaderClasspathSupport::archive_map() { + CompactHashtableStats stats; + CompactHashtableWriter writer(_aot_id_to_classpath->number_of_entries(), &stats); + URLClassLoaderClasspathArchiver archiver(&writer); + _aot_id_to_classpath->iterate(&archiver); + writer.dump(&_archived_aot_id_to_classpath, "archived prelinked table"); +} diff --git a/src/hotspot/share/cds/aotClassLocation.hpp b/src/hotspot/share/cds/aotClassLocation.hpp index 89a5e6bc939..8908baacd6e 100644 --- a/src/hotspot/share/cds/aotClassLocation.hpp +++ b/src/hotspot/share/cds/aotClassLocation.hpp @@ -60,7 +60,8 @@ class AOTClassLocation { MODULES_IMAGE, BOOT_CLASSPATH, APP_CLASSPATH, - MODULE_PATH + MODULE_PATH, + URLCLASSLOADER_CLASSPATH }; private: enum class FileType : int { @@ -115,6 +116,23 @@ class AOTClassLocation { bool check(const char* runtime_path, bool has_aot_linked_classes) const; }; +using GrowableClassLocationArray = GrowableArrayCHeap; + +class URLClassLoaderClasspath { + private: + Symbol* _loader_id; + Array* _class_locations; + public: + void init(Symbol* aot_id, Array* class_locations) { + _loader_id = aot_id; + _class_locations = class_locations; + } + Symbol* loader_id() const { return _loader_id; } + address* loader_id_addr() const { return (address*)&_loader_id; } + Array* class_locations() const { return _class_locations; } + address* class_locations_addr() const { return (address*)&_class_locations; } +}; + // AOTClassLocationConfig // // Keep track of the set of AOTClassLocations used when an AOTCache is created. @@ -134,7 +152,6 @@ class AOTClassLocation { class AOTClassLocationConfig : public CHeapObj { using Group = AOTClassLocation::Group; - using GrowableClassLocationArray = GrowableArrayCHeap; // Note: both of the following are non-null if we are dumping a dynamic archive. static AOTClassLocationConfig* _dumptime_instance; @@ -277,5 +294,11 @@ class AOTClassLocationConfig : public CHeapObj { static void print(); }; +class URLClassLoaderClasspathSupport : AllStatic { +public: + static Symbol* classpath_to_aotid(const char* classpath); + static void add_urlclassloader_classpath(ClassLoaderData* loader_data, const char* classpath); + static void archive_map(); +}; #endif // SHARE_CDS_AOTCLASSLOCATION_HPP diff --git a/src/hotspot/share/cds/finalImageRecipes.cpp b/src/hotspot/share/cds/finalImageRecipes.cpp index e08f6852cc2..9a2224b158a 100644 --- a/src/hotspot/share/cds/finalImageRecipes.cpp +++ b/src/hotspot/share/cds/finalImageRecipes.cpp @@ -166,6 +166,9 @@ void FinalImageRecipes::apply_recipes_for_constantpool(JavaThread* current) { int flags = _flags->at(i); if (cp_indices != nullptr) { InstanceKlass* ik = InstanceKlass::cast(_all_klasses->at(i)); + if (!strcmp(ik->external_name(), "org.openjdk.aot.testclass.Foo")) { + log_info(cds)("Applying constant pool recipes for %s", ik->external_name()); + } if (ik->is_loaded()) { ResourceMark rm(current); ConstantPool* cp = ik->constants(); @@ -262,6 +265,9 @@ void FinalImageRecipes::load_all_classes(TRAPS) { int flags = _flags->at(i); if (k->is_instance_klass()) { InstanceKlass* ik = InstanceKlass::cast(k); + if (!strcmp(ik->external_name(), "org/openjdk/aot/testclass/Foo")) { + log_info(cds)("FinalImageRecipes loading class %s", ik->external_name()); + } if (ik->defined_by_other_loaders() && !k->is_defined_by_aot_safe_custom_loader()) { SystemDictionaryShared::init_dumptime_info(ik); SystemDictionaryShared::add_unregistered_class(THREAD, ik); @@ -287,6 +293,15 @@ void FinalImageRecipes::load_all_classes(TRAPS) { Handle unreg_class_loader = UnregisteredClasses::unregistered_class_loader(THREAD); assert(unreg_class_loader.not_null(), "must be"); Klass* actual = SystemDictionary::resolve_or_fail(ik->name(), unreg_class_loader, true, CHECK); + if (actual != ik) { + ResourceMark rm(THREAD); + log_error(aot)("Unable to resolve class from CDS archive: %s", ik->external_name()); + log_error(aot)("Expected: " INTPTR_FORMAT ", actual: " INTPTR_FORMAT, p2i(ik), p2i(actual)); + log_error(aot)("Please check if your VM command-line is the same as in the training run"); + AOTMetaspace::unrecoverable_writing_error(); + } + assert(ik->is_loaded(), "must be"); + ik->link_class(CHECK); } } } diff --git a/src/hotspot/share/include/jvm.h b/src/hotspot/share/include/jvm.h index 5455ac0d8ad..5654d428655 100644 --- a/src/hotspot/share/include/jvm.h +++ b/src/hotspot/share/include/jvm.h @@ -759,6 +759,8 @@ JVM_AssertionStatusDirectives(JNIEnv *env, jclass unused); JNIEXPORT jboolean JNICALL JVM_RegisterAsAOTCompatibleLoader(JNIEnv *env, jobject loader); +JNIEXPORT jboolean JNICALL +JVM_RegisterURLClassLoaderAsAOTSafeLoader(JNIEnv *env, jobject loader, jstring classpath); /* * java.lang.ref.Finalizer */ diff --git a/src/hotspot/share/prims/jvm.cpp b/src/hotspot/share/prims/jvm.cpp index e6e15632bd5..3d864627efb 100644 --- a/src/hotspot/share/prims/jvm.cpp +++ b/src/hotspot/share/prims/jvm.cpp @@ -23,6 +23,7 @@ */ #include "cds/aotClassInitializer.hpp" +#include "cds/aotClassLocation.hpp" #include "cds/aotConstantPoolResolver.hpp" #include "cds/aotLinkedClassBulkLoader.hpp" #include "cds/aotMetaspace.hpp" @@ -2285,8 +2286,6 @@ JVM_ENTRY_PROF(jobject, JVM_AssertionStatusDirectives, JVM_AssertionStatusDirect JVM_END JVM_ENTRY_PROF(jboolean, JVM_RegisterAsAOTCompatibleLoader, JVM_RegisterAsAOTCompatibleLoader(JNIEnv *env, jobject loader)) - //FinalImageRecipes::add_aot_safe_custom_loader(JNIHandles::resolve_non_null(loader), CHECK); - //Nothing to do here for now if (CDSConfig::is_using_aot_linked_classes() && CDSConfig::supports_custom_loaders()) { Handle h_loader(THREAD, JNIHandles::resolve_non_null(loader)); ClassLoaderData *loader_data = SystemDictionary::register_loader(h_loader); @@ -2297,6 +2296,23 @@ JVM_ENTRY_PROF(jboolean, JVM_RegisterAsAOTCompatibleLoader, JVM_RegisterAsAOTCom return JNI_FALSE; JVM_END +JVM_ENTRY_PROF(jboolean, JVM_RegisterURLClassLoaderAsAOTSafeLoader, JVM_RegisterURLClassLoaderAsAOTSafeLoader(JNIEnv *env, jobject loader, jstring classpath)) + if (CDSConfig::supports_custom_loaders()) { + ResourceMark rm(THREAD); + Handle h_loader(THREAD, JNIHandles::resolve_non_null(loader)); + ClassLoaderData *loader_data = SystemDictionary::register_loader(h_loader); + assert(loader_data->aot_identity() != nullptr, "must be"); + if (CDSConfig::is_dumping_preimage_static_archive()) { + const char *classpath_str = java_lang_String::as_utf8_string(JNIHandles::resolve_non_null(classpath)); + URLClassLoaderClasspathSupport::add_urlclassloader_classpath(loader_data, classpath_str); + } else if (CDSConfig::is_using_aot_linked_classes()) { + AOTLinkedClassBulkLoader::preload_classes_for_loader(loader_data, CHECK_AND_CLEAR_false); + AOTLinkedClassBulkLoader::link_classes_for_loader(loader_data, CHECK_AND_CLEAR_false); + } + return JNI_TRUE; + } + return JNI_FALSE; +JVM_END // Verification //////////////////////////////////////////////////////////////////////////////// // Reflection for the verifier ///////////////////////////////////////////////////////////////// diff --git a/src/java.base/share/classes/java/lang/ClassLoader.java b/src/java.base/share/classes/java/lang/ClassLoader.java index 581e779d1f9..0bdcbb08658 100644 --- a/src/java.base/share/classes/java/lang/ClassLoader.java +++ b/src/java.base/share/classes/java/lang/ClassLoader.java @@ -2670,9 +2670,29 @@ ProtectionDomain getDefaultProtectionDomain() { * @param id * unique id of the classloader object. */ - protected void setAOTIdentity(String id) { + public void setAOTIdentity(String id) { aotIdentity = id; - registerAsAOTCompatibleLoader(); + //registerAsAOTCompatibleLoader(); + } + + /** + * Returns unique and repeatable ID of the classloader + * + * @return unique id of the classloader object. + */ + public String getAOTIdentity() { + return aotIdentity; + } + + /** + * Returns true if the parent is a builtin loader + * + * @return true if the parent is a builtin loader + */ + @SuppressWarnings("this-escape") + public final boolean hasBuiltinLoaderAsParent() { + ClassLoader parent = getParent(); + return parent == null || parent == getBuiltinPlatformClassLoader() || parent == getBuiltinAppClassLoader(); } private native boolean registerAsAOTCompatibleLoader(); diff --git a/src/java.base/share/classes/java/net/URLClassLoader.java b/src/java.base/share/classes/java/net/URLClassLoader.java index 4b579554e0a..2c6265afed8 100644 --- a/src/java.base/share/classes/java/net/URLClassLoader.java +++ b/src/java.base/share/classes/java/net/URLClassLoader.java @@ -26,6 +26,7 @@ package java.net; import java.io.Closeable; +import java.io.File; import java.io.IOException; import java.io.InputStream; import java.security.CodeSigner; @@ -64,10 +65,11 @@ * @author David Connelly * @since 1.2 */ +@SuppressWarnings("this-escape") public class URLClassLoader extends SecureClassLoader implements Closeable { /* The search path for classes and resources */ private final URLClassPath ucp; - + private static final boolean DEBUG = Boolean.getBoolean("urlclassloader.debug"); /** * Constructs a new URLClassLoader for the given URLs. The URLs will be * searched in the order specified for classes and resources after first @@ -92,6 +94,7 @@ public class URLClassLoader extends SecureClassLoader implements Closeable { public URLClassLoader(URL[] urls, ClassLoader parent) { super(parent); this.ucp = new URLClassPath(urls); + registerAsAOTSafe(urls); } /** @@ -111,6 +114,7 @@ public URLClassLoader(URL[] urls, ClassLoader parent) { public URLClassLoader(URL[] urls) { super(); this.ucp = new URLClassPath(urls); + registerAsAOTSafe(urls); } /** @@ -138,6 +142,7 @@ public URLClassLoader(URL[] urls, ClassLoader parent, URLStreamHandlerFactory factory) { super(parent); this.ucp = new URLClassPath(urls, factory); + registerAsAOTSafe(urls); } @@ -171,6 +176,7 @@ public URLClassLoader(String name, ClassLoader parent) { super(name, parent); this.ucp = new URLClassPath(urls); + registerAsAOTSafe(urls); } /** @@ -202,6 +208,7 @@ public URLClassLoader(String name, URL[] urls, ClassLoader parent, URLStreamHandlerFactory factory) { super(name, parent); this.ucp = new URLClassPath(urls, factory); + registerAsAOTSafe(urls); } /* A map (used as a set) to keep track of closeable local resources @@ -639,4 +646,63 @@ public static URLClassLoader newInstance(final URL[] urls) { static { ClassLoader.registerAsParallelCapable(); } + + // It is AOT-safe if only jar files are present in it the urls + private boolean canRegisterAsAOTSafe(final URL[] urls) { + boolean isAOTSafe = true; + for (URL url: urls) { + if (!url.getProtocol().equals("file")) { + isAOTSafe = false; + break; + } + if (!url.getPath().endsWith(".jar")) { + isAOTSafe = false; + break; + } + } + return isAOTSafe; + } + + private String createClassPath(final URL[] urls) { + StringBuilder sb = new StringBuilder(); + for (URL url: urls) { + String path = url.getPath(); + sb.append(path); + sb.append(File.pathSeparator); + } + return sb.toString(); + } + + private String convertToAOTId(String classpath) { + return classpath; + } + + private boolean registerAsAOTSafe(final URL[] urls) { + String classpath = createClassPath(urls); + if (!hasBuiltinLoaderAsParent() && getParent().getAOTIdentity() == null) { + if (DEBUG) { + System.out.println("DEBUG: URLClassLoader with classpath \"" + classpath + "\" cannot be registered as AOT-safe (reason=parent not aot-safe)"); + } + return false; + } + if (canRegisterAsAOTSafe(urls)) { + setAOTIdentity(convertToAOTId(classpath)); + boolean rc = registerAsAOTSafeImpl(classpath); + if (DEBUG) { + if (rc) { + System.out.println("DEBUG: Registered URLClassLoader as AOT-safe with classpath " + classpath); + } else { + System.out.println("DEBUG: Failed to register URLClassLoader as AOT-safe with classpath " + classpath); + } + } + return rc; + } else { + if (DEBUG) { + System.out.println("DEBUG: URLClassLoader with classpath \"" + classpath + "\" cannot be registered as AOT-safe (reason=urls contain non-jar files)"); + } + } + return false; + } + + private native boolean registerAsAOTSafeImpl(String classpath); } From 71407bd7fbd1f0b58217a003f907644286d3dc31 Mon Sep 17 00:00:00 2001 From: Ashutosh Mehra Date: Fri, 6 Feb 2026 00:08:34 -0500 Subject: [PATCH 09/30] Cleanup Signed-off-by: Ashutosh Mehra --- src/hotspot/share/cds/aotMetaspace.cpp | 5 ---- .../share/classfile/classLoaderDataShared.cpp | 23 +++++++++---------- .../share/classfile/classLoaderDataShared.hpp | 3 --- 3 files changed, 11 insertions(+), 20 deletions(-) diff --git a/src/hotspot/share/cds/aotMetaspace.cpp b/src/hotspot/share/cds/aotMetaspace.cpp index e28849df03e..630c744e6e6 100644 --- a/src/hotspot/share/cds/aotMetaspace.cpp +++ b/src/hotspot/share/cds/aotMetaspace.cpp @@ -737,11 +737,6 @@ char* VM_PopulateDumpSharedSpace::dump_early_read_only_tables() { char* VM_PopulateDumpSharedSpace::dump_read_only_tables(AOTClassLocationConfig*& cl_config) { ArchiveBuilder::OtherROAllocMark mark; -#if 0 - if (CDSConfig::is_dumping_full_module_graph()) { - ClassLoaderDataShared::write_cld_table(); - } -#endif SystemDictionaryShared::write_to_archive(); cl_config = AOTClassLocationConfig::dumptime()->write_to_archive(); AOTClassLinker::write_to_archive(); diff --git a/src/hotspot/share/classfile/classLoaderDataShared.cpp b/src/hotspot/share/classfile/classLoaderDataShared.cpp index f201e44b36b..4ba2783771e 100644 --- a/src/hotspot/share/classfile/classLoaderDataShared.cpp +++ b/src/hotspot/share/classfile/classLoaderDataShared.cpp @@ -99,7 +99,7 @@ void ArchivedClassLoaderData::allocate(ClassLoaderData* loader_data) { // So we store the packages/modules in Arrays. At runtime, we create // the hashtables using these arrays. _packages = loader_data->packages()->allocate_archived_entries(); - _modules = loader_data->modules()->allocate_archived_entries(); + _modules = loader_data->modules()->allocate_archived_entries(); _unnamed_module = loader_data->unnamed_module()->allocate_archived_entry(); } } @@ -153,7 +153,6 @@ void ArchivedClassLoaderData::clear_archived_oops() { // ------------------------------ void ClassLoaderDataShared::load_archived_platform_and_system_class_loaders() { - #if INCLUDE_CDS_JAVA_HEAP // The streaming object loader prefers loading the class loader related objects before // the CLD constructor which has a NoSafepointVerifier. @@ -258,21 +257,21 @@ ModuleEntry* ClassLoaderDataShared::archived_boot_unnamed_module() { ModuleEntry* ClassLoaderDataShared::archived_unnamed_module(ClassLoaderData* loader_data) { ModuleEntry* archived_module = nullptr; - if (CDSConfig::is_using_full_module_graph()) { - if (!Universe::is_module_initialized()) { - precond(_platform_loader_root_index >= 0); - precond(_system_loader_root_index >= 0); - - if (loader_data->class_loader() == HeapShared::get_root(_platform_loader_root_index)) { - archived_module = _archived_platform_loader_data.unnamed_module(); - } else if (loader_data->class_loader() == HeapShared::get_root(_system_loader_root_index)) { - archived_module = _archived_system_loader_data.unnamed_module(); - } + if (!Universe::is_module_initialized() && CDSConfig::is_using_full_module_graph()) { + precond(_platform_loader_root_index >= 0); + precond(_system_loader_root_index >= 0); + + if (loader_data->class_loader() == HeapShared::get_root(_platform_loader_root_index)) { + archived_module = _archived_platform_loader_data.unnamed_module(); + } else if (loader_data->class_loader() == HeapShared::get_root(_system_loader_root_index)) { + archived_module = _archived_system_loader_data.unnamed_module(); } } + return archived_module; } + void ClassLoaderDataShared::clear_archived_oops() { assert(!CDSConfig::is_using_full_module_graph(), "must be"); _archived_boot_loader_data.clear_archived_oops(); diff --git a/src/hotspot/share/classfile/classLoaderDataShared.hpp b/src/hotspot/share/classfile/classLoaderDataShared.hpp index bbe27aa81f0..39d0a89418f 100644 --- a/src/hotspot/share/classfile/classLoaderDataShared.hpp +++ b/src/hotspot/share/classfile/classLoaderDataShared.hpp @@ -55,9 +55,6 @@ class ClassLoaderDataShared : AllStatic { static ModuleEntry* archived_unnamed_module(ClassLoaderData* loader_data); #endif // INCLUDE_CDS_JAVA_HEAP static bool is_full_module_graph_loaded() { return _full_module_graph_loaded; } - static void write_cld_table(); - static void restore_custom_loader_archived_data(ClassLoaderData* loader_data); - static void get_archived_custom_loader_objs(GrowableArray& loader_obj_list); }; #endif // SHARE_CLASSFILE_CLASSLOADERDATASHARED_HPP From f89973eac547419d63b28e81121003b5a340d964 Mon Sep 17 00:00:00 2001 From: Ashutosh Mehra Date: Fri, 6 Feb 2026 15:49:10 -0500 Subject: [PATCH 10/30] Add validation of classpath for URLClassLoaders Signed-off-by: Ashutosh Mehra --- src/hotspot/share/cds/aotClassLocation.cpp | 81 +++++++++++++++---- src/hotspot/share/cds/aotClassLocation.hpp | 10 ++- src/hotspot/share/cds/aotMetaspace.cpp | 2 + .../share/classfile/systemDictionary.cpp | 1 + .../classfile/systemDictionaryShared.cpp | 4 +- src/hotspot/share/prims/jvm.cpp | 13 +-- .../classes/java/net/URLClassLoader.java | 7 +- 7 files changed, 91 insertions(+), 27 deletions(-) diff --git a/src/hotspot/share/cds/aotClassLocation.cpp b/src/hotspot/share/cds/aotClassLocation.cpp index 6a540aad4eb..357692323c5 100644 --- a/src/hotspot/share/cds/aotClassLocation.cpp +++ b/src/hotspot/share/cds/aotClassLocation.cpp @@ -1102,15 +1102,34 @@ void AOTClassLocationConfig::print_on(outputStream* st) const { typedef ResizeableHashTable AOTIdToURLLoaderClasspath; static AOTIdToURLLoaderClasspath* _aot_id_to_classpath = nullptr; -Symbol* URLClassLoaderClasspathSupport::classpath_to_aotid(const char* classpath) { - char* str = os::strdup(classpath, mtInternal); - return SymbolTable::new_symbol(str); +static inline bool ucc_symbol_equals(URLClassLoaderClasspath* ucc, Symbol* loader_id, int len_unused) { + return ucc->loader_id()->equals(loader_id); } -void URLClassLoaderClasspathSupport::add_urlclassloader_classpath(ClassLoaderData* loader_data, const char* classpath) { - if (_aot_id_to_classpath == nullptr) { - _aot_id_to_classpath = new (mtClass) AOTIdToURLLoaderClasspath(11, 1000); +class ArchivedAOTIdToClasspathMap : public OffsetCompactHashtable {}; +static ArchivedAOTIdToClasspathMap _archived_aot_id_to_classpath; + +void URLClassLoaderClasspathSupport::init() { + _aot_id_to_classpath = new (mtClass) AOTIdToURLLoaderClasspath(11, 1000); + if (CDSConfig::is_dumping_final_static_archive()) { + reload_runtime_map(); } +} + +void URLClassLoaderClasspathSupport::reload_runtime_map() { + _archived_aot_id_to_classpath.iterate([&](URLClassLoaderClasspath* ucc) { + GrowableClassLocationArray* locations = new GrowableClassLocationArray(ucc->num_entries()); + for (int i = 0; i < ucc->num_entries(); i++) { + locations->append(ucc->class_location_at(i)); + log_info(class, path)("path [%d] = %s", i, ucc->class_location_at(i)->path()); + } + _aot_id_to_classpath->put(ucc->loader_id(), locations); + }); +} + +void URLClassLoaderClasspathSupport::add_urlclassloader_classpath(ClassLoaderData* loader_data, const char* classpath) { + assert(_aot_id_to_classpath != nullptr, "sanity check"); Symbol* aot_id = loader_data->aot_identity(); if (_aot_id_to_classpath->contains(aot_id)) { // cannot allow aot_id clash; return without doing anything @@ -1127,14 +1146,6 @@ void URLClassLoaderClasspathSupport::add_urlclassloader_classpath(ClassLoaderDat _aot_id_to_classpath->put(aot_id, locations); } -static inline bool ucc_symbol_equals(URLClassLoaderClasspath* ucc, Symbol* loader_id, int len_unused) { - return ucc->loader_id()->equals(loader_id); -} - -class ArchivedAOTIdToClasspathMap : public OffsetCompactHashtable {}; -static ArchivedAOTIdToClasspathMap _archived_aot_id_to_classpath; - class URLClassLoaderClasspathArchiver : StackObj { private: CompactHashtableWriter* _writer; @@ -1145,10 +1156,15 @@ class URLClassLoaderClasspathArchiver : StackObj { {} bool do_entry(Symbol* loader_id, GrowableClassLocationArray* class_locations) { + Array* archived_copy = ArchiveBuilder::new_ro_array(class_locations->length()); + for (int i = 0; i < class_locations->length(); i++) { + archived_copy->at_put(i, class_locations->at(i)->write_to_archive()); + ArchivePtrMarker::mark_pointer((address*)archived_copy->adr_at(i)); + } URLClassLoaderClasspath* ucc = (URLClassLoaderClasspath*)ArchiveBuilder::ro_region_alloc(sizeof(URLClassLoaderClasspath)); assert(_builder->has_been_archived(loader_id), "must be"); Symbol* buffered_sym = _builder->get_buffered_addr(loader_id); - ucc->init(buffered_sym, ArchiveUtils::archive_array(class_locations)); + ucc->init(buffered_sym, archived_copy); ArchivePtrMarker::mark_pointer(ucc->loader_id_addr()); ArchivePtrMarker::mark_pointer(ucc->class_locations_addr()); unsigned int hash = Symbol::symbol_hash(loader_id); @@ -1158,10 +1174,43 @@ class URLClassLoaderClasspathArchiver : StackObj { } }; -void URLClassLoaderClasspathSupport::archive_map() { +void URLClassLoaderClasspathSupport::archive_classpath_map() { CompactHashtableStats stats; CompactHashtableWriter writer(_aot_id_to_classpath->number_of_entries(), &stats); URLClassLoaderClasspathArchiver archiver(&writer); _aot_id_to_classpath->iterate(&archiver); writer.dump(&_archived_aot_id_to_classpath, "archived prelinked table"); } + +void URLClassLoaderClasspathSupport::serialize_classpath_map_table_header(SerializeClosure* soc) { + _archived_aot_id_to_classpath.serialize_header(soc); +} + +bool URLClassLoaderClasspathSupport::verify_archived_classpath(ClassLoaderData* loader_data, const char* classpath) { + ResourceMark rm; + Symbol* aot_id = loader_data->aot_identity(); + assert(aot_id != nullptr, "sanity check"); + const char* aot_id_str = aot_id->as_C_string(); + unsigned int hash = Symbol::symbol_hash(aot_id); + URLClassLoaderClasspath* archived_classpath = _archived_aot_id_to_classpath.lookup(aot_id, hash, /*len*/0); // len is ignored + if (archived_classpath == nullptr) { + aot_log_warning(aot)("URLClassLoader (id=%s) classpath validation failed (reason: no archived entry found)", aot_id_str); + return false; + } + URLClassLoaderClassLocationStream uccs(classpath); + uccs.start(); + for (int i = 0; i < archived_classpath->num_entries(); i++) { + AOTClassLocation* location = archived_classpath->class_location_at(i); + const char* archived_path = location->path(); + if (!uccs.has_next()) { + aot_log_warning(aot)("URLClassLoader (id=%s) classpath validation failed (reason: classpath has fewer elements than expected)", aot_id_str); + return false; + } + const char* runtime_path = uccs.get_next(); + if (!location->check(runtime_path, true)) { + aot_log_warning(aot)("URLClassLoader (id=%s) classpath validation failed", aot_id_str); + return false; + } + } + return true; +} diff --git a/src/hotspot/share/cds/aotClassLocation.hpp b/src/hotspot/share/cds/aotClassLocation.hpp index 8908baacd6e..feca7647762 100644 --- a/src/hotspot/share/cds/aotClassLocation.hpp +++ b/src/hotspot/share/cds/aotClassLocation.hpp @@ -89,6 +89,7 @@ class AOTClassLocation { static AOTClassLocation* allocate(JavaThread* current, const char* path, int index, Group group, bool from_cpattr = false, bool is_jrt = false); + size_t path_length() const { return _path_length; } size_t total_size() const { return manifest_offset() + _manifest_length + 1; } const char* path() const { return ((const char*)this) + path_offset(); } size_t manifest_length() const { return _manifest_length; } @@ -131,6 +132,8 @@ class URLClassLoaderClasspath { address* loader_id_addr() const { return (address*)&_loader_id; } Array* class_locations() const { return _class_locations; } address* class_locations_addr() const { return (address*)&_class_locations; } + AOTClassLocation* class_location_at(int i) { return _class_locations->at(i); } + int num_entries() { return _class_locations->length(); } }; // AOTClassLocationConfig @@ -296,9 +299,12 @@ class AOTClassLocationConfig : public CHeapObj { class URLClassLoaderClasspathSupport : AllStatic { public: - static Symbol* classpath_to_aotid(const char* classpath); + static void init(); + static void reload_runtime_map(); static void add_urlclassloader_classpath(ClassLoaderData* loader_data, const char* classpath); - static void archive_map(); + static void archive_classpath_map(); + static void serialize_classpath_map_table_header(SerializeClosure* soc); + static bool verify_archived_classpath(ClassLoaderData* loader_data, const char* classpath); }; #endif // SHARE_CDS_AOTCLASSLOCATION_HPP diff --git a/src/hotspot/share/cds/aotMetaspace.cpp b/src/hotspot/share/cds/aotMetaspace.cpp index 630c744e6e6..8ed127881d0 100644 --- a/src/hotspot/share/cds/aotMetaspace.cpp +++ b/src/hotspot/share/cds/aotMetaspace.cpp @@ -531,6 +531,7 @@ void AOTMetaspace::serialize(SerializeClosure* soc) { SystemDictionaryShared::serialize_dictionary_headers(soc); AOTLinkedClassBulkLoader::serialize(soc); AOTClassLinker::serialize_prelinked_table_header(soc); + URLClassLoaderClasspathSupport::serialize_classpath_map_table_header(soc); FinalImageRecipes::serialize(soc); TrainingData::serialize(soc); InstanceMirrorKlass::serialize_offsets(soc); @@ -740,6 +741,7 @@ char* VM_PopulateDumpSharedSpace::dump_read_only_tables(AOTClassLocationConfig*& SystemDictionaryShared::write_to_archive(); cl_config = AOTClassLocationConfig::dumptime()->write_to_archive(); AOTClassLinker::write_to_archive(); + URLClassLoaderClasspathSupport::archive_classpath_map(); if (CDSConfig::is_dumping_preimage_static_archive()) { FinalImageRecipes::record_recipes(); } diff --git a/src/hotspot/share/classfile/systemDictionary.cpp b/src/hotspot/share/classfile/systemDictionary.cpp index d69b4da1b94..be8c297dfeb 100644 --- a/src/hotspot/share/classfile/systemDictionary.cpp +++ b/src/hotspot/share/classfile/systemDictionary.cpp @@ -1643,6 +1643,7 @@ void SystemDictionary::initialize(TRAPS) { SystemDictionaryShared::initialize(); if (CDSConfig::is_dumping_archive()) { AOTClassLocationConfig::dumptime_init(THREAD); + URLClassLoaderClasspathSupport::init(); } #endif // Resolve basic classes diff --git a/src/hotspot/share/classfile/systemDictionaryShared.cpp b/src/hotspot/share/classfile/systemDictionaryShared.cpp index 1141033e3ae..9c2f3366fdd 100644 --- a/src/hotspot/share/classfile/systemDictionaryShared.cpp +++ b/src/hotspot/share/classfile/systemDictionaryShared.cpp @@ -673,8 +673,8 @@ InstanceKlass* SystemDictionaryShared::find_or_load_shared_class( } k = load_shared_class_for_builtin_loader(name, class_loader, THREAD); - } else if (CDSConfig::supports_custom_loaders() && - (class_loader() == UnregisteredClasses::unregistered_class_loader(THREAD)() || java_lang_ClassLoader::aotIdentity(class_loader()) != nullptr)) { + } else if (CDSConfig::supports_custom_loaders() && CDSConfig::is_dumping_final_static_archive() && + class_loader() == UnregisteredClasses::unregistered_class_loader(THREAD)()) { ClassLoaderData *loader_data = register_loader(class_loader); Dictionary* dictionary = loader_data->dictionary(); diff --git a/src/hotspot/share/prims/jvm.cpp b/src/hotspot/share/prims/jvm.cpp index 3d864627efb..d25c64bc392 100644 --- a/src/hotspot/share/prims/jvm.cpp +++ b/src/hotspot/share/prims/jvm.cpp @@ -2289,8 +2289,8 @@ JVM_ENTRY_PROF(jboolean, JVM_RegisterAsAOTCompatibleLoader, JVM_RegisterAsAOTCom if (CDSConfig::is_using_aot_linked_classes() && CDSConfig::supports_custom_loaders()) { Handle h_loader(THREAD, JNIHandles::resolve_non_null(loader)); ClassLoaderData *loader_data = SystemDictionary::register_loader(h_loader); - AOTLinkedClassBulkLoader::preload_classes_for_loader(loader_data, CHECK_AND_CLEAR_false); - AOTLinkedClassBulkLoader::link_classes_for_loader(loader_data, CHECK_AND_CLEAR_false); + AOTLinkedClassBulkLoader::preload_classes_for_loader(loader_data, CHECK_AND_CLEAR_(JNI_FALSE)); + AOTLinkedClassBulkLoader::link_classes_for_loader(loader_data, CHECK_AND_CLEAR_(JNI_FALSE)); return JNI_TRUE; } return JNI_FALSE; @@ -2302,12 +2302,15 @@ JVM_ENTRY_PROF(jboolean, JVM_RegisterURLClassLoaderAsAOTSafeLoader, JVM_Register Handle h_loader(THREAD, JNIHandles::resolve_non_null(loader)); ClassLoaderData *loader_data = SystemDictionary::register_loader(h_loader); assert(loader_data->aot_identity() != nullptr, "must be"); + const char *classpath_str = java_lang_String::as_utf8_string(JNIHandles::resolve_non_null(classpath)); if (CDSConfig::is_dumping_preimage_static_archive()) { - const char *classpath_str = java_lang_String::as_utf8_string(JNIHandles::resolve_non_null(classpath)); URLClassLoaderClasspathSupport::add_urlclassloader_classpath(loader_data, classpath_str); } else if (CDSConfig::is_using_aot_linked_classes()) { - AOTLinkedClassBulkLoader::preload_classes_for_loader(loader_data, CHECK_AND_CLEAR_false); - AOTLinkedClassBulkLoader::link_classes_for_loader(loader_data, CHECK_AND_CLEAR_false); + if (!URLClassLoaderClasspathSupport::verify_archived_classpath(loader_data, classpath_str)) { + return JNI_FALSE; + } + AOTLinkedClassBulkLoader::preload_classes_for_loader(loader_data, CHECK_AND_CLEAR_(JNI_FALSE)); + AOTLinkedClassBulkLoader::link_classes_for_loader(loader_data, CHECK_AND_CLEAR_(JNI_FALSE)); } return JNI_TRUE; } diff --git a/src/java.base/share/classes/java/net/URLClassLoader.java b/src/java.base/share/classes/java/net/URLClassLoader.java index 2c6265afed8..9e3e60999ab 100644 --- a/src/java.base/share/classes/java/net/URLClassLoader.java +++ b/src/java.base/share/classes/java/net/URLClassLoader.java @@ -665,10 +665,13 @@ private boolean canRegisterAsAOTSafe(final URL[] urls) { private String createClassPath(final URL[] urls) { StringBuilder sb = new StringBuilder(); - for (URL url: urls) { + for (int i = 0; i < urls.length; i++) { + URL url = urls[i]; String path = url.getPath(); sb.append(path); - sb.append(File.pathSeparator); + if (i < urls.length-1) { + sb.append(File.pathSeparator); + } } return sb.toString(); } From d5c2f069549760f686a4077f2d538f114d6668f0 Mon Sep 17 00:00:00 2001 From: Ashutosh Mehra Date: Tue, 10 Feb 2026 10:31:52 -0500 Subject: [PATCH 11/30] Add missing file Signed-off-by: Ashutosh Mehra --- src/java.base/share/native/libjava/URLClassLoader.c | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 src/java.base/share/native/libjava/URLClassLoader.c diff --git a/src/java.base/share/native/libjava/URLClassLoader.c b/src/java.base/share/native/libjava/URLClassLoader.c new file mode 100644 index 00000000000..e15cdd976a4 --- /dev/null +++ b/src/java.base/share/native/libjava/URLClassLoader.c @@ -0,0 +1,10 @@ + +#include "jni.h" +#include "jni_util.h" +#include "jvm.h" + +JNIEXPORT jboolean JNICALL +Java_java_net_URLClassLoader_registerAsAOTSafeImpl(JNIEnv *env, jobject loader, jstring classpath) +{ + return JVM_RegisterURLClassLoaderAsAOTSafeLoader(env, loader, classpath); +} From 24c84ae1f242e38144e3f7b0a33001dc14671884 Mon Sep 17 00:00:00 2001 From: Ashutosh Mehra Date: Wed, 11 Feb 2026 15:01:56 -0500 Subject: [PATCH 12/30] Fix bugs Signed-off-by: Ashutosh Mehra --- src/hotspot/share/cds/aotClassLinker.cpp | 1 + src/hotspot/share/cds/aotClassLocation.cpp | 1 + src/hotspot/share/classfile/systemDictionaryShared.cpp | 4 ++-- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/hotspot/share/cds/aotClassLinker.cpp b/src/hotspot/share/cds/aotClassLinker.cpp index 0ab74a8ee66..711b664d69d 100644 --- a/src/hotspot/share/cds/aotClassLinker.cpp +++ b/src/hotspot/share/cds/aotClassLinker.cpp @@ -244,6 +244,7 @@ void AOTClassLinker::print_archived_custom_loader_prelinked_table() { InstanceKlass* ik = class_list->at(i); log_info(aot, link)(" %s", ik->external_name()); } + return true; }); } } diff --git a/src/hotspot/share/cds/aotClassLocation.cpp b/src/hotspot/share/cds/aotClassLocation.cpp index 357692323c5..e7ebdb29433 100644 --- a/src/hotspot/share/cds/aotClassLocation.cpp +++ b/src/hotspot/share/cds/aotClassLocation.cpp @@ -1125,6 +1125,7 @@ void URLClassLoaderClasspathSupport::reload_runtime_map() { log_info(class, path)("path [%d] = %s", i, ucc->class_location_at(i)->path()); } _aot_id_to_classpath->put(ucc->loader_id(), locations); + return true; }); } diff --git a/src/hotspot/share/classfile/systemDictionaryShared.cpp b/src/hotspot/share/classfile/systemDictionaryShared.cpp index b2c9181e7f1..9a03a46e7e8 100644 --- a/src/hotspot/share/classfile/systemDictionaryShared.cpp +++ b/src/hotspot/share/classfile/systemDictionaryShared.cpp @@ -1544,7 +1544,7 @@ void SystemDictionaryShared::get_all_archived_classes(bool is_static_archive, Gr classes->append(record->klass()); }); - get_archive(is_static_archive)->_aot_safe_custom_loader_dict.iterate([&] (const RunTimeClassInfo* record) { + get_archive(is_static_archive)->_aot_safe_custom_loader_dict.iterate_all([&] (const RunTimeClassInfo* record) { classes->append(record->klass()); }); @@ -1579,7 +1579,7 @@ void SystemDictionaryShared::ArchiveInfo::print_on(const char* prefix, st->print_cr("%sShared Builtin Dictionary", prefix); _builtin_dictionary.iterate_all(&p); st->print_cr("%sShared AOT Compatible Custom Loaders Dictionary", prefix); - _aot_safe_custom_loader_dict.iterate(&p); + _aot_safe_custom_loader_dict.iterate_all(&p); st->print_cr("%sShared Unregistered Dictionary", prefix); _unregistered_dictionary.iterate_all(&p); LambdaProxyClassDictionary::print_on(prefix, st, p.index(), is_static_archive); From da243b7affba4add841fd802077871679932714e Mon Sep 17 00:00:00 2001 From: Ashutosh Mehra Date: Wed, 11 Feb 2026 15:02:15 -0500 Subject: [PATCH 13/30] Keep aot-safe custom loaders alive Signed-off-by: Ashutosh Mehra --- src/hotspot/share/classfile/classLoaderData.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/hotspot/share/classfile/classLoaderData.cpp b/src/hotspot/share/classfile/classLoaderData.cpp index 46873d2adfa..cb305a89665 100644 --- a/src/hotspot/share/classfile/classLoaderData.cpp +++ b/src/hotspot/share/classfile/classLoaderData.cpp @@ -161,6 +161,7 @@ ClassLoaderData::ClassLoaderData(Handle h_class_loader, bool has_class_mirror_ho oop aot_identity = java_lang_ClassLoader::aotIdentity(h_class_loader()); if (aot_identity != nullptr) { _aot_identity = java_lang_String::as_symbol(aot_identity); + _keep_alive_ref_count = 1; // keep aot-safe classloaders alive } initialize_name(h_class_loader); } From 7fe29c65435154c0da2dbcfaeb172896f48e5773 Mon Sep 17 00:00:00 2001 From: Ashutosh Mehra Date: Wed, 11 Feb 2026 15:08:47 -0500 Subject: [PATCH 14/30] Keep aot-safe custom loaders alive during training run Signed-off-by: Ashutosh Mehra --- src/hotspot/share/classfile/classLoaderData.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/hotspot/share/classfile/classLoaderData.cpp b/src/hotspot/share/classfile/classLoaderData.cpp index cb305a89665..c6fdf2c6f1a 100644 --- a/src/hotspot/share/classfile/classLoaderData.cpp +++ b/src/hotspot/share/classfile/classLoaderData.cpp @@ -161,7 +161,10 @@ ClassLoaderData::ClassLoaderData(Handle h_class_loader, bool has_class_mirror_ho oop aot_identity = java_lang_ClassLoader::aotIdentity(h_class_loader()); if (aot_identity != nullptr) { _aot_identity = java_lang_String::as_symbol(aot_identity); - _keep_alive_ref_count = 1; // keep aot-safe classloaders alive + // keep aot-safe custom loaders alive during training run + if (CDSConfig::is_dumping_preimage_static_archive()) { + _keep_alive_ref_count = 1; + } } initialize_name(h_class_loader); } From 4711732edf2503c86b0d972b192ac2920e8a51b9 Mon Sep 17 00:00:00 2001 From: Ashutosh Mehra Date: Wed, 18 Feb 2026 15:54:15 -0500 Subject: [PATCH 15/30] Use SystemDictionary::preload_class to load classes in assembly phase Signed-off-by: Ashutosh Mehra --- src/hotspot/share/cds/archiveBuilder.cpp | 38 ++ src/hotspot/share/cds/archiveBuilder.hpp | 1 + src/hotspot/share/cds/cdsProtectionDomain.cpp | 2 +- src/hotspot/share/cds/dumpTimeClassInfo.cpp | 3 - src/hotspot/share/cds/dumpTimeClassInfo.hpp | 7 +- src/hotspot/share/cds/finalImageRecipes.cpp | 355 +++++++++++++++++- src/hotspot/share/cds/finalImageRecipes.hpp | 97 ++++- .../share/classfile/systemDictionary.cpp | 46 ++- .../share/classfile/systemDictionary.hpp | 1 + .../classfile/systemDictionaryShared.cpp | 150 ++------ .../classfile/systemDictionaryShared.hpp | 9 +- 11 files changed, 546 insertions(+), 163 deletions(-) diff --git a/src/hotspot/share/cds/archiveBuilder.cpp b/src/hotspot/share/cds/archiveBuilder.cpp index 0fbaff82045..688b2df32ca 100644 --- a/src/hotspot/share/cds/archiveBuilder.cpp +++ b/src/hotspot/share/cds/archiveBuilder.cpp @@ -320,6 +320,44 @@ int ArchiveBuilder::compare_klass_by_name(Klass** a, Klass** b) { void ArchiveBuilder::sort_klasses() { aot_log_info(aot)("Sorting classes ... "); _klasses->sort(compare_klass_by_name); + GrowableArray* unused = _klasses; + _klasses = sort_klasses_by_hierarchy(); + delete unused; +} + +static const int INITIAL_TABLE_SIZE = 15889; +using ClassesTable = HashTable; +ClassesTable* _classes_added = nullptr; + +static void add_to_sorted_list(GrowableArray* sorted_list, Klass* k) { + if (k == nullptr) { + return; + } + if (_classes_added->get(k) != nullptr) { + return; + } + add_to_sorted_list(sorted_list, k->super()); + + if (k->is_instance_klass()) { + InstanceKlass* ik = (InstanceKlass*)k; + Array* interfaces = ik->local_interfaces(); + int num_interfaces = interfaces->length(); + for (int index = 0; index < num_interfaces; index++) { + InstanceKlass* intf = interfaces->at(index); + add_to_sorted_list(sorted_list, intf); + } + } + _classes_added->put_when_absent(k, true); + sorted_list->append(k); +} + +GrowableArray* ArchiveBuilder::sort_klasses_by_hierarchy() { + GrowableArray* sorted_by_hierarchy = new (mtClassShared) GrowableArray(_klasses->length(), mtClassShared); + _classes_added = new (mtClass)ClassesTable(); + for (int i = 0; i < _klasses->length(); i++) { + add_to_sorted_list(sorted_by_hierarchy, _klasses->at(i)); + } + return sorted_by_hierarchy; } address ArchiveBuilder::reserve_buffer() { diff --git a/src/hotspot/share/cds/archiveBuilder.hpp b/src/hotspot/share/cds/archiveBuilder.hpp index 75ede06f988..08939ed0dad 100644 --- a/src/hotspot/share/cds/archiveBuilder.hpp +++ b/src/hotspot/share/cds/archiveBuilder.hpp @@ -279,6 +279,7 @@ class ArchiveBuilder : public StackObj { void iterate_sorted_roots(MetaspaceClosure* it); void sort_klasses(); + GrowableArray* sort_klasses_by_hierarchy(); static int compare_symbols_by_address(Symbol** a, Symbol** b); static int compare_klass_by_name(Klass** a, Klass** b); void update_hidden_class_loader_type(InstanceKlass* ik) NOT_CDS_JAVA_HEAP_RETURN; diff --git a/src/hotspot/share/cds/cdsProtectionDomain.cpp b/src/hotspot/share/cds/cdsProtectionDomain.cpp index e9dbce16275..3dd876d8808 100644 --- a/src/hotspot/share/cds/cdsProtectionDomain.cpp +++ b/src/hotspot/share/cds/cdsProtectionDomain.cpp @@ -47,7 +47,7 @@ OopHandle CDSProtectionDomain::_shared_jar_manifests; // the given InstanceKlass. // Returns the ProtectionDomain for the InstanceKlass. Handle CDSProtectionDomain::init_security_info(Handle class_loader, InstanceKlass* ik, PackageEntry* pkg_entry, TRAPS) { - if (ik->defined_by_other_loaders()) { + if (false && ik->defined_by_other_loaders()) { assert(ik->cl_aot_identity() != nullptr, "sanity check"); // define NamedPackage in java layer define_named_package(class_loader, pkg_entry, THREAD); diff --git a/src/hotspot/share/cds/dumpTimeClassInfo.cpp b/src/hotspot/share/cds/dumpTimeClassInfo.cpp index 1f8f4fdf76d..0f5773a2729 100644 --- a/src/hotspot/share/cds/dumpTimeClassInfo.cpp +++ b/src/hotspot/share/cds/dumpTimeClassInfo.cpp @@ -172,8 +172,6 @@ class CountClassByCategory : StackObj { if (!info.is_excluded()) { if (info.is_builtin()) { _table->inc_builtin_count(); - } else if (k->is_defined_by_aot_safe_custom_loader()) { - _table->inc_defined_by_aot_safe_custom_loaders_count(); } else { _table->inc_unregistered_count(); } @@ -183,7 +181,6 @@ class CountClassByCategory : StackObj { void DumpTimeSharedClassTable::update_counts() { _builtin_count = 0; - _defined_by_aot_safe_custom_loaders_count = 0; _unregistered_count = 0; CountClassByCategory counter(this); iterate_all_live_classes(&counter); diff --git a/src/hotspot/share/cds/dumpTimeClassInfo.hpp b/src/hotspot/share/cds/dumpTimeClassInfo.hpp index bf9ecb0c6f4..ed467b15d08 100644 --- a/src/hotspot/share/cds/dumpTimeClassInfo.hpp +++ b/src/hotspot/share/cds/dumpTimeClassInfo.hpp @@ -276,25 +276,20 @@ using DumpTimeSharedClassTableBaseType = HashTable< class DumpTimeSharedClassTable: public DumpTimeSharedClassTableBaseType { int _builtin_count; - int _defined_by_aot_safe_custom_loaders_count; int _unregistered_count; public: DumpTimeSharedClassTable() { _builtin_count = 0; - _defined_by_aot_safe_custom_loaders_count = 0; _unregistered_count = 0; } DumpTimeClassInfo* allocate_info(InstanceKlass* k); DumpTimeClassInfo* get_info(InstanceKlass* k); void inc_builtin_count() { _builtin_count++; } - void inc_defined_by_aot_safe_custom_loaders_count() { _defined_by_aot_safe_custom_loaders_count++; } void inc_unregistered_count() { _unregistered_count++; } void update_counts(); - int count_of(bool is_builtin, bool is_defined_by_aot_safe_custom_loaders) const { + int count_of(bool is_builtin) const { if (is_builtin) { return _builtin_count; - } else if (is_defined_by_aot_safe_custom_loaders) { - return _defined_by_aot_safe_custom_loaders_count; } else { return _unregistered_count; } diff --git a/src/hotspot/share/cds/finalImageRecipes.cpp b/src/hotspot/share/cds/finalImageRecipes.cpp index f45cb58731d..f01621d5456 100644 --- a/src/hotspot/share/cds/finalImageRecipes.cpp +++ b/src/hotspot/share/cds/finalImageRecipes.cpp @@ -22,6 +22,7 @@ * */ +#include "cds/aotClassLinker.hpp" #include "cds/aotConstantPoolResolver.hpp" #include "cds/aotLinkedClassBulkLoader.hpp" #include "cds/archiveBuilder.hpp" @@ -51,11 +52,182 @@ void* FinalImageRecipes::operator new(size_t size) throw() { return ArchiveBuilder::current()->ro_region_alloc(size); } +static void mark_pointers_in_array(Array* array) { + if (array == nullptr) { + return; + } + for (int i = 0; i < array->length(); i++) { + InstanceKlassRecipe* recipe = array->adr_at(i); + recipe->mark_pointers(); + } +} + +void FinalImageRecipeTable::mark_pointers() { + ArchivePtrMarker::mark_pointer(&_boot1); + mark_pointers_in_array(_boot1); + ArchivePtrMarker::mark_pointer(&_boot2); + mark_pointers_in_array(_boot2); + ArchivePtrMarker::mark_pointer(&_platform); + mark_pointers_in_array(_platform); + ArchivePtrMarker::mark_pointer(&_app); + mark_pointers_in_array(_app); + ArchivePtrMarker::mark_pointer(&_aot_safe_loader_classes); + mark_pointers_in_array(_aot_safe_loader_classes); + ArchivePtrMarker::mark_pointer(&_unregistered); + mark_pointers_in_array(_unregistered); +} + void FinalImageRecipes::record_all_classes() { - _all_klasses = ArchiveUtils::archive_array(ArchiveBuilder::current()->klasses()); - ArchivePtrMarker::mark_pointer(&_all_klasses); + _class_table = (FinalImageRecipeTable*)ArchiveBuilder::ro_region_alloc(sizeof(FinalImageRecipeTable)); + ArchivePtrMarker::mark_pointer(&_class_table); + _class_table->set_boot1(write_builtin_loader_classes(nullptr, true)); + _class_table->set_boot2(write_builtin_loader_classes(nullptr, false)); + _class_table->set_platform(write_builtin_loader_classes(SystemDictionary::java_platform_loader(), false)); + _class_table->set_app(write_builtin_loader_classes(SystemDictionary::java_system_loader(), false)); + _class_table->set_aot_safe_loader_classes(write_custom_loader_classes(/*aot-safe loader classes*/ true)); + _class_table->set_unregistered(write_custom_loader_classes(/*aot-safe loader classes*/ false)); + _class_table->mark_pointers(); } +Array* FinalImageRecipes::write_builtin_loader_classes(oop class_loader, bool is_javabase) { + ResourceMark rm; + GrowableArray list; + GrowableArray* all_classes = ArchiveBuilder::current()->klasses(); + + for (int i = 0; i < all_classes->length(); i++) { + Klass* k = all_classes->at(i); + if (k->is_instance_klass()) { + InstanceKlass* ik = InstanceKlass::cast(k); + if (ik->class_loader() != class_loader) { + continue; + } + if ((ik->module() == ModuleEntryTable::javabase_moduleEntry()) != is_javabase) { + continue; + } + int flags = 0; + Array* cp_recipe = record_recipe_for_constantpool(ik, flags); + InstanceKlassRecipe recipe(ArchiveBuilder::current()->get_buffered_addr(ik), cp_recipe, flags); + list.append(recipe); + } + } + + if (list.length() == 0) { + return nullptr; + } else { + const char* category = AOTClassLinker::class_category_name(list.adr_at(0)->instance_klass()); + log_info(aot, link)("wrote %d class(es) for category %s", list.length(), category); + return ArchiveUtils::archive_array(&list); + } +} + +Array* FinalImageRecipes::write_custom_loader_classes(bool write_aot_safe_loader_classes) { + ResourceMark rm; + GrowableArray list; + GrowableArray* all_classes = ArchiveBuilder::current()->klasses(); + + for (int i = 0; i < all_classes->length(); i++) { + Klass* k = all_classes->at(i); + if (k->is_instance_klass()) { + InstanceKlass* ik = InstanceKlass::cast(k); + // skip builtin loader classes + if (SystemDictionaryShared::is_builtin(ik)) { + continue; + } + if (ik->is_defined_by_aot_safe_custom_loader() != write_aot_safe_loader_classes) { + continue; + } + int flags = 0; + Array* cp_recipe = record_recipe_for_constantpool(ik, flags); + InstanceKlassRecipe recipe(ArchiveBuilder::current()->get_buffered_addr(ik), cp_recipe, flags); + list.append(recipe); + } + } + if (list.length() == 0) { + return nullptr; + } else { + const char* category = AOTClassLinker::class_category_name(list.adr_at(0)->instance_klass()); + log_info(aot, link)("wrote %d class(es) for category %s", list.length(), category); + return ArchiveUtils::archive_array(&list); + } +} + +Array* FinalImageRecipes::record_recipe_for_constantpool(InstanceKlass* ik, int& flags) { + ConstantPool* cp = ik->constants(); + ConstantPoolCache* cp_cache = cp->cache(); + GrowableArray cp_indices; + + if (ik->is_initialized()) { + flags |= WAS_INITED; + } + + for (int cp_index = 1; cp_index < cp->length(); cp_index++) { // Index 0 is unused + if (cp->tag_at(cp_index).value() == JVM_CONSTANT_Class) { + Klass* k = cp->resolved_klass_at(cp_index); + if (k->is_instance_klass()) { + cp_indices.append(cp_index); + flags |= CP_RESOLVE_CLASS; + } + } + } + + if (cp_cache != nullptr) { + Array* field_entries = cp_cache->resolved_field_entries(); + if (field_entries != nullptr) { + for (int i = 0; i < field_entries->length(); i++) { + ResolvedFieldEntry* rfe = field_entries->adr_at(i); + if (rfe->is_resolved(Bytecodes::_getstatic) || + rfe->is_resolved(Bytecodes::_putstatic) || + rfe->is_resolved(Bytecodes::_getfield) || + rfe->is_resolved(Bytecodes::_putfield)) { + cp_indices.append(rfe->constant_pool_index()); + flags |= CP_RESOLVE_FIELD_AND_METHOD; + } + } + } + + Array* method_entries = cp_cache->resolved_method_entries(); + if (method_entries != nullptr) { + for (int i = 0; i < method_entries->length(); i++) { + ResolvedMethodEntry* rme = method_entries->adr_at(i); + if (rme->is_resolved(Bytecodes::_invokevirtual) || + rme->is_resolved(Bytecodes::_invokespecial) || + rme->is_resolved(Bytecodes::_invokeinterface) || + rme->is_resolved(Bytecodes::_invokestatic) || + rme->is_resolved(Bytecodes::_invokehandle)) { + cp_indices.append(rme->constant_pool_index()); + flags |= CP_RESOLVE_FIELD_AND_METHOD; + } + } + } + + Array* indy_entries = cp_cache->resolved_indy_entries(); + if (indy_entries != nullptr) { + for (int i = 0; i < indy_entries->length(); i++) { + ResolvedIndyEntry* rie = indy_entries->adr_at(i); + int cp_index = rie->constant_pool_index(); + if (rie->is_resolved()) { + cp_indices.append(cp_index); + flags |= CP_RESOLVE_INDY; + } + } + } + } + + if (cp_indices.length() > 0) { + LogStreamHandle(Trace, aot, resolve) log; + if (log.is_enabled()) { + log.print("ConstantPool entries for %s to be pre-resolved:", ik->external_name()); + for (int i = 0; i < cp_indices.length(); i++) { + log.print(" %d", cp_indices.at(i)); + } + log.print("\n"); + } + return ArchiveUtils::archive_array(&cp_indices); + } else { + return nullptr; + } +} +/* void FinalImageRecipes::record_recipes_for_constantpool() { ResourceMark rm; @@ -157,15 +329,17 @@ void FinalImageRecipes::record_recipes_for_constantpool() { _flags = ArchiveUtils::archive_array(&tmp_flags); ArchivePtrMarker::mark_pointer(&_flags); } +*/ void FinalImageRecipes::apply_recipes_for_constantpool(JavaThread* current) { assert(CDSConfig::is_dumping_final_static_archive(), "must be"); - for (int i = 0; i < _all_klasses->length(); i++) { - Array* cp_indices = _cp_recipes->at(i); - int flags = _flags->at(i); + //for (int i = 0; i < _all_klasses->length(); i++) { + _class_table->iterate_all([&](InstanceKlassRecipe* ikr) { + InstanceKlass* ik = ikr->instance_klass(); + Array* cp_indices = ikr->cp_recipe(); + int flags = ikr->flags(); if (cp_indices != nullptr) { - InstanceKlass* ik = InstanceKlass::cast(_all_klasses->at(i)); if (!strcmp(ik->external_name(), "org.openjdk.aot.testclass.Foo")) { log_info(cds)("Applying constant pool recipes for %s", ik->external_name()); } @@ -187,7 +361,7 @@ void FinalImageRecipes::apply_recipes_for_constantpool(JavaThread* current) { } } } - } + }); } void FinalImageRecipes::record_recipes_for_reflection_data() { @@ -256,6 +430,167 @@ void FinalImageRecipes::record_recipes_for_dynamic_proxies() { } } +void FinalImageRecipes::load_builtin_loader_classes(TRAPS) { + precond(CDSConfig::is_dumping_aot_linked_classes()); + + Handle h_platform_loader(THREAD, SystemDictionary::java_platform_loader()); + Handle h_system_loader(THREAD, SystemDictionary::java_system_loader()); + + load_classes_in_table(_class_table->boot1(), "boot1", Handle(), CHECK); + load_classes_in_table(_class_table->boot2(), "boot2", Handle(), CHECK); + + initiate_loading(THREAD, "plat", h_platform_loader, _class_table->boot1()); + initiate_loading(THREAD, "plat", h_platform_loader, _class_table->boot2()); + load_classes_in_table(_class_table->platform(), "plat", h_platform_loader, CHECK); + + initiate_loading(THREAD, "app", h_system_loader, _class_table->boot1()); + initiate_loading(THREAD, "app", h_system_loader, _class_table->boot2()); + initiate_loading(THREAD, "app", h_system_loader, _class_table->platform()); + load_classes_in_table(_class_table->app(), "app", h_system_loader, CHECK); +} + +void FinalImageRecipes::load_custom_loader_classes(TRAPS) { + precond(CDSConfig::is_dumping_aot_linked_classes()); + // Use UnregisteredClassLoader to load these classes + UnregisteredClasses::initialize(CHECK); + Handle unreg_class_loader = UnregisteredClasses::unregistered_class_loader(THREAD); + SystemDictionary::register_loader(unreg_class_loader); + assert(unreg_class_loader.not_null(), "must be"); + + initiate_loading(THREAD, "app", unreg_class_loader, _class_table->boot1()); + initiate_loading(THREAD, "app", unreg_class_loader, _class_table->boot2()); + initiate_loading(THREAD, "app", unreg_class_loader, _class_table->platform()); + initiate_loading(THREAD, "app", unreg_class_loader, _class_table->app()); + load_classes_in_table(_class_table->aot_safe_loader_classes(), "aot-safe", unreg_class_loader, CHECK); + load_classes_in_table(_class_table->unregistered(), "unregistered", unreg_class_loader, CHECK); +} + +void FinalImageRecipes::load_classes_in_table(Array* recipes, + const char* category_name, Handle loader, TRAPS) { + if (recipes == nullptr) { + return; + } + for (int i = 0; i < recipes->length(); i++) { + InstanceKlass* ik = recipes->adr_at(i)->instance_klass(); + if (ik->is_hidden()) { + continue; + } + if (log_is_enabled(Info, aot, load)) { + ResourceMark rm(THREAD); + log_info(aot, load)("%-5s %s%s", category_name, ik->external_name(), + ik->is_hidden() ? " (hidden)" : ""); + } + + InstanceKlass* loaded_ik = SystemDictionary::find_instance_klass(THREAD, ik->name(), loader); + if (loaded_ik == nullptr) { + SystemDictionary::preload_class(loader, ik, CHECK); + precond(SystemDictionary::find_instance_klass(THREAD, ik->name(), loader) == ik); + } else { + assert(loaded_ik == ik, "sanity check"); + } + } +} + +// Initiate loading of the in the . The should have already been loaded +// by a parent loader of the . This is necessary for handling pre-resolved CP entries. +// +// For example, we initiate the loading of java/lang/String in the AppClassLoader. This will allow +// any App classes to have a pre-resolved ConstantPool entry that references java/lang/String. +// +// TODO: we can limit the number of initiated classes to only those that are actually referenced by +// AOT-linked classes loaded by . +void FinalImageRecipes::initiate_loading(JavaThread* current, const char* category_name, + Handle initiating_loader, Array* recipes) { + if (recipes == nullptr) { + return; + } + + assert(initiating_loader() == SystemDictionary::java_platform_loader() || + initiating_loader() == SystemDictionary::java_system_loader() || + initiating_loader() == UnregisteredClasses::unregistered_class_loader(current)(), "must be"); + ClassLoaderData* loader_data = ClassLoaderData::class_loader_data(initiating_loader()); + MonitorLocker mu1(SystemDictionary_lock); + + for (int i = 0; i < recipes->length(); i++) { + InstanceKlass* ik = recipes->adr_at(i)->instance_klass(); + assert(ik->is_loaded(), "must have already been loaded by a parent loader"); + assert(ik->class_loader() != initiating_loader(), "must be a parent loader"); + assert(ik->class_loader() == nullptr || + ik->class_loader() == SystemDictionary::java_platform_loader() || + ik->class_loader() == SystemDictionary::java_system_loader(), "must be"); + if (ik->is_public() && !ik->is_hidden()) { + if (log_is_enabled(Info, aot, load)) { + ResourceMark rm(current); + const char* defining_loader = (ik->class_loader() == nullptr ? "boot" : "plat"); + log_info(aot, load)("%-5s %s (initiated, defined by %s)", category_name, ik->external_name(), + defining_loader); + } + SystemDictionary::add_to_initiating_loader(current, ik, loader_data); + } + } +} + +void FinalImageRecipes::exit_on_exception(JavaThread* current) { + assert(current->has_pending_exception(), "precondition"); + ResourceMark rm(current); + if (current->pending_exception()->is_a(vmClasses::OutOfMemoryError_klass())) { + log_error(aot)("Out of memory. Please run with a larger Java heap, current MaxHeapSize = " + "%zuM", MaxHeapSize/M); + } else { + oop message = java_lang_Throwable::message(current->pending_exception()); + log_error(aot)("%s: %s", current->pending_exception()->klass()->external_name(), + message == nullptr ? "(no message)" : java_lang_String::as_utf8_string(message)); + } + vm_exit_during_initialization("Unexpected exception when loading aot-linked classes."); +} + +// Some cached heap objects may hold references to methods in aot-linked +// classes (via MemberName). We need to make sure all classes are +// linked before executing any bytecode. +void FinalImageRecipes::link_classes(JavaThread* current) { + link_classes_impl(current); + if (current->has_pending_exception()) { + exit_on_exception(current); + } +} + +void FinalImageRecipes::link_classes_impl(TRAPS) { + //precond(CDSConfig::is_using_aot_linked_classes()); + + link_classes_in_table(_class_table->boot1(), CHECK); + link_classes_in_table(_class_table->boot2(), CHECK); + link_classes_in_table(_class_table->platform(), CHECK); + link_classes_in_table(_class_table->app(), CHECK); + link_classes_in_table(_class_table->aot_safe_loader_classes(), CHECK); + link_classes_in_table(_class_table->unregistered(), CHECK); +} + +void FinalImageRecipes::link_classes_in_table(Array* recipes, TRAPS) { + if (recipes != nullptr) { + for (int i = 0; i < recipes->length(); i++) { + // NOTE: CDSConfig::is_preserving_verification_constraints() is required + // when storing ik in the AOT cache. This means we don't have to verify + // ik at all. + // + // Without is_preserving_verification_constraints(), ik->link_class() may cause + // class loading, which may result in invocation of ClassLoader::loadClass() calls, + // which CANNOT happen because we are not ready to execute any Java byecodes yet + // at this point. + InstanceKlass* ik = recipes->adr_at(i)->instance_klass(); + ik->link_class(CHECK); + } + } +} + +void FinalImageRecipes::load_and_link_all_classes(TRAPS) { + /* Built-in loader classes come first */ + load_builtin_loader_classes(CHECK); + /* Now load custom loader classes */ + load_custom_loader_classes(CHECK); + link_classes(THREAD); +} + +#if 0 void FinalImageRecipes::load_all_classes(TRAPS) { assert(CDSConfig::is_dumping_final_static_archive(), "sanity"); UnregisteredClasses::initialize(CHECK); @@ -308,6 +643,7 @@ void FinalImageRecipes::load_all_classes(TRAPS) { } } } +#endif void FinalImageRecipes::apply_recipes_for_reflection_data(JavaThread* current) { assert(CDSConfig::is_dumping_final_static_archive(), "must be"); @@ -402,7 +738,7 @@ void FinalImageRecipes::record_recipes() { assert(CDSConfig::is_dumping_preimage_static_archive(), "must be"); _final_image_recipes = new FinalImageRecipes(); _final_image_recipes->record_all_classes(); - _final_image_recipes->record_recipes_for_constantpool(); + //_final_image_recipes->record_recipes_for_constantpool(); _final_image_recipes->record_recipes_for_reflection_data(); _final_image_recipes->record_recipes_for_dynamic_proxies(); } @@ -424,7 +760,8 @@ void FinalImageRecipes::apply_recipes(TRAPS) { } void FinalImageRecipes::apply_recipes_impl(TRAPS) { - load_all_classes(CHECK); + //load_all_classes(CHECK); + load_and_link_all_classes(CHECK); apply_recipes_for_constantpool(THREAD); apply_recipes_for_reflection_data(CHECK); apply_recipes_for_dynamic_proxies(CHECK); diff --git a/src/hotspot/share/cds/finalImageRecipes.hpp b/src/hotspot/share/cds/finalImageRecipes.hpp index 9638fc954bc..572d14ea28a 100644 --- a/src/hotspot/share/cds/finalImageRecipes.hpp +++ b/src/hotspot/share/cds/finalImageRecipes.hpp @@ -34,6 +34,78 @@ class Klass; template class GrowableArray; template class Array; +class InstanceKlassRecipe { +private: + InstanceKlass* _ik; + Array* _cp_recipe; + int _flags; +public: + InstanceKlassRecipe() : _ik(nullptr), _cp_recipe(nullptr), _flags(0) {} // required by GrowableArray + InstanceKlassRecipe(InstanceKlass* ik, Array* cp_recipe, int flags) : + _ik(ik), _cp_recipe(cp_recipe), _flags(flags) {} + + InstanceKlass* instance_klass() const { return _ik; } + Array* cp_recipe() const { return _cp_recipe; } + int flags() const { return _flags; } + + void mark_pointers() { + ArchivePtrMarker::mark_pointer(&_ik); + ArchivePtrMarker::mark_pointer(&_cp_recipe); + } +}; + +class FinalImageRecipeTable { +private: + Array* _boot1; // boot classes in java.base module + Array* _boot2; // boot classes in all other (named and unnamed) modules, + // including classes from -Xbootclasspath/a + Array* _platform; + Array* _app; + + Array* _aot_safe_loader_classes; + Array* _unregistered; + + template + void iterate_array(Function fn, Array* array) { + if (array != nullptr) { + for (int i = 0; i < array->length(); i++) { + fn(array->adr_at(i)); + } + } + } + +public: + FinalImageRecipeTable() : + _boot1(nullptr), _boot2(nullptr), + _platform(nullptr), _app(nullptr), + _aot_safe_loader_classes(nullptr), _unregistered(nullptr) {} + + Array* boot1() const { return _boot1; } + Array* boot2() const { return _boot2; } + Array* platform() const { return _platform; } + Array* app() const { return _app; } + Array* aot_safe_loader_classes() const { return _aot_safe_loader_classes; } + Array* unregistered() const { return _unregistered; } + + 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; } + void set_aot_safe_loader_classes(Array* value) { _aot_safe_loader_classes = value; } + void set_unregistered(Array* value) { _unregistered = value; } + + template + void iterate_all(Function fn) { + iterate_array(fn, _boot1); + iterate_array(fn, _boot2); + iterate_array(fn, _platform); + iterate_array(fn, _app); + iterate_array(fn, _aot_safe_loader_classes); + iterate_array(fn, _unregistered); + } + void mark_pointers(); +}; + // This class is used for transferring information from the AOTConfiguration file (aka the "preimage") // to the JVM that creates the AOTCache (aka the "final image"). // - The recipes are recorded when CDSConfig::is_dumping_preimage_static_archive() is true. @@ -49,7 +121,8 @@ class FinalImageRecipes { // A list of all the archived classes from the preimage. We want to transfer all of these // into the final image. - Array* _all_klasses; + //Array* _all_klasses; + FinalImageRecipeTable* _class_table; // For each klass k _all_klasses->at(i): _cp_recipes->at(i) lists all the {klass,field,method,indy} // cp indices that were resolved for k during the training run; _flags->at(i) has extra info about k. @@ -81,7 +154,7 @@ class FinalImageRecipes { static GrowableArray* _tmp_dynamic_proxy_classes; - FinalImageRecipes() : _all_klasses(nullptr), _cp_recipes(nullptr), _flags(nullptr), + FinalImageRecipes() : _class_table(nullptr), _cp_recipes(nullptr), _flags(nullptr), _reflect_klasses(nullptr), _reflect_flags(nullptr), _dynamic_proxy_classes(nullptr) {} @@ -89,17 +162,33 @@ class FinalImageRecipes { // Called when dumping preimage void record_all_classes(); - void record_recipes_for_constantpool(); + Array* record_recipe_for_constantpool(InstanceKlass* ik, int& flags); + //void record_recipes_for_constantpool(); void record_recipes_for_reflection_data(); void record_recipes_for_dynamic_proxies(); // Called when dumping final image + void load_builtin_loader_classes(TRAPS); + void load_custom_loader_classes(TRAPS); + void load_classes_in_table(Array* classes, const char* category_name, Handle loader, TRAPS); + void initiate_loading(JavaThread* current, const char* category_name, Handle initiating_loader, Array* classes); + void link_classes(JavaThread* current); + void link_classes_impl(TRAPS); + void link_classes_in_table(Array* classes, TRAPS); + void apply_recipes_impl(TRAPS); - void load_all_classes(TRAPS); + void load_and_link_all_classes(TRAPS); + //void load_all_classes(TRAPS); void apply_recipes_for_constantpool(JavaThread* current); void apply_recipes_for_reflection_data(JavaThread* current); void apply_recipes_for_dynamic_proxies(TRAPS); + + Array* write_builtin_loader_classes(oop class_loader, bool is_javabase); + Array* write_custom_loader_classes(bool write_aot_safe_loader_classes); + + static void exit_on_exception(JavaThread* current); + public: static void serialize(SerializeClosure* soc); diff --git a/src/hotspot/share/classfile/systemDictionary.cpp b/src/hotspot/share/classfile/systemDictionary.cpp index f088027730a..5c647ed6aa5 100644 --- a/src/hotspot/share/classfile/systemDictionary.cpp +++ b/src/hotspot/share/classfile/systemDictionary.cpp @@ -25,6 +25,7 @@ #include "cds/aotClassLocation.hpp" #include "cds/aotLinkedClassBulkLoader.hpp" #include "cds/cdsConfig.hpp" +#include "cds/cdsProtectionDomain.hpp" #include "cds/heapShared.hpp" #include "classfile/classFileParser.hpp" #include "classfile/classFileStream.hpp" @@ -1169,8 +1170,8 @@ void SystemDictionary::load_shared_class_misc(InstanceKlass* ik, ClassLoaderData void SystemDictionary::preload_class(Handle class_loader, InstanceKlass* ik, TRAPS) { //precond(Universe::is_bootstrapping()); precond(java_platform_loader() != nullptr && java_system_loader() != nullptr); - precond(class_loader() == nullptr || class_loader() == java_platform_loader() ||class_loader() == java_system_loader() || ik->cl_aot_identity() != nullptr); - precond(CDSConfig::is_using_aot_linked_classes()); + //precond(class_loader() == nullptr || class_loader() == java_platform_loader() ||class_loader() == java_system_loader() || ik->cl_aot_identity() != nullptr); + //precond(CDSConfig::is_using_aot_linked_classes()); precond(AOTMetaspace::in_aot_cache_static_region((void*)ik)); precond(!ik->is_loaded()); @@ -1196,15 +1197,32 @@ void SystemDictionary::preload_class(Handle class_loader, InstanceKlass* ik, TRA if (ik->has_archived_mirror_index()) { oop java_mirror = ik->archived_java_mirror(); precond(java_mirror != nullptr); - //assert(java_lang_Class::module(java_mirror) != nullptr, "must have been archived"); - pd = Handle(THREAD, java_lang_Class::protection_domain(java_mirror)); - pkg_entry = ik->package(); - if (is_builtin_class_loader(class_loader())) { - assert(pkg_entry != nullptr || ClassLoader::package_from_class_name(ik->name()) == nullptr, - "non-empty packages for builtin loaders must have been archived"); - } else if (ik->is_defined_by_aot_safe_custom_loader()) { - assert(pkg_entry == nullptr, "packages for aot-safe custom loaders are not archived"); + if (CDSConfig::is_dumping_aot_linked_classes()) { + // in assembly phase + assert(java_lang_Class::module(java_mirror) == nullptr, "must not have been archived"); + assert(ik->package() == nullptr, "must not have been archived"); + + pkg_entry = CDSProtectionDomain::get_package_entry_from_class(ik, class_loader); + // pd for classes loaded by boot loader is null + if (class_loader() != nullptr) { + if (!ik->name()->starts_with("jdk/proxy")) // java/lang/reflect/Proxy$ProxyBuilder defines the proxy classes with a null protection domain. + { + pd = CDSProtectionDomain::init_security_info(class_loader, ik, pkg_entry, CHECK); + } + } + } else { + // in production phase + assert(java_lang_Class::module(java_mirror) != nullptr, "must have been archived"); + + pd = Handle(THREAD, java_lang_Class::protection_domain(java_mirror)); + pkg_entry = ik->package(); + if (is_builtin_class_loader(class_loader())) { + assert(pkg_entry != nullptr || ClassLoader::package_from_class_name(ik->name()) == nullptr, + "non-empty packages for builtin loaders must have been archived"); + } else if (ik->is_defined_by_aot_safe_custom_loader()) { + assert(pkg_entry == nullptr, "packages for aot-safe custom loaders are not archived"); + } } } @@ -1311,11 +1329,7 @@ InstanceKlass* SystemDictionary::load_instance_class_impl(Symbol* class_name, Ha { PerfTraceElapsedTime vmtimer(ClassLoader::perf_shared_classload_time()); InstanceKlass* ik = nullptr; - if (SystemDictionary::is_builtin_class_loader(class_loader())) { - ik = SystemDictionaryShared::find_builtin_class(class_name); - } else if (CDSConfig::supports_custom_loaders()) { - ik = SystemDictionaryShared::find_class_in_aot_safe_custom_loader_dict(class_name); - } + ik = SystemDictionaryShared::find_builtin_class(class_name); if (ik != nullptr && ik->defined_by_boot_loader() && !ik->shared_loading_failed()) { SharedClassLoadingMark slm(THREAD, ik); k = load_shared_class(ik, class_loader, Handle(), nullptr, pkg_entry, CHECK_NULL); @@ -1741,7 +1755,7 @@ void SystemDictionary::update_dictionary(JavaThread* current, void SystemDictionary::add_to_initiating_loader(JavaThread* current, InstanceKlass* k, ClassLoaderData* loader_data) { - assert(CDSConfig::is_using_aot_linked_classes(), "must be"); + //assert(CDSConfig::is_using_aot_linked_classes(), "must be"); assert_locked_or_safepoint(SystemDictionary_lock); Symbol* name = k->name(); Dictionary* dictionary = loader_data->dictionary(); diff --git a/src/hotspot/share/classfile/systemDictionary.hpp b/src/hotspot/share/classfile/systemDictionary.hpp index e32e0082f8f..e6f8616d0cd 100644 --- a/src/hotspot/share/classfile/systemDictionary.hpp +++ b/src/hotspot/share/classfile/systemDictionary.hpp @@ -77,6 +77,7 @@ template class GrowableArray; class SystemDictionary : AllStatic { friend class AOTLinkedClassBulkLoader; + friend class FinalImageRecipes; friend class BootstrapInfo; friend class LambdaProxyClassDictionary; friend class vmClasses; diff --git a/src/hotspot/share/classfile/systemDictionaryShared.cpp b/src/hotspot/share/classfile/systemDictionaryShared.cpp index 9a03a46e7e8..dba6d0dd244 100644 --- a/src/hotspot/share/classfile/systemDictionaryShared.cpp +++ b/src/hotspot/share/classfile/systemDictionaryShared.cpp @@ -120,34 +120,6 @@ InstanceKlass* SystemDictionaryShared::load_shared_class_for_builtin_loader( return nullptr; } -InstanceKlass* SystemDictionaryShared::load_shared_class_for_aot_safe_custom_loader( - Symbol* class_name, Handle class_loader, TRAPS) { - assert(CDSConfig::is_using_archive(), "must be"); - InstanceKlass* ik = find_class_in_aot_safe_custom_loader_dict(class_name); - - if (ik != nullptr && !ik->shared_loading_failed()) { - // In assembly phase the classes loaded by aot-safe loaders are loaded by CDS$UnregisteredClassLoader - // which doesn't have aot identity. So skip the aot-identity check. - if (class_loader() != UnregisteredClasses::unregistered_class_loader(THREAD)()) { - oop aot_identity = java_lang_ClassLoader::aotIdentity(class_loader()); - assert(aot_identity != nullptr, "must be"); - Symbol* aot_identity_sym = java_lang_String::as_symbol(aot_identity); - if (!aot_identity_sym->equals(ik->cl_aot_identity())) { - return nullptr; - } - } - SharedClassLoadingMark slm(THREAD, ik); - PackageEntry* pkg_entry = CDSProtectionDomain::get_package_entry_from_class(ik, class_loader); - 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; -} - // This function is called for loading only UNREGISTERED classes InstanceKlass* SystemDictionaryShared::lookup_from_stream(Symbol* class_name, Handle class_loader, @@ -673,24 +645,6 @@ InstanceKlass* SystemDictionaryShared::find_or_load_shared_class( } k = load_shared_class_for_builtin_loader(name, class_loader, THREAD); - } else if (CDSConfig::supports_custom_loaders() && CDSConfig::is_dumping_final_static_archive() && - class_loader() == UnregisteredClasses::unregistered_class_loader(THREAD)()) { - ClassLoaderData *loader_data = register_loader(class_loader); - Dictionary* dictionary = loader_data->dictionary(); - - // TODO: find_or_load_shared_class is called for - // aot safe custom loader, which may or may not be parallel-capable loader. - // Should we acquire loader lock or not? - //assert(get_loader_lock_or_null(class_loader) == nullptr, "ObjectLocker not required"); - { - MutexLocker mu(THREAD, SystemDictionary_lock); - InstanceKlass* check = dictionary->find_class(THREAD, name); - if (check != nullptr) { - return check; - } - } - - k = load_shared_class_for_aot_safe_custom_loader(name, class_loader, THREAD); } } @@ -1360,69 +1314,60 @@ unsigned int SystemDictionaryShared::hash_for_shared_dictionary(address ptr) { class CopySharedClassInfoToArchive : StackObj { CompactHashtableWriter* _writer; bool _is_builtin; - bool _is_defined_by_aot_safe_custom_loader; ArchiveBuilder *_builder; public: CopySharedClassInfoToArchive(CompactHashtableWriter* writer, - bool is_builtin, - bool is_defined_by_aot_safe_custom_loader) - : _writer(writer), _is_builtin(is_builtin), _is_defined_by_aot_safe_custom_loader(is_defined_by_aot_safe_custom_loader), _builder(ArchiveBuilder::current()) - { - assert(!_is_builtin || !_is_defined_by_aot_safe_custom_loader, "both conditions cannot be true"); - } + bool is_builtin) + : _writer(writer), _is_builtin(is_builtin), _builder(ArchiveBuilder::current()) + {} void do_entry(InstanceKlass* k, DumpTimeClassInfo& info) { - if (!info.is_excluded()) { - if ((info.is_builtin() && _is_builtin) || (k->is_defined_by_aot_safe_custom_loader() && _is_defined_by_aot_safe_custom_loader) || - (!info.is_builtin() && !_is_builtin && !k->is_defined_by_aot_safe_custom_loader() && !_is_defined_by_aot_safe_custom_loader)) { - size_t byte_size = info.runtime_info_bytesize(); - RunTimeClassInfo* record; - record = (RunTimeClassInfo*)ArchiveBuilder::ro_region_alloc(byte_size); - record->init(info); - - unsigned int hash; - Symbol* name = info._klass->name(); - name = ArchiveBuilder::current()->get_buffered_addr(name); - hash = SystemDictionaryShared::hash_for_shared_dictionary((address)name); - u4 delta = _builder->buffer_to_offset_u4((address)record); - if (_is_builtin && info._klass->is_hidden()) { - // skip - } else { - _writer->add(hash, delta); - } - if (log_is_enabled(Trace, aot, hashtables)) { - ResourceMark rm; - const char* dict_name = _is_builtin ? "builtin" : _is_defined_by_aot_safe_custom_loader ? "aot_safe_custom_loader_dict" : "unregistered"; - log_trace(aot, hashtables)("%s dictionary: %s", dict_name, info._klass->external_name()); - } - - // Save this for quick runtime lookup of InstanceKlass* -> RunTimeClassInfo* - InstanceKlass* buffered_klass = ArchiveBuilder::current()->get_buffered_addr(info._klass); - RunTimeClassInfo::set_for(buffered_klass, record); + if (!info.is_excluded() && info.is_builtin() == _is_builtin) { + size_t byte_size = info.runtime_info_bytesize(); + RunTimeClassInfo* record; + record = (RunTimeClassInfo*)ArchiveBuilder::ro_region_alloc(byte_size); + record->init(info); + + unsigned int hash; + Symbol* name = info._klass->name(); + name = ArchiveBuilder::current()->get_buffered_addr(name); + hash = SystemDictionaryShared::hash_for_shared_dictionary((address)name); + u4 delta = _builder->buffer_to_offset_u4((address)record); + if (_is_builtin && info._klass->is_hidden()) { + // skip + } else { + _writer->add(hash, delta); } + if (log_is_enabled(Trace, aot, hashtables)) { + ResourceMark rm; + const char* dict_name = _is_builtin ? "builtin" : "unregistered"; + log_trace(aot, hashtables)("%s dictionary: %s", dict_name, info._klass->external_name()); + } + + // Save this for quick runtime lookup of InstanceKlass* -> RunTimeClassInfo* + InstanceKlass* buffered_klass = ArchiveBuilder::current()->get_buffered_addr(info._klass); + RunTimeClassInfo::set_for(buffered_klass, record); } } }; void SystemDictionaryShared::write_dictionary(RunTimeSharedDictionary* dictionary, - bool is_builtin, - bool is_defined_by_aot_safe_custom_loader) { + bool is_builtin) { CompactHashtableStats stats; dictionary->reset(); - CompactHashtableWriter writer(_dumptime_table->count_of(is_builtin, is_defined_by_aot_safe_custom_loader), &stats); - CopySharedClassInfoToArchive copy(&writer, is_builtin, is_defined_by_aot_safe_custom_loader); + CompactHashtableWriter writer(_dumptime_table->count_of(is_builtin), &stats); + CopySharedClassInfoToArchive copy(&writer, is_builtin); assert_lock_strong(DumpTimeTable_lock); _dumptime_table->iterate_all_live_classes(©); - const char* dict_name = is_builtin ? "builtin" : is_defined_by_aot_safe_custom_loader ? "aot_safe_custom_loader_dict" : "unregistered"; + const char* dict_name = is_builtin ? "builtin" : "unregistered"; writer.dump(dictionary, dict_name); } void SystemDictionaryShared::write_to_archive(bool is_static_archive) { ArchiveInfo* archive = get_archive(is_static_archive); - write_dictionary(&archive->_builtin_dictionary, true, false); - write_dictionary(&archive->_aot_safe_custom_loader_dict, false, true); - write_dictionary(&archive->_unregistered_dictionary, false, false); + write_dictionary(&archive->_builtin_dictionary, true); + write_dictionary(&archive->_unregistered_dictionary, false); if (CDSConfig::is_dumping_lambdas_in_legacy_mode()) { LambdaProxyClassDictionary::write_dictionary(is_static_archive); } else { @@ -1435,7 +1380,6 @@ void SystemDictionaryShared::serialize_dictionary_headers(SerializeClosure* soc, ArchiveInfo* archive = get_archive(is_static_archive); archive->_builtin_dictionary.serialize_header(soc); - archive->_aot_safe_custom_loader_dict.serialize_header(soc); archive->_unregistered_dictionary.serialize_header(soc); LambdaProxyClassDictionary::serialize(soc, is_static_archive); } @@ -1497,24 +1441,6 @@ InstanceKlass* SystemDictionaryShared::find_builtin_class(Symbol* name) { } } -InstanceKlass* SystemDictionaryShared::find_class_in_aot_safe_custom_loader_dict(Symbol* name) { - const RunTimeClassInfo* record = find_record(&_static_archive._aot_safe_custom_loader_dict, - &_dynamic_archive._aot_safe_custom_loader_dict, - name); - if (record != nullptr) { - assert(!record->klass()->is_hidden(), "hidden class cannot be looked up by name"); - DEBUG_ONLY(check_klass_after_loading(record->klass());) - // We did not save the classfile data of the generated LambdaForm invoker classes, - // so we cannot support CLFH for such classes. - if (record->klass()->is_aot_generated_class() && JvmtiExport::should_post_class_file_load_hook()) { - return nullptr; - } - return record->klass(); - } else { - return nullptr; - } -} - void SystemDictionaryShared::update_shared_entry(InstanceKlass* k, int id) { assert(CDSConfig::is_dumping_static_archive(), "class ID is used only for static dump (from classlist)"); DumpTimeClassInfo* info = get_info(k); @@ -1543,11 +1469,6 @@ void SystemDictionaryShared::get_all_archived_classes(bool is_static_archive, Gr get_archive(is_static_archive)->_builtin_dictionary.iterate_all([&] (const RunTimeClassInfo* record) { classes->append(record->klass()); }); - - get_archive(is_static_archive)->_aot_safe_custom_loader_dict.iterate_all([&] (const RunTimeClassInfo* record) { - classes->append(record->klass()); - }); - get_archive(is_static_archive)->_unregistered_dictionary.iterate_all([&] (const RunTimeClassInfo* record) { classes->append(record->klass()); }); @@ -1578,8 +1499,6 @@ void SystemDictionaryShared::ArchiveInfo::print_on(const char* prefix, SharedDictionaryPrinter p(st); st->print_cr("%sShared Builtin Dictionary", prefix); _builtin_dictionary.iterate_all(&p); - st->print_cr("%sShared AOT Compatible Custom Loaders Dictionary", prefix); - _aot_safe_custom_loader_dict.iterate_all(&p); st->print_cr("%sShared Unregistered Dictionary", prefix); _unregistered_dictionary.iterate_all(&p); LambdaProxyClassDictionary::print_on(prefix, st, p.index(), is_static_archive); @@ -1589,7 +1508,6 @@ void SystemDictionaryShared::ArchiveInfo::print_table_statistics(const char* pre outputStream* st, bool is_static_archive) { st->print_cr("%sArchve Statistics", prefix); - _aot_safe_custom_loader_dict.print_table_statistics(st, "AOT Compatible Loaders Dictionary"); _unregistered_dictionary.print_table_statistics(st, "AOT Incompatible Loaders Dictionary"); LambdaProxyClassDictionary::print_statistics(st, is_static_archive); } @@ -1623,7 +1541,7 @@ void SystemDictionaryShared::print_table_statistics(outputStream* st) { bool SystemDictionaryShared::is_dumptime_table_empty() { assert_lock_strong(DumpTimeTable_lock); _dumptime_table->update_counts(); - if (_dumptime_table->count_of(true, false) == 0 && _dumptime_table->count_of(false, true) == 0 && _dumptime_table->count_of(false, false) == 0) { + if (_dumptime_table->count_of(true) == 0 && _dumptime_table->count_of(false) == 0) { return true; } return false; diff --git a/src/hotspot/share/classfile/systemDictionaryShared.hpp b/src/hotspot/share/classfile/systemDictionaryShared.hpp index 1177dc244f3..f1895587e11 100644 --- a/src/hotspot/share/classfile/systemDictionaryShared.hpp +++ b/src/hotspot/share/classfile/systemDictionaryShared.hpp @@ -140,7 +140,6 @@ class SystemDictionaryShared: public SystemDictionary { struct ArchiveInfo { RunTimeSharedDictionary _builtin_dictionary; - RunTimeSharedDictionary _aot_safe_custom_loader_dict; RunTimeSharedDictionary _unregistered_dictionary; void print_on(const char* prefix, outputStream* st, bool is_static_archive); @@ -163,10 +162,6 @@ class SystemDictionaryShared: public SystemDictionary { Symbol* class_name, Handle class_loader, TRAPS); - static InstanceKlass* load_shared_class_for_aot_safe_custom_loader( - Symbol* class_name, - Handle class_loader, - TRAPS); static InstanceKlass* acquire_class_for_current_thread( InstanceKlass *ik, Handle class_loader, @@ -175,8 +170,7 @@ class SystemDictionaryShared: public SystemDictionary { TRAPS); - static void write_dictionary(RunTimeSharedDictionary* dictionary, - bool is_builtin, bool is_defined_by_aot_safe_custom_loader); + static void write_dictionary(RunTimeSharedDictionary* dictionary, bool is_builtin); static bool is_jfr_event_class(InstanceKlass *k); static void link_all_exclusion_check_candidates(InstanceKlass* ik); static bool should_be_excluded_impl(InstanceKlass* k, DumpTimeClassInfo* info); @@ -215,7 +209,6 @@ class SystemDictionaryShared: public SystemDictionary { static void set_has_archived_enum_objs(InstanceKlass* ik); static InstanceKlass* find_builtin_class(Symbol* name); - static InstanceKlass* find_class_in_aot_safe_custom_loader_dict(Symbol* class_name); static const RunTimeClassInfo* find_record(RunTimeSharedDictionary* static_dict, RunTimeSharedDictionary* dynamic_dict, From e9fafef20e725d2ae6c14708b8732efe8a26cc4d Mon Sep 17 00:00:00 2001 From: Ashutosh Mehra Date: Wed, 18 Feb 2026 17:25:10 -0500 Subject: [PATCH 16/30] Some cleanup Signed-off-by: Ashutosh Mehra --- src/hotspot/share/cds/finalImageRecipes.cpp | 74 +++++++-------------- src/hotspot/share/cds/finalImageRecipes.hpp | 18 ++--- 2 files changed, 29 insertions(+), 63 deletions(-) diff --git a/src/hotspot/share/cds/finalImageRecipes.cpp b/src/hotspot/share/cds/finalImageRecipes.cpp index f01621d5456..3d51c33ae4d 100644 --- a/src/hotspot/share/cds/finalImageRecipes.cpp +++ b/src/hotspot/share/cds/finalImageRecipes.cpp @@ -71,25 +71,22 @@ void FinalImageRecipeTable::mark_pointers() { mark_pointers_in_array(_platform); ArchivePtrMarker::mark_pointer(&_app); mark_pointers_in_array(_app); - ArchivePtrMarker::mark_pointer(&_aot_safe_loader_classes); - mark_pointers_in_array(_aot_safe_loader_classes); - ArchivePtrMarker::mark_pointer(&_unregistered); - mark_pointers_in_array(_unregistered); + ArchivePtrMarker::mark_pointer(&_custom_loader_classes); + mark_pointers_in_array(_custom_loader_classes); } void FinalImageRecipes::record_all_classes() { _class_table = (FinalImageRecipeTable*)ArchiveBuilder::ro_region_alloc(sizeof(FinalImageRecipeTable)); ArchivePtrMarker::mark_pointer(&_class_table); - _class_table->set_boot1(write_builtin_loader_classes(nullptr, true)); - _class_table->set_boot2(write_builtin_loader_classes(nullptr, false)); - _class_table->set_platform(write_builtin_loader_classes(SystemDictionary::java_platform_loader(), false)); - _class_table->set_app(write_builtin_loader_classes(SystemDictionary::java_system_loader(), false)); - _class_table->set_aot_safe_loader_classes(write_custom_loader_classes(/*aot-safe loader classes*/ true)); - _class_table->set_unregistered(write_custom_loader_classes(/*aot-safe loader classes*/ false)); + _class_table->set_boot1(write_classes(nullptr, true, true)); + _class_table->set_boot2(write_classes(nullptr, false, true)); + _class_table->set_platform(write_classes(SystemDictionary::java_platform_loader(), false, true)); + _class_table->set_app(write_classes(SystemDictionary::java_system_loader(), false, true)); + _class_table->set_custom_loader_classes(write_classes(nullptr, false, false)); _class_table->mark_pointers(); } -Array* FinalImageRecipes::write_builtin_loader_classes(oop class_loader, bool is_javabase) { +Array* FinalImageRecipes::write_classes(oop class_loader, bool is_javabase, bool is_builtin_loader) { ResourceMark rm; GrowableArray list; GrowableArray* all_classes = ArchiveBuilder::current()->klasses(); @@ -98,11 +95,18 @@ Array* FinalImageRecipes::write_builtin_loader_classes(oop Klass* k = all_classes->at(i); if (k->is_instance_klass()) { InstanceKlass* ik = InstanceKlass::cast(k); - if (ik->class_loader() != class_loader) { - continue; - } - if ((ik->module() == ModuleEntryTable::javabase_moduleEntry()) != is_javabase) { - continue; + if (is_builtin_loader) { + if (ik->class_loader() != class_loader) { + continue; + } + if ((ik->module() == ModuleEntryTable::javabase_moduleEntry()) != is_javabase) { + continue; + } + } else { + // skip builtin loader classes when writing custom loader classes + if (SystemDictionaryShared::is_builtin(ik)) { + continue; + } } int flags = 0; Array* cp_recipe = record_recipe_for_constantpool(ik, flags); @@ -120,37 +124,6 @@ Array* FinalImageRecipes::write_builtin_loader_classes(oop } } -Array* FinalImageRecipes::write_custom_loader_classes(bool write_aot_safe_loader_classes) { - ResourceMark rm; - GrowableArray list; - GrowableArray* all_classes = ArchiveBuilder::current()->klasses(); - - for (int i = 0; i < all_classes->length(); i++) { - Klass* k = all_classes->at(i); - if (k->is_instance_klass()) { - InstanceKlass* ik = InstanceKlass::cast(k); - // skip builtin loader classes - if (SystemDictionaryShared::is_builtin(ik)) { - continue; - } - if (ik->is_defined_by_aot_safe_custom_loader() != write_aot_safe_loader_classes) { - continue; - } - int flags = 0; - Array* cp_recipe = record_recipe_for_constantpool(ik, flags); - InstanceKlassRecipe recipe(ArchiveBuilder::current()->get_buffered_addr(ik), cp_recipe, flags); - list.append(recipe); - } - } - if (list.length() == 0) { - return nullptr; - } else { - const char* category = AOTClassLinker::class_category_name(list.adr_at(0)->instance_klass()); - log_info(aot, link)("wrote %d class(es) for category %s", list.length(), category); - return ArchiveUtils::archive_array(&list); - } -} - Array* FinalImageRecipes::record_recipe_for_constantpool(InstanceKlass* ik, int& flags) { ConstantPool* cp = ik->constants(); ConstantPoolCache* cp_cache = cp->cache(); @@ -227,6 +200,7 @@ Array* FinalImageRecipes::record_recipe_for_constantpool(InstanceKlass* ik, return nullptr; } } + /* void FinalImageRecipes::record_recipes_for_constantpool() { ResourceMark rm; @@ -461,8 +435,7 @@ void FinalImageRecipes::load_custom_loader_classes(TRAPS) { initiate_loading(THREAD, "app", unreg_class_loader, _class_table->boot2()); initiate_loading(THREAD, "app", unreg_class_loader, _class_table->platform()); initiate_loading(THREAD, "app", unreg_class_loader, _class_table->app()); - load_classes_in_table(_class_table->aot_safe_loader_classes(), "aot-safe", unreg_class_loader, CHECK); - load_classes_in_table(_class_table->unregistered(), "unregistered", unreg_class_loader, CHECK); + load_classes_in_table(_class_table->custom_loader_classes(), "custom-loader", unreg_class_loader, CHECK); } void FinalImageRecipes::load_classes_in_table(Array* recipes, @@ -561,8 +534,7 @@ void FinalImageRecipes::link_classes_impl(TRAPS) { link_classes_in_table(_class_table->boot2(), CHECK); link_classes_in_table(_class_table->platform(), CHECK); link_classes_in_table(_class_table->app(), CHECK); - link_classes_in_table(_class_table->aot_safe_loader_classes(), CHECK); - link_classes_in_table(_class_table->unregistered(), CHECK); + link_classes_in_table(_class_table->custom_loader_classes(), CHECK); } void FinalImageRecipes::link_classes_in_table(Array* recipes, TRAPS) { diff --git a/src/hotspot/share/cds/finalImageRecipes.hpp b/src/hotspot/share/cds/finalImageRecipes.hpp index 572d14ea28a..0703bc4b862 100644 --- a/src/hotspot/share/cds/finalImageRecipes.hpp +++ b/src/hotspot/share/cds/finalImageRecipes.hpp @@ -62,8 +62,7 @@ class FinalImageRecipeTable { Array* _platform; Array* _app; - Array* _aot_safe_loader_classes; - Array* _unregistered; + Array* _custom_loader_classes; template void iterate_array(Function fn, Array* array) { @@ -78,21 +77,19 @@ class FinalImageRecipeTable { FinalImageRecipeTable() : _boot1(nullptr), _boot2(nullptr), _platform(nullptr), _app(nullptr), - _aot_safe_loader_classes(nullptr), _unregistered(nullptr) {} + _custom_loader_classes(nullptr) {} Array* boot1() const { return _boot1; } Array* boot2() const { return _boot2; } Array* platform() const { return _platform; } Array* app() const { return _app; } - Array* aot_safe_loader_classes() const { return _aot_safe_loader_classes; } - Array* unregistered() const { return _unregistered; } + Array* custom_loader_classes() const { return _custom_loader_classes; } 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; } - void set_aot_safe_loader_classes(Array* value) { _aot_safe_loader_classes = value; } - void set_unregistered(Array* value) { _unregistered = value; } + void set_custom_loader_classes(Array* value) { _custom_loader_classes = value; } template void iterate_all(Function fn) { @@ -100,8 +97,7 @@ class FinalImageRecipeTable { iterate_array(fn, _boot2); iterate_array(fn, _platform); iterate_array(fn, _app); - iterate_array(fn, _aot_safe_loader_classes); - iterate_array(fn, _unregistered); + iterate_array(fn, _custom_loader_classes); } void mark_pointers(); }; @@ -183,9 +179,7 @@ class FinalImageRecipes { void apply_recipes_for_reflection_data(JavaThread* current); void apply_recipes_for_dynamic_proxies(TRAPS); - - Array* write_builtin_loader_classes(oop class_loader, bool is_javabase); - Array* write_custom_loader_classes(bool write_aot_safe_loader_classes); + Array* write_classes(oop class_loader, bool is_javabase, bool is_builtin_loader); static void exit_on_exception(JavaThread* current); From d3eeebc27267a061c04c26f272400a7803763266 Mon Sep 17 00:00:00 2001 From: Ashutosh Mehra Date: Wed, 18 Feb 2026 17:28:47 -0500 Subject: [PATCH 17/30] Revert 7fe29c65435154c0da2dbcfaeb172896f48e5773 Signed-off-by: Ashutosh Mehra --- src/hotspot/share/classfile/classLoaderData.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/hotspot/share/classfile/classLoaderData.cpp b/src/hotspot/share/classfile/classLoaderData.cpp index c6fdf2c6f1a..46873d2adfa 100644 --- a/src/hotspot/share/classfile/classLoaderData.cpp +++ b/src/hotspot/share/classfile/classLoaderData.cpp @@ -161,10 +161,6 @@ ClassLoaderData::ClassLoaderData(Handle h_class_loader, bool has_class_mirror_ho oop aot_identity = java_lang_ClassLoader::aotIdentity(h_class_loader()); if (aot_identity != nullptr) { _aot_identity = java_lang_String::as_symbol(aot_identity); - // keep aot-safe custom loaders alive during training run - if (CDSConfig::is_dumping_preimage_static_archive()) { - _keep_alive_ref_count = 1; - } } initialize_name(h_class_loader); } From 0d29c8b315940d2ba65b11595c5ec96209e55ad8 Mon Sep 17 00:00:00 2001 From: Ashutosh Mehra Date: Wed, 18 Feb 2026 17:43:32 -0500 Subject: [PATCH 18/30] Remove unused code Signed-off-by: Ashutosh Mehra --- src/hotspot/share/cds/cdsProtectionDomain.cpp | 143 ++++++------------ src/hotspot/share/cds/cdsProtectionDomain.hpp | 2 - src/hotspot/share/cds/heapShared.cpp | 9 -- .../share/classes/java/lang/ClassLoader.java | 6 +- .../share/classes/java/lang/NamedPackage.java | 2 +- 5 files changed, 51 insertions(+), 111 deletions(-) diff --git a/src/hotspot/share/cds/cdsProtectionDomain.cpp b/src/hotspot/share/cds/cdsProtectionDomain.cpp index 3dd876d8808..399c401ec2d 100644 --- a/src/hotspot/share/cds/cdsProtectionDomain.cpp +++ b/src/hotspot/share/cds/cdsProtectionDomain.cpp @@ -47,67 +47,59 @@ OopHandle CDSProtectionDomain::_shared_jar_manifests; // the given InstanceKlass. // Returns the ProtectionDomain for the InstanceKlass. Handle CDSProtectionDomain::init_security_info(Handle class_loader, InstanceKlass* ik, PackageEntry* pkg_entry, TRAPS) { - if (false && ik->defined_by_other_loaders()) { - assert(ik->cl_aot_identity() != nullptr, "sanity check"); - // define NamedPackage in java layer - define_named_package(class_loader, pkg_entry, THREAD); - // Get ClassLoader::defaultDomain - return get_default_protection_domain(class_loader, THREAD); - } else { - int index = ik->shared_classpath_index(); - assert(index >= 0, "Sanity"); - const AOTClassLocation* cl = AOTClassLocationConfig::runtime()->class_location_at(index); - Symbol* class_name = ik->name(); + int index = ik->shared_classpath_index(); + assert(index >= 0, "Sanity"); + const AOTClassLocation* cl = AOTClassLocationConfig::runtime()->class_location_at(index); + Symbol* class_name = ik->name(); - if (cl->is_modules_image()) { - // For shared app/platform classes originated from the run-time image: - // The ProtectionDomains are cached in the corresponding ModuleEntries - // for fast access by the VM. - // all packages from module image are already created during VM bootstrap in - // Modules::define_module(). - assert(pkg_entry != nullptr, "archived class in module image cannot be from unnamed package"); - ModuleEntry* mod_entry = pkg_entry->module(); - return get_shared_protection_domain(class_loader, mod_entry, THREAD); - } else { - // For shared app/platform classes originated from JAR files on the class path: - // Each of the 3 CDSProtectionDomain::_shared_xxx arrays has the same length - // as the shared classpath table in the shared archive. - // - // If a shared InstanceKlass k is loaded from the class path, let - // - // index = k->shared_classpath_index(); - // - // AOTClassLocationConfig::_runtime_instance->_array->at(index) identifies the JAR file that contains k. - // - // k's protection domain is: - // - // ProtectionDomain pd = _shared_protection_domains[index]; - // - // and k's Package is initialized using - // - // manifest = _shared_jar_manifests[index]; - // url = _shared_jar_urls[index]; - // define_shared_package(class_name, class_loader, manifest, url, CHECK_NH); - // - // Note that if an element of these 3 _shared_xxx arrays is null, it will be initialized by - // the corresponding CDSProtectionDomain::get_shared_xxx() function. - Handle manifest = get_shared_jar_manifest(index, CHECK_NH); - Handle url = get_shared_jar_url(index, CHECK_NH); - int index_offset = index - AOTClassLocationConfig::runtime()->app_cp_start_index(); - if (index_offset < PackageEntry::max_index_for_defined_in_class_path()) { - if (pkg_entry == nullptr || !pkg_entry->is_defined_by_cds_in_class_path(index_offset)) { - // define_shared_package only needs to be called once for each package in a jar specified - // in the shared class path. - define_shared_package(class_name, class_loader, manifest, url, CHECK_NH); - if (pkg_entry != nullptr) { - pkg_entry->set_defined_by_cds_in_class_path(index_offset); - } - } - } else { + if (cl->is_modules_image()) { + // For shared app/platform classes originated from the run-time image: + // The ProtectionDomains are cached in the corresponding ModuleEntries + // for fast access by the VM. + // all packages from module image are already created during VM bootstrap in + // Modules::define_module(). + assert(pkg_entry != nullptr, "archived class in module image cannot be from unnamed package"); + ModuleEntry* mod_entry = pkg_entry->module(); + return get_shared_protection_domain(class_loader, mod_entry, THREAD); + } else { + // For shared app/platform classes originated from JAR files on the class path: + // Each of the 3 CDSProtectionDomain::_shared_xxx arrays has the same length + // as the shared classpath table in the shared archive. + // + // If a shared InstanceKlass k is loaded from the class path, let + // + // index = k->shared_classpath_index(); + // + // AOTClassLocationConfig::_runtime_instance->_array->at(index) identifies the JAR file that contains k. + // + // k's protection domain is: + // + // ProtectionDomain pd = _shared_protection_domains[index]; + // + // and k's Package is initialized using + // + // manifest = _shared_jar_manifests[index]; + // url = _shared_jar_urls[index]; + // define_shared_package(class_name, class_loader, manifest, url, CHECK_NH); + // + // Note that if an element of these 3 _shared_xxx arrays is null, it will be initialized by + // the corresponding CDSProtectionDomain::get_shared_xxx() function. + Handle manifest = get_shared_jar_manifest(index, CHECK_NH); + Handle url = get_shared_jar_url(index, CHECK_NH); + int index_offset = index - AOTClassLocationConfig::runtime()->app_cp_start_index(); + if (index_offset < PackageEntry::max_index_for_defined_in_class_path()) { + if (pkg_entry == nullptr || !pkg_entry->is_defined_by_cds_in_class_path(index_offset)) { + // define_shared_package only needs to be called once for each package in a jar specified + // in the shared class path. define_shared_package(class_name, class_loader, manifest, url, CHECK_NH); + if (pkg_entry != nullptr) { + pkg_entry->set_defined_by_cds_in_class_path(index_offset); + } } - return get_shared_protection_domain(class_loader, index, url, THREAD); + } else { + define_shared_package(class_name, class_loader, manifest, url, CHECK_NH); } + return get_shared_protection_domain(class_loader, index, url, THREAD); } } @@ -166,33 +158,6 @@ void CDSProtectionDomain::define_shared_package(Symbol* class_name, } } -void CDSProtectionDomain::define_named_package(Handle class_loader, PackageEntry* pkg_entry, TRAPS) { - Handle pkgname_string; - Handle module_h; - - if (pkg_entry != nullptr) { - ResourceMark rm(THREAD); - Symbol* pkg = pkg_entry->name(); - const char* pkgname = pkg->as_klass_external_name(); - pkgname_string = java_lang_String::create_from_str(pkgname, CHECK); - module_h = Handle(THREAD, pkg_entry->module()->module_oop()); - } else { - // unnamed package; get unnamed module from class_loader - module_h = Handle(THREAD, java_lang_ClassLoader::unnamedModule(class_loader())); - } - if (pkgname_string.not_null()) { - JavaValue result(T_OBJECT); - JavaCallArguments args(2); - args.set_receiver(class_loader); - args.push_oop(pkgname_string); - args.push_oop(module_h); - JavaCalls::call_virtual(&result, vmClasses::ClassLoader_klass(), - vmSymbols::getNamedPackage_name(), - vmSymbols::getNamedPackage_signature(), - &args, - CHECK); - } -} Handle CDSProtectionDomain::create_jar_manifest(const char* manifest_chars, size_t size, TRAPS) { typeArrayOop buf = oopFactory::new_byteArray((int)size, CHECK_NH); typeArrayHandle bufhandle(THREAD, buf); @@ -270,16 +235,6 @@ Handle CDSProtectionDomain::get_protection_domain_from_classloader(Handle class_ return Handle(THREAD, obj_result.get_oop()); } -Handle CDSProtectionDomain::get_default_protection_domain(Handle class_loader, TRAPS) { - Handle protection_domain; - JavaValue obj_result(T_OBJECT); - JavaCalls::call_virtual(&obj_result, class_loader, vmClasses::ClassLoader_klass(), - vmSymbols::getDefaultProtectionDomain_name(), - vmSymbols::getDefaultProtectionDomain_signature(), - CHECK_NH); - return Handle(THREAD, obj_result.get_oop()); -} - // Returns the ProtectionDomain associated with the JAR file identified by the url. Handle CDSProtectionDomain::get_shared_protection_domain(Handle class_loader, int shared_path_index, diff --git a/src/hotspot/share/cds/cdsProtectionDomain.hpp b/src/hotspot/share/cds/cdsProtectionDomain.hpp index 6141431b78b..4a82d6fe869 100644 --- a/src/hotspot/share/cds/cdsProtectionDomain.hpp +++ b/src/hotspot/share/cds/cdsProtectionDomain.hpp @@ -113,8 +113,6 @@ class CDSProtectionDomain : AllStatic { static void atomic_set_shared_jar_manifest(int index, oop man) { atomic_set_array_index(_shared_jar_manifests, index, man); } - static void define_named_package(Handle class_loader, PackageEntry* pkg_entry, TRAPS); - static Handle get_default_protection_domain(Handle class_loader, TRAPS); }; #endif // SHARE_CDS_CDSPROTECTIONDOMAIN_HPP diff --git a/src/hotspot/share/cds/heapShared.cpp b/src/hotspot/share/cds/heapShared.cpp index 7cc5602de01..0cf802524d0 100644 --- a/src/hotspot/share/cds/heapShared.cpp +++ b/src/hotspot/share/cds/heapShared.cpp @@ -1253,15 +1253,6 @@ void KlassSubGraphInfo::check_allowed_klass(InstanceKlass* ik) { const char* testcls_msg = ""; #endif - if (CDSConfig::supports_custom_loaders()) { - if (ik->class_loader() == SystemDictionary::java_system_loader()) { - return; - } - if (ik->cl_aot_identity() != nullptr) { - return; - } - } - ResourceMark rm; log_error(aot, heap)("Class %s not allowed in archive heap. Must be in java.base%s%s", ik->external_name(), lambda_msg, testcls_msg); diff --git a/src/java.base/share/classes/java/lang/ClassLoader.java b/src/java.base/share/classes/java/lang/ClassLoader.java index 0bdcbb08658..5e2d3fd89f7 100644 --- a/src/java.base/share/classes/java/lang/ClassLoader.java +++ b/src/java.base/share/classes/java/lang/ClassLoader.java @@ -346,7 +346,7 @@ void addClass(Class c) { * module * @return NamedPackage */ - protected NamedPackage getNamedPackage(String pn, Module m) { + private NamedPackage getNamedPackage(String pn, Module m) { NamedPackage p = packages.get(pn); if (p == null) { p = new NamedPackage(pn, m); @@ -2658,10 +2658,6 @@ private void resetArchivedStates() { libraries.clear(); } - ProtectionDomain getDefaultProtectionDomain() { - return defaultDomain; - } - private String aotIdentity = null; /** diff --git a/src/java.base/share/classes/java/lang/NamedPackage.java b/src/java.base/share/classes/java/lang/NamedPackage.java index 91756bc33c5..6234b949e65 100644 --- a/src/java.base/share/classes/java/lang/NamedPackage.java +++ b/src/java.base/share/classes/java/lang/NamedPackage.java @@ -40,7 +40,7 @@ * packages with minimal footprint and avoid constructing Package * object. */ -public class NamedPackage { +class NamedPackage { private final String name; private final Module module; From a5d31aaf16e9dda2aff5ac423f163edb69d3e73e Mon Sep 17 00:00:00 2001 From: Ashutosh Mehra Date: Wed, 18 Feb 2026 17:46:01 -0500 Subject: [PATCH 19/30] Remove whitespace Signed-off-by: Ashutosh Mehra --- src/hotspot/share/cds/aotLinkedClassBulkLoader.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hotspot/share/cds/aotLinkedClassBulkLoader.cpp b/src/hotspot/share/cds/aotLinkedClassBulkLoader.cpp index 69b944e2f1a..710b24ba2e1 100644 --- a/src/hotspot/share/cds/aotLinkedClassBulkLoader.cpp +++ b/src/hotspot/share/cds/aotLinkedClassBulkLoader.cpp @@ -156,14 +156,14 @@ void AOTLinkedClassBulkLoader::mark_initiating_loader(JavaThread* currentThread, //terminating condition: loader is builtin loader for which this computation has already been done return; } - ClassLoaderData* cl_data = java_lang_ClassLoader::loader_data(loader); + ClassLoaderData* cl_data = java_lang_ClassLoader::loader_data(loader); if (processed.contains(cl_data)) { return; } oop parent = java_lang_ClassLoader::parent(loader); assert(parent != nullptr, "custom loader's parent loader cannot be null"); mark_initiating_loader(currentThread, parent, processed); - ClassLoaderData* parent_cl_data = java_lang_ClassLoader::loader_data(parent); + ClassLoaderData* parent_cl_data = java_lang_ClassLoader::loader_data(parent); Dictionary* parent_dict = parent_cl_data->dictionary(); Dictionary* loader_dict = cl_data->dictionary(); DictionaryCopier copier(loader_dict); From 5bca9fa31d5294a05a8d336eec6a3292a7cede25 Mon Sep 17 00:00:00 2001 From: Ashutosh Mehra Date: Thu, 19 Feb 2026 16:08:42 -0500 Subject: [PATCH 20/30] Skip archiving URLClassLoader instance classpath if it has not loaded any class Signed-off-by: Ashutosh Mehra --- src/hotspot/share/cds/aotClassLocation.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/hotspot/share/cds/aotClassLocation.cpp b/src/hotspot/share/cds/aotClassLocation.cpp index e7ebdb29433..42f710948c6 100644 --- a/src/hotspot/share/cds/aotClassLocation.cpp +++ b/src/hotspot/share/cds/aotClassLocation.cpp @@ -1157,6 +1157,11 @@ class URLClassLoaderClasspathArchiver : StackObj { {} bool do_entry(Symbol* loader_id, GrowableClassLocationArray* class_locations) { + // If the loader_id has not been archived yet, it implies no class was loaded using this loader. + // So there is no point to archive classpath for this loader_id. + if (!_builder->has_been_archived(loader_id)) { + return true; + } Array* archived_copy = ArchiveBuilder::new_ro_array(class_locations->length()); for (int i = 0; i < class_locations->length(); i++) { archived_copy->at_put(i, class_locations->at(i)->write_to_archive()); From 3f5670f3ab68b69f46ccadb70ec59d684040be72 Mon Sep 17 00:00:00 2001 From: Ashutosh Mehra Date: Thu, 19 Feb 2026 16:10:06 -0500 Subject: [PATCH 21/30] Move code for creating custom loader specific class list to a separate file Signed-off-by: Ashutosh Mehra --- src/hotspot/share/cds/aotClassLinker.cpp | 75 +++--------------- src/hotspot/share/cds/aotClassLinker.hpp | 6 +- .../share/cds/aotLinkedClassBulkLoader.cpp | 5 +- src/hotspot/share/cds/aotMetaspace.cpp | 2 +- src/hotspot/share/cds/customLoaderSupport.hpp | 79 +++++++++++++++++++ 5 files changed, 97 insertions(+), 70 deletions(-) create mode 100644 src/hotspot/share/cds/customLoaderSupport.hpp diff --git a/src/hotspot/share/cds/aotClassLinker.cpp b/src/hotspot/share/cds/aotClassLinker.cpp index 711b664d69d..c8ab98df992 100644 --- a/src/hotspot/share/cds/aotClassLinker.cpp +++ b/src/hotspot/share/cds/aotClassLinker.cpp @@ -28,6 +28,7 @@ #include "cds/archiveBuilder.hpp" #include "cds/archiveUtils.inline.hpp" #include "cds/cdsConfig.hpp" +#include "cds/customLoaderSupport.hpp" #include "cds/heapShared.hpp" #include "cds/lambdaFormInvokers.inline.hpp" #include "classfile/classLoader.hpp" @@ -47,9 +48,9 @@ GrowableArrayCHeap* AOTClassLinker::_sorted_candi static const unsigned INITIAL_TABLE_SIZE = 997; // prime number static const unsigned MAX_TABLE_SIZE = 10000; -typedef GrowableArrayCHeap ClassList; -typedef ResizeableHashTable ClassLoaderIdToPrelinkedTable; -ClassLoaderIdToPrelinkedTable* _custom_loader_prelinked_table; + +ClassLoaderIdToClassTableMap * _custom_loader_prelinked_table; +ArchivedCustomLoaderClassTableMap _archived_custom_loader_prelinked_classes_map; #ifdef ASSERT bool AOTClassLinker::is_initialized() { @@ -65,7 +66,7 @@ void AOTClassLinker::initialize() { _candidates = new (mtClass)ClassesTable(); _sorted_candidates = new GrowableArrayCHeap(1000); - _custom_loader_prelinked_table = new (mtClass) ClassLoaderIdToPrelinkedTable(INITIAL_TABLE_SIZE, MAX_TABLE_SIZE); + _custom_loader_prelinked_table = new (mtClass) ClassLoaderIdToClassTableMap(INITIAL_TABLE_SIZE, MAX_TABLE_SIZE); for (auto id : EnumRange{}) { add_vm_class(vmClasses::klass_at(id)); @@ -211,67 +212,20 @@ void AOTClassLinker::add_candidates() { } } -static inline bool prelinked_table_equals(AOTLinkedClassTableForCustomLoader* table, Symbol* loader_id, int len_unused) { - return table->loader_id()->equals(loader_id); -} - -class ArchivedCustomLoaderPrelinkedTable : public OffsetCompactHashtable {}; -ArchivedCustomLoaderPrelinkedTable _archived_custom_loader_prelinked_table; - -AOTLinkedClassTableForCustomLoader* AOTClassLinker::get_prelinked_table(Symbol* aot_id) { - unsigned int hash = Symbol::symbol_hash(aot_id); - return _archived_custom_loader_prelinked_table.lookup(aot_id, hash, 0 /* ignored */); +CustomLoaderClassTable* AOTClassLinker::get_archived_prelinked_table(Symbol* aot_id) { + return _archived_custom_loader_prelinked_classes_map.get_class_list(aot_id); } void AOTClassLinker::all_symbols_do(MetaspaceClosure* it) { - _custom_loader_prelinked_table->iterate_all([&](Symbol* loader_id, ClassList* class_list) { + _custom_loader_prelinked_table->iterate_all([&](Symbol*& loader_id, ClassList*& class_list) { it->push(&loader_id); }); } -void AOTClassLinker::serialize_prelinked_table_header(SerializeClosure* soc) { - _archived_custom_loader_prelinked_table.serialize_header(soc); +void AOTClassLinker::serialize_prelinked_classes_map_header(SerializeClosure* soc) { + _archived_custom_loader_prelinked_classes_map.serialize_header(soc); } -void AOTClassLinker::print_archived_custom_loader_prelinked_table() { - if (log_is_enabled(Info, aot, link)) { - ResourceMark rm; - _archived_custom_loader_prelinked_table.iterate([&](AOTLinkedClassTableForCustomLoader* table) { - Array* class_list = table->class_list(); - log_info(aot, link)("Class loader \"%s\" has %d classes in prelinked table", table->loader_id()->as_C_string(), class_list->length()); - for (int i = 0; i < class_list->length(); i++) { - InstanceKlass* ik = class_list->at(i); - log_info(aot, link)(" %s", ik->external_name()); - } - return true; - }); - } -} - -class CopyPrelinkTableToArchive : StackObj { -private: - CompactHashtableWriter* _writer; - ArchiveBuilder* _builder; -public: - CopyPrelinkTableToArchive(CompactHashtableWriter* writer) : _writer(writer), - _builder(ArchiveBuilder::current()) - {} - - bool do_entry(Symbol* loader_id, ClassList* class_list) { - AOTLinkedClassTableForCustomLoader* tableForLoader = (AOTLinkedClassTableForCustomLoader*)ArchiveBuilder::ro_region_alloc(sizeof(AOTLinkedClassTableForCustomLoader)); - assert(_builder->has_been_archived(loader_id), "must be"); - Symbol* buffered_sym = _builder->get_buffered_addr(loader_id); - tableForLoader->init(buffered_sym, ArchiveUtils::archive_array(class_list)); - ArchivePtrMarker::mark_pointer(tableForLoader->loader_id_addr()); - ArchivePtrMarker::mark_pointer(tableForLoader->class_list_addr()); - unsigned int hash = Symbol::symbol_hash(loader_id); - u4 delta = _builder->buffer_to_offset_u4((address)tableForLoader); - _writer->add(hash, delta); - return true; - } -}; - void AOTClassLinker::write_to_archive() { assert(is_initialized(), "sanity"); assert_at_safepoint(); @@ -283,11 +237,7 @@ void AOTClassLinker::write_to_archive() { table->set_platform(write_classes(SystemDictionary::java_platform_loader(), false)); table->set_app(write_classes(SystemDictionary::java_system_loader(), false)); - CompactHashtableStats stats; - CompactHashtableWriter writer(_custom_loader_prelinked_table->number_of_entries(), &stats); - CopyPrelinkTableToArchive archiver(&writer); - _custom_loader_prelinked_table->iterate(&archiver); - writer.dump(&_archived_custom_loader_prelinked_table, "archived prelinked table"); + _custom_loader_prelinked_table->write_to_archive(&_archived_custom_loader_prelinked_classes_map, "archived prelinked table"); if (log_is_enabled(Info, aot, link)) { ResourceMark rm; @@ -297,7 +247,6 @@ void AOTClassLinker::write_to_archive() { InstanceKlass* ik = class_list->at(i); log_info(aot, link)(" %s", ik->external_name()); } - return true; }); } } @@ -328,8 +277,6 @@ Array* AOTClassLinker::write_classes(oop class_loader, bool is_j } } - - int AOTClassLinker::num_platform_initiated_classes() { if (CDSConfig::is_dumping_aot_linked_classes()) { // AOTLinkedClassBulkLoader will initiate loading of all public boot classes in the platform loader. diff --git a/src/hotspot/share/cds/aotClassLinker.hpp b/src/hotspot/share/cds/aotClassLinker.hpp index cdeb1a5fcac..f3bd5aed852 100644 --- a/src/hotspot/share/cds/aotClassLinker.hpp +++ b/src/hotspot/share/cds/aotClassLinker.hpp @@ -35,6 +35,7 @@ #include "utilities/macros.hpp" class AOTLinkedClassTable; +class CustomLoaderClassTable; class InstanceKlass; class SerializeClosure; template class Array; @@ -131,9 +132,8 @@ class AOTClassLinker : AllStatic { static int num_platform_initiated_classes(); static void all_symbols_do(MetaspaceClosure* it); - static void serialize_prelinked_table_header(SerializeClosure* soc); - static void print_archived_custom_loader_prelinked_table(); - static AOTLinkedClassTableForCustomLoader* get_prelinked_table(Symbol* aot_id); + static void serialize_prelinked_classes_map_header(SerializeClosure* soc); + static CustomLoaderClassTable* get_archived_prelinked_table(Symbol* aot_id); // Used in logging: "boot1", "boot2", "plat", "app" and "unreg"; static const char* class_category_name(AOTLinkedClassCategory category); diff --git a/src/hotspot/share/cds/aotLinkedClassBulkLoader.cpp b/src/hotspot/share/cds/aotLinkedClassBulkLoader.cpp index 710b24ba2e1..21ef12b868c 100644 --- a/src/hotspot/share/cds/aotLinkedClassBulkLoader.cpp +++ b/src/hotspot/share/cds/aotLinkedClassBulkLoader.cpp @@ -28,6 +28,7 @@ #include "cds/aotLinkedClassBulkLoader.hpp" #include "cds/aotLinkedClassTable.hpp" #include "cds/cdsConfig.hpp" +#include "cds/customLoaderSupport.hpp" #include "cds/heapShared.hpp" #include "classfile/classLoaderData.hpp" #include "classfile/classLoaderDataShared.hpp" @@ -110,7 +111,7 @@ void AOTLinkedClassBulkLoader::preload_classes_for_loader_impl(Handle loader_obj if (aot_id == nullptr) { return; } - AOTLinkedClassTableForCustomLoader* table = AOTClassLinker::get_prelinked_table(aot_id); + CustomLoaderClassTable* table = AOTClassLinker::get_archived_prelinked_table(aot_id); preload_classes_in_table(table->class_list(), aot_id->as_C_string(), loader_obj, CHECK); MonitorLocker mu1(SystemDictionary_lock); mark_initiating_loader(THREAD, loader_obj()); @@ -247,7 +248,7 @@ void AOTLinkedClassBulkLoader::link_classes_for_loader_impl(Handle loader_obj, T if (aot_id == nullptr) { return; } - AOTLinkedClassTableForCustomLoader* table = AOTClassLinker::get_prelinked_table(aot_id); + CustomLoaderClassTable* table = AOTClassLinker::get_archived_prelinked_table(aot_id); link_classes_in_table(table->class_list(), CHECK); } diff --git a/src/hotspot/share/cds/aotMetaspace.cpp b/src/hotspot/share/cds/aotMetaspace.cpp index a99c751cf9f..21e664dfe28 100644 --- a/src/hotspot/share/cds/aotMetaspace.cpp +++ b/src/hotspot/share/cds/aotMetaspace.cpp @@ -532,7 +532,7 @@ void AOTMetaspace::serialize(SerializeClosure* soc) { HeapShared::serialize_tables(soc); SystemDictionaryShared::serialize_dictionary_headers(soc); AOTLinkedClassBulkLoader::serialize(soc); - AOTClassLinker::serialize_prelinked_table_header(soc); + AOTClassLinker::serialize_prelinked_classes_map_header(soc); URLClassLoaderClasspathSupport::serialize_classpath_map_table_header(soc); FinalImageRecipes::serialize(soc); TrainingData::serialize(soc); diff --git a/src/hotspot/share/cds/customLoaderSupport.hpp b/src/hotspot/share/cds/customLoaderSupport.hpp new file mode 100644 index 00000000000..2c0fa384e95 --- /dev/null +++ b/src/hotspot/share/cds/customLoaderSupport.hpp @@ -0,0 +1,79 @@ + +#ifndef SHARE_CDS_CUSTOM_LOADER_SUPPORT_HPP +#define SHARE_CDS_CUSTOM_LOADER_SUPPORT_HPP + +#include "classfile/compactHashtable.hpp" +#include "oops/instanceKlass.hpp" +#include "utilities/growableArray.hpp" +#include "utilities/resizableHashTable.hpp" + +class CustomLoaderClassTable { +private: + Symbol* _loader_id; + Array* _class_list; +public: + void init(Symbol* aot_id, Array* class_list) { + _loader_id = aot_id; + _class_list = class_list; + } + Symbol* loader_id() const { return _loader_id; } + address* loader_id_addr() const { return (address*)&_loader_id; } + Array* class_list() const { return _class_list; } + address* class_list_addr() const { return (address*)&_class_list; } +}; + +inline bool custom_loader_class_list_equals(CustomLoaderClassTable* table, Symbol* loader_id, int len_unused) { + return table->loader_id()->equals(loader_id); +} + +class ArchivedCustomLoaderClassTableMap : public OffsetCompactHashtable +{ +public: + CustomLoaderClassTable* get_class_list(Symbol* aot_id) { + unsigned int hash = Symbol::symbol_hash(aot_id); + return lookup(aot_id, hash, 0 /* ignored */); + } +}; + +typedef GrowableArrayCHeap ClassList; + +class ClassLoaderIdToClassTableMap : public ResizeableHashTable +{ + using ResizeableHashTableBase = ResizeableHashTable; +private: + class CopyClassTableToArchive : StackObj { + private: + CompactHashtableWriter* _writer; + ArchiveBuilder* _builder; + public: + CopyClassTableToArchive(CompactHashtableWriter* writer) : _writer(writer), + _builder(ArchiveBuilder::current()) + {} + + bool do_entry(Symbol* loader_id, ClassList* table) { + CustomLoaderClassTable* tableForLoader = (CustomLoaderClassTable*)ArchiveBuilder::ro_region_alloc(sizeof(CustomLoaderClassTable)); + assert(_builder->has_been_archived(loader_id), "must be"); + Symbol* buffered_sym = _builder->get_buffered_addr(loader_id); + tableForLoader->init(buffered_sym, ArchiveUtils::archive_array(table)); + ArchivePtrMarker::mark_pointer(tableForLoader->loader_id_addr()); + ArchivePtrMarker::mark_pointer(tableForLoader->class_list_addr()); + unsigned int hash = Symbol::symbol_hash(loader_id); + u4 delta = _builder->buffer_to_offset_u4((address)tableForLoader); + _writer->add(hash, delta); + return true; + } + }; + +public: + ClassLoaderIdToClassTableMap(unsigned size, unsigned max_size) : ResizeableHashTableBase(size, max_size) {} + + void write_to_archive(ArchivedCustomLoaderClassTableMap* archived_map, const char* map_name) { + CompactHashtableStats stats; + CompactHashtableWriter writer(number_of_entries(), &stats); + CopyClassTableToArchive archiver(&writer); + iterate(&archiver); + writer.dump(archived_map, map_name); + } +}; + +#endif // SHARE_CDS_CUSTOM_LOADER_SUPPORT_HPP From 12cc3f1fa9190d790cc1bdf0e969efd1074ef193 Mon Sep 17 00:00:00 2001 From: Ashutosh Mehra Date: Wed, 25 Feb 2026 16:55:19 -0500 Subject: [PATCH 22/30] Store aot-safe classes in a map in FinalImageRecipes Signed-off-by: Ashutosh Mehra --- src/hotspot/share/cds/aotClassLinker.cpp | 12 +- src/hotspot/share/cds/aotClassLinker.hpp | 23 +- .../share/cds/aotLinkedClassBulkLoader.cpp | 4 +- src/hotspot/share/cds/customLoaderSupport.hpp | 37 +- src/hotspot/share/cds/finalImageRecipes.cpp | 320 +++++++++++------- src/hotspot/share/cds/finalImageRecipes.hpp | 21 +- src/hotspot/share/cds/unregisteredClasses.cpp | 11 + src/hotspot/share/cds/unregisteredClasses.hpp | 1 + .../share/classfile/classLoaderData.cpp | 6 + .../share/classfile/classLoaderData.hpp | 1 + .../classfile/systemDictionaryShared.cpp | 2 +- 11 files changed, 273 insertions(+), 165 deletions(-) diff --git a/src/hotspot/share/cds/aotClassLinker.cpp b/src/hotspot/share/cds/aotClassLinker.cpp index c8ab98df992..db594400420 100644 --- a/src/hotspot/share/cds/aotClassLinker.cpp +++ b/src/hotspot/share/cds/aotClassLinker.cpp @@ -125,15 +125,7 @@ void AOTClassLinker::add_new_candidate(InstanceKlass* ik) { Symbol* loader_id; loader_id = ik->cl_aot_identity(); if (loader_id != nullptr) { - ClassList** class_list_ptr = _custom_loader_prelinked_table->get(loader_id); - ClassList* class_list = nullptr; - if (class_list_ptr != nullptr) { - class_list = *class_list_ptr; - } else { - class_list = new ClassList(1000); - _custom_loader_prelinked_table->put(loader_id, class_list); - } - class_list->append(ik); + _custom_loader_prelinked_table->add_class(loader_id, ik); } if (log_is_enabled(Info, aot, link)) { ResourceMark rm; @@ -212,7 +204,7 @@ void AOTClassLinker::add_candidates() { } } -CustomLoaderClassTable* AOTClassLinker::get_archived_prelinked_table(Symbol* aot_id) { +ArchivedCustomLoaderClassTable* AOTClassLinker::get_archived_prelinked_table(Symbol* aot_id) { return _archived_custom_loader_prelinked_classes_map.get_class_list(aot_id); } diff --git a/src/hotspot/share/cds/aotClassLinker.hpp b/src/hotspot/share/cds/aotClassLinker.hpp index f3bd5aed852..0bd9b5a35dc 100644 --- a/src/hotspot/share/cds/aotClassLinker.hpp +++ b/src/hotspot/share/cds/aotClassLinker.hpp @@ -35,31 +35,12 @@ #include "utilities/macros.hpp" class AOTLinkedClassTable; -class CustomLoaderClassTable; +class ArchivedCustomLoaderClassTable; class InstanceKlass; class SerializeClosure; template class Array; enum class AOTLinkedClassCategory : int; -class AOTLinkedClassTableForCustomLoader { -private: - Symbol* _loader_id; - Array* _prelinked_classes; - bool _loaded; -public: - void init(Symbol* aot_id, Array* class_list) { - _loader_id = aot_id; - _prelinked_classes = class_list; - _loaded = false; - } - Symbol* loader_id() const { return _loader_id; } - address* loader_id_addr() const { return (address*)&_loader_id; } - Array* class_list() const { return _prelinked_classes; } - address* class_list_addr() const { return (address*)&_prelinked_classes; } - void set_loaded(bool value) { _loaded = value; } - bool is_loaded() const { return _loaded; } -}; - // AOTClassLinker is used during the AOTCache Assembly Phase. // It links eligible classes before they are written into the AOTCache // @@ -133,7 +114,7 @@ class AOTClassLinker : AllStatic { static void all_symbols_do(MetaspaceClosure* it); static void serialize_prelinked_classes_map_header(SerializeClosure* soc); - static CustomLoaderClassTable* get_archived_prelinked_table(Symbol* aot_id); + static ArchivedCustomLoaderClassTable* get_archived_prelinked_table(Symbol* aot_id); // Used in logging: "boot1", "boot2", "plat", "app" and "unreg"; static const char* class_category_name(AOTLinkedClassCategory category); diff --git a/src/hotspot/share/cds/aotLinkedClassBulkLoader.cpp b/src/hotspot/share/cds/aotLinkedClassBulkLoader.cpp index 21ef12b868c..9e0f9c1c2ee 100644 --- a/src/hotspot/share/cds/aotLinkedClassBulkLoader.cpp +++ b/src/hotspot/share/cds/aotLinkedClassBulkLoader.cpp @@ -111,7 +111,7 @@ void AOTLinkedClassBulkLoader::preload_classes_for_loader_impl(Handle loader_obj if (aot_id == nullptr) { return; } - CustomLoaderClassTable* table = AOTClassLinker::get_archived_prelinked_table(aot_id); + ArchivedCustomLoaderClassTable* table = AOTClassLinker::get_archived_prelinked_table(aot_id); preload_classes_in_table(table->class_list(), aot_id->as_C_string(), loader_obj, CHECK); MonitorLocker mu1(SystemDictionary_lock); mark_initiating_loader(THREAD, loader_obj()); @@ -248,7 +248,7 @@ void AOTLinkedClassBulkLoader::link_classes_for_loader_impl(Handle loader_obj, T if (aot_id == nullptr) { return; } - CustomLoaderClassTable* table = AOTClassLinker::get_archived_prelinked_table(aot_id); + ArchivedCustomLoaderClassTable* table = AOTClassLinker::get_archived_prelinked_table(aot_id); link_classes_in_table(table->class_list(), CHECK); } diff --git a/src/hotspot/share/cds/customLoaderSupport.hpp b/src/hotspot/share/cds/customLoaderSupport.hpp index 2c0fa384e95..db37af55b86 100644 --- a/src/hotspot/share/cds/customLoaderSupport.hpp +++ b/src/hotspot/share/cds/customLoaderSupport.hpp @@ -7,7 +7,7 @@ #include "utilities/growableArray.hpp" #include "utilities/resizableHashTable.hpp" -class CustomLoaderClassTable { +class ArchivedCustomLoaderClassTable { private: Symbol* _loader_id; Array* _class_list; @@ -20,16 +20,21 @@ class CustomLoaderClassTable { address* loader_id_addr() const { return (address*)&_loader_id; } Array* class_list() const { return _class_list; } address* class_list_addr() const { return (address*)&_class_list; } + + void mark_pointers() { + ArchivePtrMarker::mark_pointer(loader_id_addr()); + ArchivePtrMarker::mark_pointer(class_list_addr()); + } }; -inline bool custom_loader_class_list_equals(CustomLoaderClassTable* table, Symbol* loader_id, int len_unused) { +inline bool custom_loader_class_list_equals(ArchivedCustomLoaderClassTable* table, Symbol* loader_id, int len_unused) { return table->loader_id()->equals(loader_id); } -class ArchivedCustomLoaderClassTableMap : public OffsetCompactHashtable +class ArchivedCustomLoaderClassTableMap : public OffsetCompactHashtable { public: - CustomLoaderClassTable* get_class_list(Symbol* aot_id) { + ArchivedCustomLoaderClassTable* get_class_list(Symbol* aot_id) { unsigned int hash = Symbol::symbol_hash(aot_id); return lookup(aot_id, hash, 0 /* ignored */); } @@ -47,16 +52,15 @@ class ClassLoaderIdToClassTableMap : public ResizeableHashTablehas_been_archived(loader_id), "must be"); - Symbol* buffered_sym = _builder->get_buffered_addr(loader_id); - tableForLoader->init(buffered_sym, ArchiveUtils::archive_array(table)); - ArchivePtrMarker::mark_pointer(tableForLoader->loader_id_addr()); - ArchivePtrMarker::mark_pointer(tableForLoader->class_list_addr()); + Symbol* buffered_loader_id = _builder->get_buffered_addr(loader_id); + tableForLoader->init(buffered_loader_id, ArchiveUtils::archive_array(table)); + tableForLoader->mark_pointers(); unsigned int hash = Symbol::symbol_hash(loader_id); u4 delta = _builder->buffer_to_offset_u4((address)tableForLoader); _writer->add(hash, delta); @@ -67,6 +71,19 @@ class ClassLoaderIdToClassTableMap : public ResizeableHashTableappend(ik); + } + void write_to_archive(ArchivedCustomLoaderClassTableMap* archived_map, const char* map_name) { CompactHashtableStats stats; CompactHashtableWriter writer(number_of_entries(), &stats); diff --git a/src/hotspot/share/cds/finalImageRecipes.cpp b/src/hotspot/share/cds/finalImageRecipes.cpp index 3d51c33ae4d..d2330ea31d8 100644 --- a/src/hotspot/share/cds/finalImageRecipes.cpp +++ b/src/hotspot/share/cds/finalImageRecipes.cpp @@ -43,15 +43,14 @@ #include "runtime/mutexLocker.hpp" #include "utilities/resizableHashTable.hpp" +static const unsigned INITIAL_TABLE_SIZE = 997; // prime number +static const unsigned MAX_TABLE_SIZE = 10000; + GrowableArray* FinalImageRecipes::_tmp_reflect_klasses = nullptr; GrowableArray* FinalImageRecipes::_tmp_reflect_flags = nullptr; GrowableArray* FinalImageRecipes::_tmp_dynamic_proxy_classes = nullptr; static FinalImageRecipes* _final_image_recipes = nullptr; -void* FinalImageRecipes::operator new(size_t size) throw() { - return ArchiveBuilder::current()->ro_region_alloc(size); -} - static void mark_pointers_in_array(Array* array) { if (array == nullptr) { return; @@ -62,6 +61,102 @@ static void mark_pointers_in_array(Array* array) { } } +class ArchivedCustomLoaderClassRecipesTable { +private: + Symbol* _loader_id; + Array* _class_recipes_list; + + address* loader_id_addr() const { return (address*)&_loader_id; } + address* class_recipes_list_addr() const { return (address*)&_class_recipes_list; } + +public: + void init(Symbol* aot_id, Array* class_recipes_list) { + _loader_id = aot_id; + _class_recipes_list = class_recipes_list; + } + Symbol* loader_id() const { return _loader_id; } + Array* class_recipes_list() const { return _class_recipes_list; } + + void mark_pointers() { + ArchivePtrMarker::mark_pointer(loader_id_addr()); + ArchivePtrMarker::mark_pointer(class_recipes_list_addr()); + mark_pointers_in_array(_class_recipes_list); + } +}; + +inline bool custom_loader_class_recipes_equals(ArchivedCustomLoaderClassRecipesTable* table, Symbol* loader_id, int len_unused) { + return table->loader_id()->equals(loader_id); +} + +class ArchivedCustomLoaderClassRecipesTableMap : public OffsetCompactHashtable +{ +public: + ArchivedCustomLoaderClassRecipesTable* get_class_recipe_list(Symbol* aot_id) { + unsigned int hash = Symbol::symbol_hash(aot_id); + return lookup(aot_id, hash, 0 /* ignored */); + } +}; + +typedef GrowableArrayCHeap ClassRecipeList; + +class ClassLoaderIdToClassRecipesTableMap : public ResizeableHashTable +{ + using ResizeableHashTableBase = ResizeableHashTable; +private: + class CopyClassRecipeTableToArchive : StackObj { + private: + CompactHashtableWriter* _writer; + ArchiveBuilder* _builder; + public: + CopyClassRecipeTableToArchive(CompactHashtableWriter* writer) : _writer(writer), + _builder(ArchiveBuilder::current()) + {} + + bool do_entry(Symbol* loader_id, ClassRecipeList* table) { + ArchivedCustomLoaderClassRecipesTable* tableForLoader = (ArchivedCustomLoaderClassRecipesTable*)ArchiveBuilder::ro_region_alloc(sizeof(ArchivedCustomLoaderClassRecipesTable)); + assert(_builder->has_been_archived(loader_id), "must be"); + Symbol* buffered_loader_id = _builder->get_buffered_addr(loader_id); + tableForLoader->init(buffered_loader_id, ArchiveUtils::archive_array(table)); + tableForLoader->mark_pointers(); + unsigned int hash = Symbol::symbol_hash(loader_id); + u4 delta = _builder->buffer_to_offset_u4((address)tableForLoader); + _writer->add(hash, delta); + return true; + } + }; + +public: + ClassLoaderIdToClassRecipesTableMap(unsigned size, unsigned max_size) : ResizeableHashTableBase(size, max_size) {} + + void add_class_recipe(Symbol* loader_id, InstanceKlassRecipe* ikr) { + assert(loader_id != nullptr, "sanity check"); + ClassRecipeList** class_recipe_list_ptr = get(loader_id); + ClassRecipeList* class_recipe_list = nullptr; + if (class_recipe_list_ptr != nullptr) { + class_recipe_list = *class_recipe_list_ptr; + } else { + class_recipe_list = new ClassRecipeList(1000); + put(loader_id, class_recipe_list); + } + class_recipe_list->append(*ikr); + } + + void write_to_archive(ArchivedCustomLoaderClassRecipesTableMap* archived_map, const char* map_name) { + CompactHashtableStats stats; + CompactHashtableWriter writer(number_of_entries(), &stats); + CopyClassRecipeTableToArchive archiver(&writer); + iterate(&archiver); + writer.dump(archived_map, map_name); + } +}; + +ClassLoaderIdToClassRecipesTableMap* _aot_safe_loader_classes_map; +ArchivedCustomLoaderClassRecipesTableMap _archived_aot_safe_loader_classes_map; + +void* FinalImageRecipes::operator new(size_t size) throw() { + return ArchiveBuilder::current()->ro_region_alloc(size); +} + void FinalImageRecipeTable::mark_pointers() { ArchivePtrMarker::mark_pointer(&_boot1); mark_pointers_in_array(_boot1); @@ -71,8 +166,8 @@ void FinalImageRecipeTable::mark_pointers() { mark_pointers_in_array(_platform); ArchivePtrMarker::mark_pointer(&_app); mark_pointers_in_array(_app); - ArchivePtrMarker::mark_pointer(&_custom_loader_classes); - mark_pointers_in_array(_custom_loader_classes); + ArchivePtrMarker::mark_pointer(&_aot_unsafe_custom_loader_classes); + mark_pointers_in_array(_aot_unsafe_custom_loader_classes); } void FinalImageRecipes::record_all_classes() { @@ -82,10 +177,45 @@ void FinalImageRecipes::record_all_classes() { _class_table->set_boot2(write_classes(nullptr, false, true)); _class_table->set_platform(write_classes(SystemDictionary::java_platform_loader(), false, true)); _class_table->set_app(write_classes(SystemDictionary::java_system_loader(), false, true)); - _class_table->set_custom_loader_classes(write_classes(nullptr, false, false)); + _class_table->set_aot_unsafe_custom_loader_classes(write_classes(nullptr, false, false)); + record_aot_safe_custom_loader_classes(); _class_table->mark_pointers(); } +void FinalImageRecipes::record_aot_safe_custom_loader_classes() { + ResourceMark rm; + GrowableArray* all_classes = ArchiveBuilder::current()->klasses(); + _aot_safe_loader_classes_map = new (mtClass) ClassLoaderIdToClassRecipesTableMap(INITIAL_TABLE_SIZE, MAX_TABLE_SIZE); + for (int i = 0; i < all_classes->length(); i++) { + Klass* k = all_classes->at(i); + if (k->is_instance_klass()) { + InstanceKlass* ik = InstanceKlass::cast(k); + if (SystemDictionaryShared::is_builtin(ik) || !ik->is_defined_by_aot_safe_custom_loader()) { + continue; + } + + int flags = 0; + Array* cp_recipe = record_recipe_for_constantpool(ik, flags); + InstanceKlassRecipe ikr(ArchiveBuilder::current()->get_buffered_addr(ik), cp_recipe, flags); + + Symbol* loader_id = ik->cl_aot_identity(); + assert(loader_id != nullptr, "must be"); + _aot_safe_loader_classes_map->add_class_recipe(loader_id, &ikr); + } + } + if (log_is_enabled(Info, aot, load)) { + _aot_safe_loader_classes_map->iterate_all([&](Symbol* loader_id, ClassRecipeList* table) { + ResourceMark rm; + for (int i = 0; i < table->length(); i++) { + InstanceKlassRecipe* ikr = table->adr_at(i); + InstanceKlass* ik = ikr->instance_klass(); + log_info(aot, load)("category %s[%d] %s", loader_id->as_C_string(), i, ik->external_name()); + } + }); + } + _aot_safe_loader_classes_map->write_to_archive(&_archived_aot_safe_loader_classes_map, "archived custom loader classes map"); +} + Array* FinalImageRecipes::write_classes(oop class_loader, bool is_javabase, bool is_builtin_loader) { ResourceMark rm; GrowableArray list; @@ -104,7 +234,7 @@ Array* FinalImageRecipes::write_classes(oop class_loader, b } } else { // skip builtin loader classes when writing custom loader classes - if (SystemDictionaryShared::is_builtin(ik)) { + if (SystemDictionaryShared::is_builtin(ik) || ik->is_defined_by_aot_safe_custom_loader()) { continue; } } @@ -112,6 +242,8 @@ Array* FinalImageRecipes::write_classes(oop class_loader, b Array* cp_recipe = record_recipe_for_constantpool(ik, flags); InstanceKlassRecipe recipe(ArchiveBuilder::current()->get_buffered_addr(ik), cp_recipe, flags); list.append(recipe); + const char* category = AOTClassLinker::class_category_name(ik); + log_info(aot, load)("category %s[%d] %s", category, list.length()-1, ik->external_name()); } } @@ -119,7 +251,7 @@ Array* FinalImageRecipes::write_classes(oop class_loader, b return nullptr; } else { const char* category = AOTClassLinker::class_category_name(list.adr_at(0)->instance_klass()); - log_info(aot, link)("wrote %d class(es) for category %s", list.length(), category); + log_info(aot, link)("recorded %d class(es) for category %s", list.length(), category); return ArchiveUtils::archive_array(&list); } } @@ -201,115 +333,39 @@ Array* FinalImageRecipes::record_recipe_for_constantpool(InstanceKlass* ik, } } -/* -void FinalImageRecipes::record_recipes_for_constantpool() { - ResourceMark rm; - - // The recipes are recorded regardless of CDSConfig::is_dumping_{invokedynamic,dynamic_proxies,reflection_data}(). - // If some of these options are not enabled, the corresponding recipes will be - // ignored during the final image assembly. - - GrowableArray*> tmp_cp_recipes; - GrowableArray tmp_flags; - - GrowableArray* klasses = ArchiveBuilder::current()->klasses(); - for (int i = 0; i < klasses->length(); i++) { - GrowableArray cp_indices; - int flags = 0; - - Klass* k = klasses->at(i); - if (k->is_instance_klass()) { - InstanceKlass* ik = InstanceKlass::cast(k); +void FinalImageRecipes::apply_cp_recipes_for_class(JavaThread* current, InstanceKlassRecipe* ikr) { + InstanceKlass* ik = ikr->instance_klass(); + Array* cp_indices = ikr->cp_recipe(); + int flags = ikr->flags(); + if (cp_indices != nullptr) { + if (!strcmp(ik->external_name(), "org.openjdk.aot.testclass.Foo")) { + log_info(cds)("Applying constant pool recipes for %s", ik->external_name()); + } + if (ik->is_loaded()) { + ResourceMark rm(current); ConstantPool* cp = ik->constants(); - ConstantPoolCache* cp_cache = cp->cache(); - - if (ik->is_initialized()) { - flags |= WAS_INITED; + GrowableArray preresolve_list(cp->length(), cp->length(), false); + for (int j = 0; j < cp_indices->length(); j++) { + preresolve_list.at_put(cp_indices->at(j), true); } - - for (int cp_index = 1; cp_index < cp->length(); cp_index++) { // Index 0 is unused - if (cp->tag_at(cp_index).value() == JVM_CONSTANT_Class) { - Klass* k = cp->resolved_klass_at(cp_index); - if (k->is_instance_klass()) { - cp_indices.append(cp_index); - flags |= CP_RESOLVE_CLASS; - } - } + if ((flags & CP_RESOLVE_CLASS) != 0) { + AOTConstantPoolResolver::preresolve_class_cp_entries(current, ik, &preresolve_list); } - - if (cp_cache != nullptr) { - Array* field_entries = cp_cache->resolved_field_entries(); - if (field_entries != nullptr) { - for (int i = 0; i < field_entries->length(); i++) { - ResolvedFieldEntry* rfe = field_entries->adr_at(i); - if (rfe->is_resolved(Bytecodes::_getstatic) || - rfe->is_resolved(Bytecodes::_putstatic) || - rfe->is_resolved(Bytecodes::_getfield) || - rfe->is_resolved(Bytecodes::_putfield)) { - cp_indices.append(rfe->constant_pool_index()); - flags |= CP_RESOLVE_FIELD_AND_METHOD; - } - } - } - - Array* method_entries = cp_cache->resolved_method_entries(); - if (method_entries != nullptr) { - for (int i = 0; i < method_entries->length(); i++) { - ResolvedMethodEntry* rme = method_entries->adr_at(i); - if (rme->is_resolved(Bytecodes::_invokevirtual) || - rme->is_resolved(Bytecodes::_invokespecial) || - rme->is_resolved(Bytecodes::_invokeinterface) || - rme->is_resolved(Bytecodes::_invokestatic) || - rme->is_resolved(Bytecodes::_invokehandle)) { - cp_indices.append(rme->constant_pool_index()); - flags |= CP_RESOLVE_FIELD_AND_METHOD; - } - } - } - - Array* indy_entries = cp_cache->resolved_indy_entries(); - if (indy_entries != nullptr) { - for (int i = 0; i < indy_entries->length(); i++) { - ResolvedIndyEntry* rie = indy_entries->adr_at(i); - int cp_index = rie->constant_pool_index(); - if (rie->is_resolved()) { - cp_indices.append(cp_index); - flags |= CP_RESOLVE_INDY; - } - } - } + if ((flags & CP_RESOLVE_FIELD_AND_METHOD) != 0) { + AOTConstantPoolResolver::preresolve_field_and_method_cp_entries(current, ik, &preresolve_list); } - } - - if (cp_indices.length() > 0) { - LogStreamHandle(Trace, aot, resolve) log; - if (log.is_enabled()) { - log.print("ConstantPool entries for %s to be pre-resolved:", k->external_name()); - for (int i = 0; i < cp_indices.length(); i++) { - log.print(" %d", cp_indices.at(i)); - } - log.print("\n"); + if ((flags & CP_RESOLVE_INDY) != 0) { + AOTConstantPoolResolver::preresolve_indy_cp_entries(current, ik, &preresolve_list); } - tmp_cp_recipes.append(ArchiveUtils::archive_array(&cp_indices)); - } else { - tmp_cp_recipes.append(nullptr); } - tmp_flags.append(flags); } - - _cp_recipes = ArchiveUtils::archive_array(&tmp_cp_recipes); - ArchivePtrMarker::mark_pointer(&_cp_recipes); - - _flags = ArchiveUtils::archive_array(&tmp_flags); - ArchivePtrMarker::mark_pointer(&_flags); } -*/ void FinalImageRecipes::apply_recipes_for_constantpool(JavaThread* current) { assert(CDSConfig::is_dumping_final_static_archive(), "must be"); - //for (int i = 0; i < _all_klasses->length(); i++) { - _class_table->iterate_all([&](InstanceKlassRecipe* ikr) { + _class_table->iterate_builtin_classes([&](InstanceKlassRecipe* ikr) { + apply_cp_recipes_for_class(current, ikr); InstanceKlass* ik = ikr->instance_klass(); Array* cp_indices = ikr->cp_recipe(); int flags = ikr->flags(); @@ -336,6 +392,13 @@ void FinalImageRecipes::apply_recipes_for_constantpool(JavaThread* current) { } } }); + + _archived_aot_safe_loader_classes_map.iterate_all([&](ArchivedCustomLoaderClassRecipesTable* cl_table) { + Array* recipes_list = cl_table->class_recipes_list(); + for (int i = 0; i < recipes_list->length(); i++) { + apply_cp_recipes_for_class(current, recipes_list->adr_at(i)); + } + }); } void FinalImageRecipes::record_recipes_for_reflection_data() { @@ -423,7 +486,26 @@ void FinalImageRecipes::load_builtin_loader_classes(TRAPS) { load_classes_in_table(_class_table->app(), "app", h_system_loader, CHECK); } -void FinalImageRecipes::load_custom_loader_classes(TRAPS) { +void FinalImageRecipes::load_aot_safe_custom_loader_classes(TRAPS) { + UnregisteredClasses::initialize(CHECK); + _archived_aot_safe_loader_classes_map.iterate_all([&](ArchivedCustomLoaderClassRecipesTable* cl_table) { + Handle unreg_class_loader = UnregisteredClasses::create_unregistered_loader(THREAD); + SystemDictionary::register_loader(unreg_class_loader); + assert(unreg_class_loader.not_null(), "must be"); + ResourceMark rm; + char* loader_id_str = cl_table->loader_id()->as_C_string(); + + initiate_loading(THREAD, loader_id_str, unreg_class_loader, _class_table->boot1()); + initiate_loading(THREAD, loader_id_str, unreg_class_loader, _class_table->boot2()); + initiate_loading(THREAD, loader_id_str, unreg_class_loader, _class_table->platform()); + initiate_loading(THREAD, loader_id_str, unreg_class_loader, _class_table->app()); + + Array* recipes = cl_table->class_recipes_list(); + load_classes_in_table(recipes, loader_id_str, unreg_class_loader, CHECK); + }); +} + +void FinalImageRecipes::load_aot_unsafe_custom_loader_classes(TRAPS) { precond(CDSConfig::is_dumping_aot_linked_classes()); // Use UnregisteredClassLoader to load these classes UnregisteredClasses::initialize(CHECK); @@ -431,11 +513,12 @@ void FinalImageRecipes::load_custom_loader_classes(TRAPS) { SystemDictionary::register_loader(unreg_class_loader); assert(unreg_class_loader.not_null(), "must be"); - initiate_loading(THREAD, "app", unreg_class_loader, _class_table->boot1()); - initiate_loading(THREAD, "app", unreg_class_loader, _class_table->boot2()); - initiate_loading(THREAD, "app", unreg_class_loader, _class_table->platform()); - initiate_loading(THREAD, "app", unreg_class_loader, _class_table->app()); - load_classes_in_table(_class_table->custom_loader_classes(), "custom-loader", unreg_class_loader, CHECK); + initiate_loading(THREAD, "unreg", unreg_class_loader, _class_table->boot1()); + initiate_loading(THREAD, "unreg", unreg_class_loader, _class_table->boot2()); + initiate_loading(THREAD, "unreg", unreg_class_loader, _class_table->platform()); + initiate_loading(THREAD, "unreg", unreg_class_loader, _class_table->app()); + + load_classes_in_table(_class_table->aot_unsafe_custom_loader_classes(), "unreg", unreg_class_loader, CHECK); } void FinalImageRecipes::load_classes_in_table(Array* recipes, @@ -478,9 +561,11 @@ void FinalImageRecipes::initiate_loading(JavaThread* current, const char* catego return; } +#if 0 assert(initiating_loader() == SystemDictionary::java_platform_loader() || initiating_loader() == SystemDictionary::java_system_loader() || initiating_loader() == UnregisteredClasses::unregistered_class_loader(current)(), "must be"); +#endif ClassLoaderData* loader_data = ClassLoaderData::class_loader_data(initiating_loader()); MonitorLocker mu1(SystemDictionary_lock); @@ -534,7 +619,10 @@ void FinalImageRecipes::link_classes_impl(TRAPS) { link_classes_in_table(_class_table->boot2(), CHECK); link_classes_in_table(_class_table->platform(), CHECK); link_classes_in_table(_class_table->app(), CHECK); - link_classes_in_table(_class_table->custom_loader_classes(), CHECK); + link_classes_in_table(_class_table->aot_unsafe_custom_loader_classes(), CHECK); + _archived_aot_safe_loader_classes_map.iterate_all([&](ArchivedCustomLoaderClassRecipesTable* cl_table) { + link_classes_in_table(cl_table->class_recipes_list(), CHECK); + }); } void FinalImageRecipes::link_classes_in_table(Array* recipes, TRAPS) { @@ -558,7 +646,8 @@ void FinalImageRecipes::load_and_link_all_classes(TRAPS) { /* Built-in loader classes come first */ load_builtin_loader_classes(CHECK); /* Now load custom loader classes */ - load_custom_loader_classes(CHECK); + load_aot_safe_custom_loader_classes(CHECK); + load_aot_unsafe_custom_loader_classes(CHECK); link_classes(THREAD); } @@ -740,5 +829,8 @@ void FinalImageRecipes::apply_recipes_impl(TRAPS) { } void FinalImageRecipes::serialize(SerializeClosure* soc) { + if (CDSConfig::is_dumping_preimage_static_archive() || (CDSConfig::is_dumping_final_static_archive() && soc->reading())) { + _archived_aot_safe_loader_classes_map.serialize_header(soc); + } soc->do_ptr((void**)&_final_image_recipes); } diff --git a/src/hotspot/share/cds/finalImageRecipes.hpp b/src/hotspot/share/cds/finalImageRecipes.hpp index 0703bc4b862..cffcb470fb4 100644 --- a/src/hotspot/share/cds/finalImageRecipes.hpp +++ b/src/hotspot/share/cds/finalImageRecipes.hpp @@ -62,7 +62,7 @@ class FinalImageRecipeTable { Array* _platform; Array* _app; - Array* _custom_loader_classes; + Array* _aot_unsafe_custom_loader_classes; template void iterate_array(Function fn, Array* array) { @@ -77,27 +77,31 @@ class FinalImageRecipeTable { FinalImageRecipeTable() : _boot1(nullptr), _boot2(nullptr), _platform(nullptr), _app(nullptr), - _custom_loader_classes(nullptr) {} + _aot_unsafe_custom_loader_classes(nullptr) {} Array* boot1() const { return _boot1; } Array* boot2() const { return _boot2; } Array* platform() const { return _platform; } Array* app() const { return _app; } - Array* custom_loader_classes() const { return _custom_loader_classes; } + Array* aot_unsafe_custom_loader_classes() const { return _aot_unsafe_custom_loader_classes; } 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; } - void set_custom_loader_classes(Array* value) { _custom_loader_classes = value; } + void set_aot_unsafe_custom_loader_classes(Array* value) { _aot_unsafe_custom_loader_classes = value; } template - void iterate_all(Function fn) { + void iterate_builtin_classes(Function fn) { iterate_array(fn, _boot1); iterate_array(fn, _boot2); iterate_array(fn, _platform); iterate_array(fn, _app); - iterate_array(fn, _custom_loader_classes); + } + template + void iterate_all_classes(Function fn) { + iterate_builtin_classes(fn); + iterate_array(fn, _aot_unsafe_custom_loader_classes); } void mark_pointers(); }; @@ -158,6 +162,7 @@ class FinalImageRecipes { // Called when dumping preimage void record_all_classes(); + void record_aot_safe_custom_loader_classes(); Array* record_recipe_for_constantpool(InstanceKlass* ik, int& flags); //void record_recipes_for_constantpool(); void record_recipes_for_reflection_data(); @@ -165,7 +170,8 @@ class FinalImageRecipes { // Called when dumping final image void load_builtin_loader_classes(TRAPS); - void load_custom_loader_classes(TRAPS); + void load_aot_safe_custom_loader_classes(TRAPS); + void load_aot_unsafe_custom_loader_classes(TRAPS); void load_classes_in_table(Array* classes, const char* category_name, Handle loader, TRAPS); void initiate_loading(JavaThread* current, const char* category_name, Handle initiating_loader, Array* classes); void link_classes(JavaThread* current); @@ -175,6 +181,7 @@ class FinalImageRecipes { void apply_recipes_impl(TRAPS); void load_and_link_all_classes(TRAPS); //void load_all_classes(TRAPS); + void apply_cp_recipes_for_class(JavaThread* current, InstanceKlassRecipe* ikr); void apply_recipes_for_constantpool(JavaThread* current); void apply_recipes_for_reflection_data(JavaThread* current); void apply_recipes_for_dynamic_proxies(TRAPS); diff --git a/src/hotspot/share/cds/unregisteredClasses.cpp b/src/hotspot/share/cds/unregisteredClasses.cpp index 9de0abfd214..08d6392f321 100644 --- a/src/hotspot/share/cds/unregisteredClasses.cpp +++ b/src/hotspot/share/cds/unregisteredClasses.cpp @@ -38,6 +38,7 @@ static InstanceKlass* _UnregisteredClassLoader_klass; static InstanceKlass* _UnregisteredClassLoader_Source_klass; static OopHandle _unregistered_class_loader; +static GrowableArray* _unregistered_class_loader_list; void UnregisteredClasses::initialize(TRAPS) { if (_UnregisteredClassLoader_klass != nullptr) { @@ -61,6 +62,16 @@ void UnregisteredClasses::initialize(TRAPS) { const Handle cl = JavaCalls::construct_new_instance(_UnregisteredClassLoader_klass, vmSymbols::void_method_signature(), CHECK); _unregistered_class_loader = OopHandle(Universe::vm_global(), cl()); + _unregistered_class_loader_list = new (mtClassShared)GrowableArray(10, mtClassShared); +} + +Handle UnregisteredClasses::create_unregistered_loader(TRAPS) { + assert(_UnregisteredClassLoader_klass != nullptr, "missing call to UnregisteredClasses::initialize"); + const Handle cl = JavaCalls::construct_new_instance(_UnregisteredClassLoader_klass, + vmSymbols::void_method_signature(), CHECK_NH); + OopHandle cl_handle = OopHandle(Universe::vm_global(), cl()); + _unregistered_class_loader_list->append(cl_handle); + return cl; } Handle UnregisteredClasses::unregistered_class_loader(Thread* current) { diff --git a/src/hotspot/share/cds/unregisteredClasses.hpp b/src/hotspot/share/cds/unregisteredClasses.hpp index 28b3af400c0..3b39670159b 100644 --- a/src/hotspot/share/cds/unregisteredClasses.hpp +++ b/src/hotspot/share/cds/unregisteredClasses.hpp @@ -35,6 +35,7 @@ class UnregisteredClasses: AllStatic { public: static InstanceKlass* load_class(Symbol* name, const char* path, TRAPS); static void initialize(TRAPS); + static Handle create_unregistered_loader(TRAPS); // Returns true if the class is loaded internally for dumping unregistered classes. static bool check_for_exclusion(const InstanceKlass* k); static Handle unregistered_class_loader(Thread* current); diff --git a/src/hotspot/share/classfile/classLoaderData.cpp b/src/hotspot/share/classfile/classLoaderData.cpp index 46873d2adfa..8c67bfaa069 100644 --- a/src/hotspot/share/classfile/classLoaderData.cpp +++ b/src/hotspot/share/classfile/classLoaderData.cpp @@ -1133,3 +1133,9 @@ bool ClassLoaderData::contains_klass(Klass* klass) { } return false; } + +Symbol* ClassLoaderData::parent_aot_identity() const { + oop parent = java_lang_ClassLoader::parent(class_loader()); + ClassLoaderData* parent_cld = java_lang_ClassLoader::loader_data(parent); + return parent_cld->aot_identity(); +} diff --git a/src/hotspot/share/classfile/classLoaderData.hpp b/src/hotspot/share/classfile/classLoaderData.hpp index 1e0a595552c..e3d0de8132f 100644 --- a/src/hotspot/share/classfile/classLoaderData.hpp +++ b/src/hotspot/share/classfile/classLoaderData.hpp @@ -360,6 +360,7 @@ class ClassLoaderData : public CHeapObj { const char* loader_name_and_id() const; Symbol* name_and_id() const { return _name_and_id; } Symbol* aot_identity() const { return _aot_identity; } + Symbol* parent_aot_identity() const; unsigned identity_hash() const { return (unsigned)((uintptr_t)this >> LogBytesPerWord); diff --git a/src/hotspot/share/classfile/systemDictionaryShared.cpp b/src/hotspot/share/classfile/systemDictionaryShared.cpp index dba6d0dd244..87585b05720 100644 --- a/src/hotspot/share/classfile/systemDictionaryShared.cpp +++ b/src/hotspot/share/classfile/systemDictionaryShared.cpp @@ -958,7 +958,7 @@ bool SystemDictionaryShared::should_be_excluded(Klass* k) { void SystemDictionaryShared::finish_exclusion_checks() { assert_at_safepoint(); - if (CDSConfig::is_dumping_dynamic_archive() || CDSConfig::is_dumping_preimage_static_archive()) { + if (CDSConfig::is_dumping_dynamic_archive() || (CDSConfig::is_dumping_preimage_static_archive() && !CDSConfig::supports_custom_loaders())) { // Do this first -- if a base class is excluded due to duplication, // all of its subclasses will also be excluded. ResourceMark rm; From db91bc8e10beaaf2eca1c6c79708105a19c8ab00 Mon Sep 17 00:00:00 2001 From: Ashutosh Mehra Date: Wed, 25 Feb 2026 16:56:08 -0500 Subject: [PATCH 23/30] Handle multipe instances of URLClassLoader with same classpath Signed-off-by: Ashutosh Mehra --- src/hotspot/share/cds/aotClassLocation.cpp | 7 ++- src/hotspot/share/cds/aotClassLocation.hpp | 2 +- src/hotspot/share/cds/finalImageRecipes.cpp | 55 ------------------- src/hotspot/share/cds/finalImageRecipes.hpp | 1 - .../classfile/systemDictionaryShared.cpp | 4 +- src/hotspot/share/prims/jvm.cpp | 7 ++- .../classes/java/net/URLClassLoader.java | 13 +++-- 7 files changed, 20 insertions(+), 69 deletions(-) diff --git a/src/hotspot/share/cds/aotClassLocation.cpp b/src/hotspot/share/cds/aotClassLocation.cpp index 42f710948c6..a2472846bda 100644 --- a/src/hotspot/share/cds/aotClassLocation.cpp +++ b/src/hotspot/share/cds/aotClassLocation.cpp @@ -1129,12 +1129,12 @@ void URLClassLoaderClasspathSupport::reload_runtime_map() { }); } -void URLClassLoaderClasspathSupport::add_urlclassloader_classpath(ClassLoaderData* loader_data, const char* classpath) { +bool URLClassLoaderClasspathSupport::add_urlclassloader_classpath(ClassLoaderData* loader_data, const char* aot_id_str, const char* classpath) { assert(_aot_id_to_classpath != nullptr, "sanity check"); - Symbol* aot_id = loader_data->aot_identity(); + Symbol* aot_id = SymbolTable::new_symbol(aot_id_str); if (_aot_id_to_classpath->contains(aot_id)) { // cannot allow aot_id clash; return without doing anything - return; + return false; } GrowableClassLocationArray* locations = new GrowableClassLocationArray(10); URLClassLoaderClassLocationStream css(classpath); @@ -1145,6 +1145,7 @@ void URLClassLoaderClasspathSupport::add_urlclassloader_classpath(ClassLoaderDat locations->append(cs); } _aot_id_to_classpath->put(aot_id, locations); + return true; } class URLClassLoaderClasspathArchiver : StackObj { diff --git a/src/hotspot/share/cds/aotClassLocation.hpp b/src/hotspot/share/cds/aotClassLocation.hpp index feca7647762..013ed62e390 100644 --- a/src/hotspot/share/cds/aotClassLocation.hpp +++ b/src/hotspot/share/cds/aotClassLocation.hpp @@ -301,7 +301,7 @@ class URLClassLoaderClasspathSupport : AllStatic { public: static void init(); static void reload_runtime_map(); - static void add_urlclassloader_classpath(ClassLoaderData* loader_data, const char* classpath); + static bool add_urlclassloader_classpath(ClassLoaderData* loader_data, const char* aot_id, const char* classpath); static void archive_classpath_map(); static void serialize_classpath_map_table_header(SerializeClosure* soc); static bool verify_archived_classpath(ClassLoaderData* loader_data, const char* classpath); diff --git a/src/hotspot/share/cds/finalImageRecipes.cpp b/src/hotspot/share/cds/finalImageRecipes.cpp index d2330ea31d8..8b0c1bae6b0 100644 --- a/src/hotspot/share/cds/finalImageRecipes.cpp +++ b/src/hotspot/share/cds/finalImageRecipes.cpp @@ -651,61 +651,6 @@ void FinalImageRecipes::load_and_link_all_classes(TRAPS) { link_classes(THREAD); } -#if 0 -void FinalImageRecipes::load_all_classes(TRAPS) { - assert(CDSConfig::is_dumping_final_static_archive(), "sanity"); - UnregisteredClasses::initialize(CHECK); - Handle class_loader(THREAD, SystemDictionary::java_system_loader()); - for (int i = 0; i < _all_klasses->length(); i++) { - Klass* k = _all_klasses->at(i); - int flags = _flags->at(i); - if (k->is_instance_klass()) { - InstanceKlass* ik = InstanceKlass::cast(k); - if (!strcmp(ik->external_name(), "org/openjdk/aot/testclass/Foo")) { - log_info(cds)("FinalImageRecipes loading class %s", ik->external_name()); - } - if (ik->defined_by_other_loaders() && !k->is_defined_by_aot_safe_custom_loader()) { - SystemDictionaryShared::init_dumptime_info(ik); - SystemDictionaryShared::add_unregistered_class(THREAD, ik); - SystemDictionaryShared::copy_unregistered_class_size_and_crc32(ik); - } else if (!ik->is_hidden() && !ik->defined_by_other_loaders()) { - Klass* actual = SystemDictionary::resolve_or_fail(ik->name(), class_loader, true, CHECK); - if (actual != ik) { - ResourceMark rm(THREAD); - log_error(aot)("Unable to resolve class from CDS archive: %s", ik->external_name()); - log_error(aot)("Expected: " INTPTR_FORMAT ", actual: " INTPTR_FORMAT, p2i(ik), p2i(actual)); - log_error(aot)("Please check if your VM command-line is the same as in the training run"); - AOTMetaspace::unrecoverable_writing_error(); - } - assert(ik->is_loaded(), "must be"); - ik->link_class(CHECK); - - if (ik->has_aot_safe_initializer() && (flags & WAS_INITED) != 0) { - assert(ik->class_loader() == nullptr, "supported only for boot classes for now"); - ResourceMark rm(THREAD); - log_info(aot, init)("Initializing %s", ik->external_name()); - ik->initialize(CHECK); - } - } else if (k->is_defined_by_aot_safe_custom_loader()) { - // Use UnregisteredClassLoader to load these classes - Handle unreg_class_loader = UnregisteredClasses::unregistered_class_loader(THREAD); - assert(unreg_class_loader.not_null(), "must be"); - Klass* actual = SystemDictionary::resolve_or_fail(ik->name(), unreg_class_loader, true, CHECK); - if (actual != ik) { - ResourceMark rm(THREAD); - log_error(aot)("Unable to resolve class from CDS archive: %s", ik->external_name()); - log_error(aot)("Expected: " INTPTR_FORMAT ", actual: " INTPTR_FORMAT, p2i(ik), p2i(actual)); - log_error(aot)("Please check if your VM command-line is the same as in the training run"); - AOTMetaspace::unrecoverable_writing_error(); - } - assert(ik->is_loaded(), "must be"); - ik->link_class(CHECK); - } - } - } -} -#endif - void FinalImageRecipes::apply_recipes_for_reflection_data(JavaThread* current) { assert(CDSConfig::is_dumping_final_static_archive(), "must be"); diff --git a/src/hotspot/share/cds/finalImageRecipes.hpp b/src/hotspot/share/cds/finalImageRecipes.hpp index cffcb470fb4..b744b88f5af 100644 --- a/src/hotspot/share/cds/finalImageRecipes.hpp +++ b/src/hotspot/share/cds/finalImageRecipes.hpp @@ -180,7 +180,6 @@ class FinalImageRecipes { void apply_recipes_impl(TRAPS); void load_and_link_all_classes(TRAPS); - //void load_all_classes(TRAPS); void apply_cp_recipes_for_class(JavaThread* current, InstanceKlassRecipe* ikr); void apply_recipes_for_constantpool(JavaThread* current); void apply_recipes_for_reflection_data(JavaThread* current); diff --git a/src/hotspot/share/classfile/systemDictionaryShared.cpp b/src/hotspot/share/classfile/systemDictionaryShared.cpp index 87585b05720..06af5fa97fa 100644 --- a/src/hotspot/share/classfile/systemDictionaryShared.cpp +++ b/src/hotspot/share/classfile/systemDictionaryShared.cpp @@ -825,7 +825,7 @@ class UnregisteredClassesDuplicationChecker : StackObj { UnregisteredClassesDuplicationChecker() : _thread(Thread::current()) {} void do_entry(InstanceKlass* k, DumpTimeClassInfo& info) { - if (!SystemDictionaryShared::is_builtin(k)) { + if (!SystemDictionaryShared::is_builtin(k) && !k->is_defined_by_aot_safe_custom_loader()) { _list.append(k); } } @@ -958,7 +958,7 @@ bool SystemDictionaryShared::should_be_excluded(Klass* k) { void SystemDictionaryShared::finish_exclusion_checks() { assert_at_safepoint(); - if (CDSConfig::is_dumping_dynamic_archive() || (CDSConfig::is_dumping_preimage_static_archive() && !CDSConfig::supports_custom_loaders())) { + if (CDSConfig::is_dumping_dynamic_archive() || CDSConfig::is_dumping_preimage_static_archive()) { // Do this first -- if a base class is excluded due to duplication, // all of its subclasses will also be excluded. ResourceMark rm; diff --git a/src/hotspot/share/prims/jvm.cpp b/src/hotspot/share/prims/jvm.cpp index 4a2c5b21ff3..0365eea9de3 100644 --- a/src/hotspot/share/prims/jvm.cpp +++ b/src/hotspot/share/prims/jvm.cpp @@ -2255,15 +2255,18 @@ JVM_ENTRY_PROF(jboolean, JVM_RegisterAsAOTCompatibleLoader, JVM_RegisterAsAOTCom return JNI_FALSE; JVM_END -JVM_ENTRY_PROF(jboolean, JVM_RegisterURLClassLoaderAsAOTSafeLoader, JVM_RegisterURLClassLoaderAsAOTSafeLoader(JNIEnv *env, jobject loader, jstring classpath)) +JVM_ENTRY_PROF(jboolean, JVM_RegisterURLClassLoaderAsAOTSafeLoader, JVM_RegisterURLClassLoaderAsAOTSafeLoader(JNIEnv *env, jobject loader, jstring aot_id, jstring classpath)) if (CDSConfig::supports_custom_loaders()) { ResourceMark rm(THREAD); Handle h_loader(THREAD, JNIHandles::resolve_non_null(loader)); ClassLoaderData *loader_data = SystemDictionary::register_loader(h_loader); assert(loader_data->aot_identity() != nullptr, "must be"); + const char* aot_id_str = java_lang_String::as_utf8_string(JNIHandles::resolve_non_null(aot_id)); const char *classpath_str = java_lang_String::as_utf8_string(JNIHandles::resolve_non_null(classpath)); if (CDSConfig::is_dumping_preimage_static_archive()) { - URLClassLoaderClasspathSupport::add_urlclassloader_classpath(loader_data, classpath_str); + if (!URLClassLoaderClasspathSupport::add_urlclassloader_classpath(loader_data, classpath_str)) { + return JNI_FALSE; + } } else if (CDSConfig::is_using_aot_linked_classes()) { if (!URLClassLoaderClasspathSupport::verify_archived_classpath(loader_data, classpath_str)) { return JNI_FALSE; diff --git a/src/java.base/share/classes/java/net/URLClassLoader.java b/src/java.base/share/classes/java/net/URLClassLoader.java index 9e3e60999ab..0162aa53b99 100644 --- a/src/java.base/share/classes/java/net/URLClassLoader.java +++ b/src/java.base/share/classes/java/net/URLClassLoader.java @@ -689,12 +689,15 @@ private boolean registerAsAOTSafe(final URL[] urls) { return false; } if (canRegisterAsAOTSafe(urls)) { - setAOTIdentity(convertToAOTId(classpath)); - boolean rc = registerAsAOTSafeImpl(classpath); - if (DEBUG) { - if (rc) { + String aotId = convertToAOTId(classpath); + boolean rc = registerAsAOTSafeImpl(aotId, classpath); + if (rc) { + setAOTIdentity(aotId); + if (DEBUG) { System.out.println("DEBUG: Registered URLClassLoader as AOT-safe with classpath " + classpath); - } else { + } + } else { + if (DEBUG) { System.out.println("DEBUG: Failed to register URLClassLoader as AOT-safe with classpath " + classpath); } } From eab1a7b7beab97dab360c80ef54b221b2d43a032 Mon Sep 17 00:00:00 2001 From: Ashutosh Mehra Date: Wed, 25 Feb 2026 18:39:06 -0500 Subject: [PATCH 24/30] Fix compile failure Signed-off-by: Ashutosh Mehra --- src/hotspot/share/include/jvm.h | 2 +- src/hotspot/share/prims/jvm.cpp | 4 ++-- src/java.base/share/classes/java/net/URLClassLoader.java | 2 +- src/java.base/share/native/libjava/URLClassLoader.c | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/hotspot/share/include/jvm.h b/src/hotspot/share/include/jvm.h index c65aedead97..d49fc94acc9 100644 --- a/src/hotspot/share/include/jvm.h +++ b/src/hotspot/share/include/jvm.h @@ -751,7 +751,7 @@ JNIEXPORT jboolean JNICALL JVM_RegisterAsAOTCompatibleLoader(JNIEnv *env, jobject loader); JNIEXPORT jboolean JNICALL -JVM_RegisterURLClassLoaderAsAOTSafeLoader(JNIEnv *env, jobject loader, jstring classpath); +JVM_RegisterURLClassLoaderAsAOTSafeLoader(JNIEnv *env, jobject loader, jstring aot_id, jstring classpath); /* * java.lang.ref.Finalizer */ diff --git a/src/hotspot/share/prims/jvm.cpp b/src/hotspot/share/prims/jvm.cpp index 0365eea9de3..2dbb1312591 100644 --- a/src/hotspot/share/prims/jvm.cpp +++ b/src/hotspot/share/prims/jvm.cpp @@ -2260,11 +2260,11 @@ JVM_ENTRY_PROF(jboolean, JVM_RegisterURLClassLoaderAsAOTSafeLoader, JVM_Register ResourceMark rm(THREAD); Handle h_loader(THREAD, JNIHandles::resolve_non_null(loader)); ClassLoaderData *loader_data = SystemDictionary::register_loader(h_loader); - assert(loader_data->aot_identity() != nullptr, "must be"); + assert(loader_data->aot_identity() == nullptr, "loader's aot identity should not be set"); const char* aot_id_str = java_lang_String::as_utf8_string(JNIHandles::resolve_non_null(aot_id)); const char *classpath_str = java_lang_String::as_utf8_string(JNIHandles::resolve_non_null(classpath)); if (CDSConfig::is_dumping_preimage_static_archive()) { - if (!URLClassLoaderClasspathSupport::add_urlclassloader_classpath(loader_data, classpath_str)) { + if (!URLClassLoaderClasspathSupport::add_urlclassloader_classpath(loader_data, aot_id_str, classpath_str)) { return JNI_FALSE; } } else if (CDSConfig::is_using_aot_linked_classes()) { diff --git a/src/java.base/share/classes/java/net/URLClassLoader.java b/src/java.base/share/classes/java/net/URLClassLoader.java index 0162aa53b99..89a2339d49a 100644 --- a/src/java.base/share/classes/java/net/URLClassLoader.java +++ b/src/java.base/share/classes/java/net/URLClassLoader.java @@ -710,5 +710,5 @@ private boolean registerAsAOTSafe(final URL[] urls) { return false; } - private native boolean registerAsAOTSafeImpl(String classpath); + private native boolean registerAsAOTSafeImpl(String aotId, String classpath); } diff --git a/src/java.base/share/native/libjava/URLClassLoader.c b/src/java.base/share/native/libjava/URLClassLoader.c index e15cdd976a4..7c8e87928a0 100644 --- a/src/java.base/share/native/libjava/URLClassLoader.c +++ b/src/java.base/share/native/libjava/URLClassLoader.c @@ -4,7 +4,7 @@ #include "jvm.h" JNIEXPORT jboolean JNICALL -Java_java_net_URLClassLoader_registerAsAOTSafeImpl(JNIEnv *env, jobject loader, jstring classpath) +Java_java_net_URLClassLoader_registerAsAOTSafeImpl(JNIEnv *env, jobject loader, jstring aot_id, jstring classpath) { - return JVM_RegisterURLClassLoaderAsAOTSafeLoader(env, loader, classpath); + return JVM_RegisterURLClassLoaderAsAOTSafeLoader(env, loader, aot_id, classpath); } From 61247fc740cf0be61852efa91d118a77eec8a04e Mon Sep 17 00:00:00 2001 From: Ashutosh Mehra Date: Wed, 25 Feb 2026 23:58:31 -0500 Subject: [PATCH 25/30] Fix bug in setting aot identity Signed-off-by: Ashutosh Mehra --- src/hotspot/share/cds/aotClassLocation.cpp | 6 ++---- src/hotspot/share/cds/aotClassLocation.hpp | 4 ++-- src/hotspot/share/classfile/classLoaderData.cpp | 5 ----- src/hotspot/share/classfile/classLoaderData.hpp | 1 + src/hotspot/share/prims/jvm.cpp | 7 +++++-- 5 files changed, 10 insertions(+), 13 deletions(-) diff --git a/src/hotspot/share/cds/aotClassLocation.cpp b/src/hotspot/share/cds/aotClassLocation.cpp index a2472846bda..2c6114667bf 100644 --- a/src/hotspot/share/cds/aotClassLocation.cpp +++ b/src/hotspot/share/cds/aotClassLocation.cpp @@ -1129,9 +1129,8 @@ void URLClassLoaderClasspathSupport::reload_runtime_map() { }); } -bool URLClassLoaderClasspathSupport::add_urlclassloader_classpath(ClassLoaderData* loader_data, const char* aot_id_str, const char* classpath) { +bool URLClassLoaderClasspathSupport::add_urlclassloader_classpath(ClassLoaderData* loader_data, Symbol* aot_id, const char* classpath) { assert(_aot_id_to_classpath != nullptr, "sanity check"); - Symbol* aot_id = SymbolTable::new_symbol(aot_id_str); if (_aot_id_to_classpath->contains(aot_id)) { // cannot allow aot_id clash; return without doing anything return false; @@ -1193,9 +1192,8 @@ void URLClassLoaderClasspathSupport::serialize_classpath_map_table_header(Serial _archived_aot_id_to_classpath.serialize_header(soc); } -bool URLClassLoaderClasspathSupport::verify_archived_classpath(ClassLoaderData* loader_data, const char* classpath) { +bool URLClassLoaderClasspathSupport::verify_archived_classpath(ClassLoaderData* loader_data, Symbol* aot_id, const char* classpath) { ResourceMark rm; - Symbol* aot_id = loader_data->aot_identity(); assert(aot_id != nullptr, "sanity check"); const char* aot_id_str = aot_id->as_C_string(); unsigned int hash = Symbol::symbol_hash(aot_id); diff --git a/src/hotspot/share/cds/aotClassLocation.hpp b/src/hotspot/share/cds/aotClassLocation.hpp index 013ed62e390..ccbbcbf364a 100644 --- a/src/hotspot/share/cds/aotClassLocation.hpp +++ b/src/hotspot/share/cds/aotClassLocation.hpp @@ -301,10 +301,10 @@ class URLClassLoaderClasspathSupport : AllStatic { public: static void init(); static void reload_runtime_map(); - static bool add_urlclassloader_classpath(ClassLoaderData* loader_data, const char* aot_id, const char* classpath); + static bool add_urlclassloader_classpath(ClassLoaderData* loader_data, Symbol* aot_id, const char* classpath); static void archive_classpath_map(); static void serialize_classpath_map_table_header(SerializeClosure* soc); - static bool verify_archived_classpath(ClassLoaderData* loader_data, const char* classpath); + static bool verify_archived_classpath(ClassLoaderData* loader_data, Symbol* aot_id, const char* classpath); }; #endif // SHARE_CDS_AOTCLASSLOCATION_HPP diff --git a/src/hotspot/share/classfile/classLoaderData.cpp b/src/hotspot/share/classfile/classLoaderData.cpp index 8c67bfaa069..f385ffda72c 100644 --- a/src/hotspot/share/classfile/classLoaderData.cpp +++ b/src/hotspot/share/classfile/classLoaderData.cpp @@ -92,7 +92,6 @@ void ClassLoaderData::init_null_class_loader_data() { _the_null_class_loader_data = new ClassLoaderData(Handle(), false); ClassLoaderDataGraph::_head = _the_null_class_loader_data; assert(_the_null_class_loader_data->is_the_null_class_loader_data(), "Must be"); - //_the_null_class_loader_data->_aot_identity = SymbolTable::new_symbol("BOOT"); LogTarget(Trace, class, loader, data) lt; if (lt.is_enabled()) { ResourceMark rm; @@ -158,10 +157,6 @@ ClassLoaderData::ClassLoaderData(Handle h_class_loader, bool has_class_mirror_ho if (!h_class_loader.is_null()) { _class_loader = _handles.add(h_class_loader()); _class_loader_klass = h_class_loader->klass(); - oop aot_identity = java_lang_ClassLoader::aotIdentity(h_class_loader()); - if (aot_identity != nullptr) { - _aot_identity = java_lang_String::as_symbol(aot_identity); - } initialize_name(h_class_loader); } diff --git a/src/hotspot/share/classfile/classLoaderData.hpp b/src/hotspot/share/classfile/classLoaderData.hpp index e3d0de8132f..b81c75cb2e7 100644 --- a/src/hotspot/share/classfile/classLoaderData.hpp +++ b/src/hotspot/share/classfile/classLoaderData.hpp @@ -360,6 +360,7 @@ class ClassLoaderData : public CHeapObj { const char* loader_name_and_id() const; Symbol* name_and_id() const { return _name_and_id; } Symbol* aot_identity() const { return _aot_identity; } + void set_aot_identity(Symbol* aot_id) { _aot_identity = aot_id; } Symbol* parent_aot_identity() const; unsigned identity_hash() const { diff --git a/src/hotspot/share/prims/jvm.cpp b/src/hotspot/share/prims/jvm.cpp index 2dbb1312591..7a9cd4c159c 100644 --- a/src/hotspot/share/prims/jvm.cpp +++ b/src/hotspot/share/prims/jvm.cpp @@ -2262,15 +2262,18 @@ JVM_ENTRY_PROF(jboolean, JVM_RegisterURLClassLoaderAsAOTSafeLoader, JVM_Register ClassLoaderData *loader_data = SystemDictionary::register_loader(h_loader); assert(loader_data->aot_identity() == nullptr, "loader's aot identity should not be set"); const char* aot_id_str = java_lang_String::as_utf8_string(JNIHandles::resolve_non_null(aot_id)); + Symbol* aot_id_symbol = SymbolTable::new_symbol(aot_id_str); const char *classpath_str = java_lang_String::as_utf8_string(JNIHandles::resolve_non_null(classpath)); if (CDSConfig::is_dumping_preimage_static_archive()) { - if (!URLClassLoaderClasspathSupport::add_urlclassloader_classpath(loader_data, aot_id_str, classpath_str)) { + if (!URLClassLoaderClasspathSupport::add_urlclassloader_classpath(loader_data, aot_id_symbol, classpath_str)) { return JNI_FALSE; } + loader_data->set_aot_identity(aot_id_symbol); } else if (CDSConfig::is_using_aot_linked_classes()) { - if (!URLClassLoaderClasspathSupport::verify_archived_classpath(loader_data, classpath_str)) { + if (!URLClassLoaderClasspathSupport::verify_archived_classpath(loader_data, aot_id_symbol, classpath_str)) { return JNI_FALSE; } + loader_data->set_aot_identity(aot_id_symbol); AOTLinkedClassBulkLoader::preload_classes_for_loader(loader_data, CHECK_AND_CLEAR_(JNI_FALSE)); AOTLinkedClassBulkLoader::link_classes_for_loader(loader_data, CHECK_AND_CLEAR_(JNI_FALSE)); } From c17f50b2e1ac74b1f5acb508dfd9d8dd5c555763 Mon Sep 17 00:00:00 2001 From: Ashutosh Mehra Date: Thu, 26 Feb 2026 00:02:21 -0500 Subject: [PATCH 26/30] Add some tests Signed-off-by: Ashutosh Mehra --- ...eURLClassLoadersWithSameClasspathTest.java | 104 +++++++++++++++++ .../SameNameInTwoLoadersTest.java | 108 ++++++++++++++++++ .../SingleURLClassLoaderTest.java | 103 +++++++++++++++++ .../test-classes/CustomLoadee.java | 29 +++++ .../test-classes/CustomLoadee3.java | 29 +++++ .../test-classes/CustomLoadee5.java | 28 +++++ .../MultipleURClassLoadersSameClasspath.java | 79 +++++++++++++ .../SameNameUnrelatedLoaders.java | 90 +++++++++++++++ .../test-classes/SingleURLClassLoader.java | 71 ++++++++++++ 9 files changed, 641 insertions(+) create mode 100644 test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/MultipleURLClassLoadersWithSameClasspathTest.java create mode 100644 test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/SameNameInTwoLoadersTest.java create mode 100644 test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/SingleURLClassLoaderTest.java create mode 100644 test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/test-classes/CustomLoadee.java create mode 100644 test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/test-classes/CustomLoadee3.java create mode 100644 test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/test-classes/CustomLoadee5.java create mode 100644 test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/test-classes/MultipleURClassLoadersSameClasspath.java create mode 100644 test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/test-classes/SameNameUnrelatedLoaders.java create mode 100644 test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/test-classes/SingleURLClassLoader.java diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/MultipleURLClassLoadersWithSameClasspathTest.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/MultipleURLClassLoadersWithSameClasspathTest.java new file mode 100644 index 00000000000..a21e4ef71bc --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/MultipleURLClassLoadersWithSameClasspathTest.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2026, 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 Testing the loading of a class with the same name in two different instances of URLClassLoader + * with partially overlapping classpaths. The test is run with support for URLClassLoader enabled + * in AOTCache. + * + * @requires vm.cds + * @requires vm.cds.custom.loaders + * + * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds + * @compile test-classes/CustomLoadee.java + * test-classes/CustomLoadee3.java + * test-classes/CustomLoadee5.java + * test-classes/SameNameUnrelatedLoaders.java + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar WhiteBox.jar jdk.test.whitebox.WhiteBox + * @run driver SameNameInTwoLoadersTest + */ + +import jdk.test.lib.cds.CDSAppTester; +import jdk.test.lib.helpers.ClassFileInstaller; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.whitebox.WhiteBox; + + +public class MultipleURLClassLoadersWithSameClasspathTest { + private static String appJar; + private static String customJar1; + private static String customJar2; + private static final String mainClass = "MultipleURLClassLoadersSameClasspath"; + + public static void main(String[] args) throws Exception { + appJar = JarBuilder.build("MultipleURLClassLoadersWithSameClasspathTest", "MultipleURLClassLoadersSameClasspath"); + + customJar = JarBuilder.build("MultipleURLClassLoadersWithSameClasspathTest_customjar", "CustomLoadee", "CustomLoadee3"); + + Tester tester = new Tester(); + tester.runAOTWorkflow("AOT", "--two-step-training"); + } + + static class Tester extends CDSAppTester { + public Tester() { + super(mainClass); + useWhiteBox(ClassFileInstaller.getJarPath("WhiteBox.jar")); + } + + @Override + public String classpath(RunMode runMode) { + return appJar; + } + + @Override + public String[] vmArgs(RunMode runMode) { + return new String[] { + "-Xlog:aot+load", + "-XX:+AOTCacheSupportForCustomLoader", + }; + } + + @Override + public String[] appCommandLine(RunMode runMode) { + return new String[] { + mainClass, + customJar + }; + } + + @Override + public void checkExecution(OutputAnalyzer out, RunMode runMode) throws Exception { + if (isAOTWorkflow()) { + if (runMode == RunMode.TRAINING) { + out.shouldMatch("category .*MultipleURLClassLoadersWithSameClasspathTest_customjar.jar\\[0\\] CustomLoadee"); + } + if (runMode == RunMode.PRODUCTION) { + out.shouldMatch("MultipleURLClassLoadersWithSameClasspathTest_customjar.jar CustomLoadee"); + } + } + } + } +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/SameNameInTwoLoadersTest.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/SameNameInTwoLoadersTest.java new file mode 100644 index 00000000000..51122d8569e --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/SameNameInTwoLoadersTest.java @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2026, 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 Testing the loading of a class with the same name in two different instances of URLClassLoader + * with partially overlapping classpaths. The test is run with support for URLClassLoader enabled + * in AOTCache. + * + * @requires vm.cds + * @requires vm.cds.custom.loaders + * + * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds + * @compile test-classes/CustomLoadee.java + * test-classes/CustomLoadee3.java + * test-classes/CustomLoadee5.java + * test-classes/SameNameUnrelatedLoaders.java + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar WhiteBox.jar jdk.test.whitebox.WhiteBox + * @run driver SameNameInTwoLoadersTest + */ + +import jdk.test.lib.cds.CDSAppTester; +import jdk.test.lib.helpers.ClassFileInstaller; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.whitebox.WhiteBox; + + +public class SameNameInTwoLoadersTest { + private static String appJar; + private static String customJar1; + private static String customJar2; + private static final String mainClass = "SameNameUnrelatedLoaders"; + + public static void main(String[] args) throws Exception { + appJar = JarBuilder.build("SameNameInTwoLoadersTest", "SameNameUnrelatedLoaders"); + + customJar1 = JarBuilder.build("SameNameInTwoLoadersTest_customjar1", "CustomLoadee", "CustomLoadee3"); + customJar2 = JarBuilder.build("SameNameInTwoLoadersTest_customjar2", "CustomLoadee", "CustomLoadee5"); + + Tester tester = new Tester(); + tester.runAOTWorkflow("AOT", "--two-step-training"); + } + + static class Tester extends CDSAppTester { + public Tester() { + super(mainClass); + useWhiteBox(ClassFileInstaller.getJarPath("WhiteBox.jar")); + } + + @Override + public String classpath(RunMode runMode) { + return appJar; + } + + @Override + public String[] vmArgs(RunMode runMode) { + return new String[] { + "-Xlog:aot+load", + "-XX:+AOTCacheSupportForCustomLoader", + }; + } + + @Override + public String[] appCommandLine(RunMode runMode) { + return new String[] { + mainClass, + customJar1, + customJar2 + }; + } + + @Override + public void checkExecution(OutputAnalyzer out, RunMode runMode) throws Exception { + if (isAOTWorkflow()) { + if (runMode == RunMode.TRAINING) { + out.shouldMatch("category .*SameNameInTwoLoadersTest_customjar1.jar\\[0\\] CustomLoadee"); + out.shouldMatch("category .*SameNameInTwoLoadersTest_customjar2.jar\\[0\\] CustomLoadee"); + } + if (runMode == RunMode.PRODUCTION) { + out.shouldMatch("SameNameInTwoLoadersTest_customjar1.jar CustomLoadee"); + out.shouldMatch("SameNameInTwoLoadersTest_customjar2.jar CustomLoadee"); + } + } + } + } +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/SingleURLClassLoaderTest.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/SingleURLClassLoaderTest.java new file mode 100644 index 00000000000..813f352086c --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/SingleURLClassLoaderTest.java @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2026, 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 Testing the loading of a class with an instances of URLClassLoader. + * The test is run with support for URLClassLoader enabled in AOTCache. + * It verifies the class is stored in AOTCache and loaded from the AOTCache in production run. + * + * @requires vm.cds + * @requires vm.cds.custom.loaders + * + * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds + * @compile test-classes/CustomLoadee.java + * test-classes/CustomLoadee3.java + * test-classes/CustomLoadee5.java + * test-classes/SingleURLClassLoader.java + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar WhiteBox.jar jdk.test.whitebox.WhiteBox + * @run driver SingleURLClassLoaderTest + */ + +import jdk.test.lib.cds.CDSAppTester; +import jdk.test.lib.helpers.ClassFileInstaller; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.whitebox.WhiteBox; + +public class SingleURLClassLoaderTest { + private static String appJar; + private static String customJar; + private static final String mainClass = "SingleURLClassLoader"; + + public static void main(String[] args) throws Exception { + appJar = JarBuilder.build("SingleURLClassLoaderTest", "SingleURLClassLoader", "CustomLoadee"); + + customJar = JarBuilder.build("SingleURLClassLoaderTest_customjar", "CustomLoadee3", "CustomLoadee5"); + + Tester tester = new Tester(); + tester.runAOTWorkflow("AOT", "--two-step-training"); + } + + static class Tester extends CDSAppTester { + public Tester() { + super(mainClass); + useWhiteBox(ClassFileInstaller.getJarPath("WhiteBox.jar")); + } + + @Override + public String classpath(RunMode runMode) { + return appJar; + } + + @Override + public String[] vmArgs(RunMode runMode) { + return new String[] { + "-Xlog:aot+load", + "-XX:+AOTCacheSupportForCustomLoader", + }; + } + + @Override + public String[] appCommandLine(RunMode runMode) { + return new String[] { + mainClass, + appJar, + customJar, + }; + } + + @Override + public void checkExecution(OutputAnalyzer out, RunMode runMode) throws Exception { + if (isAOTWorkflow()) { + if (runMode == RunMode.TRAINING) { + out.shouldMatch("category .*SingleURLClassLoaderTest_customjar.jar\\[0\\] CustomLoadee3"); + } + if (runMode == RunMode.PRODUCTION) { + out.shouldMatch("SingleURLClassLoaderTest_customjar.jar CustomLoadee3"); + } + } + } + } +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/test-classes/CustomLoadee.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/test-classes/CustomLoadee.java new file mode 100644 index 00000000000..5cc1e412c8d --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/test-classes/CustomLoadee.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2015, 2019, 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 CustomLoadee { + public String toString() { + return "this is CustomLoadee"; + } +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/test-classes/CustomLoadee3.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/test-classes/CustomLoadee3.java new file mode 100644 index 00000000000..71e8fe2a8c4 --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/test-classes/CustomLoadee3.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2015, 2019, 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 CustomLoadee3 { + public String toString() { + return "this is CustomLoadee3"; + } +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/test-classes/CustomLoadee5.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/test-classes/CustomLoadee5.java new file mode 100644 index 00000000000..ba9f6dafa95 --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/test-classes/CustomLoadee5.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 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 + * 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 CustomLoadee5 { + public String toString() { + return "this is CustomLoadee5"; + } +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/test-classes/MultipleURClassLoadersSameClasspath.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/test-classes/MultipleURClassLoadersSameClasspath.java new file mode 100644 index 00000000000..3bc25d9a9d5 --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/test-classes/MultipleURClassLoadersSameClasspath.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2026, 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. + * + */ + +import java.io.File; +import java.net.URL; +import java.net.URLClassLoader; +import jdk.test.whitebox.WhiteBox; + +public class MultipleURLClassLoadersSameClasspath { + static URLClassLoader ldr01, ldr02, ldr03; + + public static void main(String args[]) throws Exception { + if (args.length < 2) { + throw new RuntimeException("insufficient arguments"); + } + String path = args[0]; + URL url = new File(path).toURI().toURL(); + URL[] urls = new URL[] {url}; + + ldr01 = new URLClassLoader(urls); + ldr02 = new URLClassLoader(urls); + ldr03 = new URLClassLoader(urls); + + Class class01 = ldr01.loadClass("CustomLoadee"); + Class class02 = ldr02.loadClass("CustomLoadee"); + Class class03 = ldr03.loadClass("CustomLoadee"); + + System.out.println("class01 = " + class01); + System.out.println("class02 = " + class02); + System.out.println("class03 = " + class03); + + if (class01.getClassLoader() != ldr01) { + throw new RuntimeException("class01 loaded by wrong loader"); + } + if (class02.getClassLoader() != ldr02) { + throw new RuntimeException("class02 loaded by wrong loader"); + } + if (class03.getClassLoader() != ldr03) { + throw new RuntimeException("class03 loaded by wrong loader"); + } + + WhiteBox wb = WhiteBox.getWhiteBox(); + if (wb.isSharedClass(MultipleURClassLoadersSameClasspath.class)) { + boolean class1Shared = wb.isSharedClass(class01); + boolean class2Shared = wb.isSharedClass(class02); + boolean class3Shared = wb.isSharedClass(class03); + if (!class1Shared) { + throw new RuntimeException("first class is not shared"); + } + if (!class2Shared) { + throw new RuntimeException("second class is not shared"); + } + if (class3Shared) { + throw new RuntimeException("third class should not be shared"); + } + } + } +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/test-classes/SameNameUnrelatedLoaders.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/test-classes/SameNameUnrelatedLoaders.java new file mode 100644 index 00000000000..aec241ce63a --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/test-classes/SameNameUnrelatedLoaders.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2026, 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. + * + */ + +import java.io.File; +import java.net.URL; +import java.net.URLClassLoader; +import jdk.test.whitebox.WhiteBox; + +public class SameNameUnrelatedLoaders { + static URLClassLoader ldr01, ldr02; + + public static void main(String args[]) throws Exception { + if (args.length < 2) { + throw new RuntimeException("insufficient arguments"); + } + String path = args[0]; + URL url = new File(path).toURI().toURL(); + URL[] ldr01_urls = new URL[] {url}; + + path = args[1]; + url = new File(path).toURI().toURL(); + URL[] ldr02_urls = new URL[] {url}; + + ldr01 = new URLClassLoader(ldr01_urls); + ldr02 = new URLClassLoader(ldr02_urls); + + Class class01 = ldr01.loadClass("CustomLoadee"); + Class class02 = ldr02.loadClass("CustomLoadee"); + + System.out.println("class01 = " + class01); + System.out.println("class02 = " + class02); + + if (class01.getClassLoader() != ldr01) { + throw new RuntimeException("class01 loaded by wrong loader"); + } + if (class02.getClassLoader() != ldr02) { + throw new RuntimeException("class02 loaded by wrong loader"); + } + + if (true) { + if (class01.isAssignableFrom(class02)) { + throw new RuntimeException("assignable condition failed"); + } + + Object obj01 = class01.newInstance(); + Object obj02 = class02.newInstance(); + + if (class01.isInstance(obj02)) { + throw new RuntimeException("instance relationship condition 01 failed"); + } + if (class02.isInstance(obj01)) { + throw new RuntimeException("instance relationship condition 02 failed"); + } + } + + WhiteBox wb = WhiteBox.getWhiteBox(); + if (wb.isSharedClass(SameNameUnrelatedLoaders.class)) { + boolean class1Shared = wb.isSharedClass(class01); + boolean class2Shared = wb.isSharedClass(class02); + if (!class1Shared) { + throw new RuntimeException("first class is not shared"); + } + + if (!class2Shared) { + throw new RuntimeException("second class is not shared"); + } + } + } +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/test-classes/SingleURLClassLoader.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/test-classes/SingleURLClassLoader.java new file mode 100644 index 00000000000..889ccd880b0 --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/test-classes/SingleURLClassLoader.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2026, 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. + * + */ + +import java.io.File; +import java.net.URL; +import java.net.URLClassLoader; +import jdk.test.whitebox.WhiteBox; + +public class SingleURLClassLoader { + static URLClassLoader loader; + + public static void main(String args[]) throws Exception { + if (args.length < 2) { + throw new RuntimeException("insufficient arguments"); + } + URL url1 = new File(args[0]).toURI().toURL(); + URL url2 = new File(args[1]).toURI().toURL(); + URL[] urls = new URL[] {url1, url2}; + + loader = new URLClassLoader(urls); + + // CustomLoadee is present in classpath of the URLClassLoader and its parent (which is system loader); + // so the CustomLoadee should get loaded by the parent loader if URLClassLoader follows parent-first delegation model + Class class01 = loader.loadClass("CustomLoadee"); + // CustomLoadee3 is only present in the classpath of the URLClassLoader + Class class02 = loader.loadClass("CustomLoadee3"); + + System.out.println("class01 = " + class01); + System.out.println("class02 = " + class02); + + if (class01.getClassLoader() != ClassLoader.getSystemClassLoader()) { + throw new RuntimeException("CustomLoadee loaded by wrong loader"); + } + if (class02.getClassLoader() != loader) { + throw new RuntimeException("CustomLoadee3 loaded by wrong loader"); + } + + WhiteBox wb = WhiteBox.getWhiteBox(); + if (wb.isSharedClass(SingleURLClassLoader.class)) { + boolean class1Shared = wb.isSharedClass(class01); + if (!class1Shared) { + throw new RuntimeException("CustomLoadee class is not shared"); + } + boolean class2Shared = wb.isSharedClass(class02); + if (!class2Shared) { + throw new RuntimeException("CustomLoadee3 class is not shared"); + } + } + } +} From 67278a3964d6c7bf2a3a61c30f30da5ad4bf8ff5 Mon Sep 17 00:00:00 2001 From: Ashutosh Mehra Date: Mon, 9 Mar 2026 14:11:19 -0400 Subject: [PATCH 27/30] Fix bugs, update tests Signed-off-by: Ashutosh Mehra --- src/hotspot/share/cds/aotClassLocation.cpp | 16 +++- src/hotspot/share/cds/aotClassLocation.hpp | 6 +- src/hotspot/share/cds/runTimeClassInfo.hpp | 12 ++- .../share/classfile/compactHashtable.hpp | 12 +-- .../classfile/systemDictionaryShared.cpp | 19 +++-- .../classfile/systemDictionaryShared.hpp | 2 +- src/hotspot/share/oops/constantPool.cpp | 3 +- src/hotspot/share/oops/instanceKlass.hpp | 1 + src/hotspot/share/oops/instanceKlassFlags.hpp | 4 + src/hotspot/share/prims/jvm.cpp | 2 +- src/hotspot/share/prims/whitebox.cpp | 32 ++++++++ src/hotspot/share/runtime/mutexLocker.cpp | 2 + src/hotspot/share/runtime/mutexLocker.hpp | 1 + .../classes/java/net/URLClassLoader.java | 4 +- ...eURLClassLoadersWithSameClasspathTest.java | 11 +-- .../SameNameInTwoLoadersTest.java | 7 +- .../SingleURLClassLoaderTest.java | 1 + .../MultipleURClassLoadersSameClasspath.java | 79 ------------------- .../SameNameUnrelatedLoaders.java | 47 ++++++----- .../test-classes/SingleURLClassLoader.java | 17 +++- test/lib/jdk/test/whitebox/WhiteBox.java | 4 + 21 files changed, 145 insertions(+), 137 deletions(-) delete mode 100644 test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/test-classes/MultipleURClassLoadersSameClasspath.java diff --git a/src/hotspot/share/cds/aotClassLocation.cpp b/src/hotspot/share/cds/aotClassLocation.cpp index 2c6114667bf..614f87fc82f 100644 --- a/src/hotspot/share/cds/aotClassLocation.cpp +++ b/src/hotspot/share/cds/aotClassLocation.cpp @@ -44,6 +44,7 @@ #include "oops/array.hpp" #include "oops/objArrayKlass.hpp" #include "runtime/arguments.hpp" +#include "runtime/mutexLocker.hpp" #include "utilities/classpathStream.hpp" #include "utilities/formatBuffer.hpp" #include "utilities/resizableHashTable.hpp" @@ -1192,16 +1193,27 @@ void URLClassLoaderClasspathSupport::serialize_classpath_map_table_header(Serial _archived_aot_id_to_classpath.serialize_header(soc); } -bool URLClassLoaderClasspathSupport::verify_archived_classpath(ClassLoaderData* loader_data, Symbol* aot_id, const char* classpath) { +bool URLClassLoaderClasspathSupport::claim_and_verify_archived_classpath(ClassLoaderData* loader_data, Symbol* aot_id, const char* classpath) { ResourceMark rm; assert(aot_id != nullptr, "sanity check"); const char* aot_id_str = aot_id->as_C_string(); unsigned int hash = Symbol::symbol_hash(aot_id); URLClassLoaderClasspath* archived_classpath = _archived_aot_id_to_classpath.lookup(aot_id, hash, /*len*/0); // len is ignored if (archived_classpath == nullptr) { - aot_log_warning(aot)("URLClassLoader (id=%s) classpath validation failed (reason: no archived entry found)", aot_id_str); + aot_log_trace(aot)("No archived entry found for URLClassLoader (id=%s)", aot_id_str); return false; } + + { + // Acquire lock before loader_data claims the archived_classpath + MutexLocker mu(URLClassLoaderClasspath_lock, Mutex::_no_safepoint_check_flag); + if (archived_classpath->cld_owner() != nullptr) { + aot_log_warning(aot)("Duplicate URLClassLoader with same classpath found"); + return false; + } + archived_classpath->set_cld_owner(loader_data); + } + URLClassLoaderClassLocationStream uccs(classpath); uccs.start(); for (int i = 0; i < archived_classpath->num_entries(); i++) { diff --git a/src/hotspot/share/cds/aotClassLocation.hpp b/src/hotspot/share/cds/aotClassLocation.hpp index ccbbcbf364a..cef414715b4 100644 --- a/src/hotspot/share/cds/aotClassLocation.hpp +++ b/src/hotspot/share/cds/aotClassLocation.hpp @@ -123,10 +123,12 @@ class URLClassLoaderClasspath { private: Symbol* _loader_id; Array* _class_locations; + ClassLoaderData* _cld_owner; public: void init(Symbol* aot_id, Array* class_locations) { _loader_id = aot_id; _class_locations = class_locations; + _cld_owner = nullptr; } Symbol* loader_id() const { return _loader_id; } address* loader_id_addr() const { return (address*)&_loader_id; } @@ -134,6 +136,8 @@ class URLClassLoaderClasspath { address* class_locations_addr() const { return (address*)&_class_locations; } AOTClassLocation* class_location_at(int i) { return _class_locations->at(i); } int num_entries() { return _class_locations->length(); } + ClassLoaderData* cld_owner() const { return _cld_owner; } + void set_cld_owner(ClassLoaderData* cld) { _cld_owner = cld; } }; // AOTClassLocationConfig @@ -304,7 +308,7 @@ class URLClassLoaderClasspathSupport : AllStatic { static bool add_urlclassloader_classpath(ClassLoaderData* loader_data, Symbol* aot_id, const char* classpath); static void archive_classpath_map(); static void serialize_classpath_map_table_header(SerializeClosure* soc); - static bool verify_archived_classpath(ClassLoaderData* loader_data, Symbol* aot_id, const char* classpath); + static bool claim_and_verify_archived_classpath(ClassLoaderData* loader_data, Symbol* aot_id, const char* classpath); }; #endif // SHARE_CDS_AOTCLASSLOCATION_HPP diff --git a/src/hotspot/share/cds/runTimeClassInfo.hpp b/src/hotspot/share/cds/runTimeClassInfo.hpp index 371924f9065..472cf174978 100644 --- a/src/hotspot/share/cds/runTimeClassInfo.hpp +++ b/src/hotspot/share/cds/runTimeClassInfo.hpp @@ -265,12 +265,16 @@ class RunTimeClassInfo { // Used by RunTimeSharedDictionary to implement OffsetCompactHashtable::EQUALS static inline bool EQUALS( - const RunTimeClassInfo* value, Symbol* key, int len_unused) { + const RunTimeClassInfo* value, Symbol* key, int is_aot_unsafe_loader_class) { #if INCLUDE_CDS - return (value->klass()->name() == key); -#else - return false; + if (value->klass()->name() == key) { + if (is_aot_unsafe_loader_class) { + return !value->klass()->is_defined_by_aot_safe_custom_loader(); + } + return true; + } #endif + return false; } }; diff --git a/src/hotspot/share/classfile/compactHashtable.hpp b/src/hotspot/share/classfile/compactHashtable.hpp index 944fb876521..de1f004c316 100644 --- a/src/hotspot/share/classfile/compactHashtable.hpp +++ b/src/hotspot/share/classfile/compactHashtable.hpp @@ -36,7 +36,7 @@ template < typename K, typename V, V (*DECODE)(address base_address, u4 encoded_value), - bool (*EQUALS)(V value, K key, int len) + bool (*EQUALS)(V value, K key, int user_data) > class CompactHashtable; class NumberSeq; @@ -251,7 +251,7 @@ template < typename K, typename V, V (*DECODE)(address base_address, u4 encoded_value), - bool (*EQUALS)(V value, K key, int len) + bool (*EQUALS)(V value, K key, int user_data) > class CompactHashtable : public SimpleCompactHashtable { @@ -261,7 +261,7 @@ class CompactHashtable : public SimpleCompactHashtable { public: // Lookup a value V from the compact table using key K - inline V lookup(K key, unsigned int hash, int len) const { + inline V lookup(K key, unsigned int hash, int user_data) const { if (_entry_count > 0) { int index = hash % _bucket_count; u4 bucket_info = _buckets[index]; @@ -271,7 +271,7 @@ class CompactHashtable : public SimpleCompactHashtable { if (bucket_type == VALUE_ONLY_BUCKET_TYPE) { V value = decode(entry[0]); - if (EQUALS(value, key, len)) { + if (EQUALS(value, key, user_data)) { return value; } } else { @@ -283,7 +283,7 @@ class CompactHashtable : public SimpleCompactHashtable { unsigned int h = (unsigned int)(entry[0]); if (h == hash) { V value = decode(entry[1]); - if (EQUALS(value, key, len)) { + if (EQUALS(value, key, user_data)) { return value; } } @@ -381,7 +381,7 @@ inline V read_value_from_compact_hashtable(address base_address, u4 offset) { template < typename K, typename V, - bool (*EQUALS)(V value, K key, int len) + bool (*EQUALS)(V value, K key, int user_data) > class OffsetCompactHashtable : public CompactHashtable< K, V, read_value_from_compact_hashtable, EQUALS> { diff --git a/src/hotspot/share/classfile/systemDictionaryShared.cpp b/src/hotspot/share/classfile/systemDictionaryShared.cpp index 06af5fa97fa..7f523caaa12 100644 --- a/src/hotspot/share/classfile/systemDictionaryShared.cpp +++ b/src/hotspot/share/classfile/systemDictionaryShared.cpp @@ -141,7 +141,7 @@ InstanceKlass* SystemDictionaryShared::lookup_from_stream(Symbol* class_name, const RunTimeClassInfo* record = find_record(&_static_archive._unregistered_dictionary, &_dynamic_archive._unregistered_dictionary, - class_name); + class_name, /*is_aot_unsafe_loader_class*/ true); if (record == nullptr) { return nullptr; } @@ -697,8 +697,9 @@ void SystemDictionaryShared::copy_unregistered_class_size_and_crc32(InstanceKlas precond(klass->in_aot_cache()); // A shared class must have a RunTimeClassInfo record - const RunTimeClassInfo* record = find_record(&_static_archive._unregistered_dictionary, - nullptr, klass->name()); + //const RunTimeClassInfo* record = find_record(&_static_archive._unregistered_dictionary, + // nullptr, klass->name()); + const RunTimeClassInfo* record = RunTimeClassInfo::get_for(klass); precond(record != nullptr); precond(record->klass() == klass); @@ -775,6 +776,10 @@ void SystemDictionaryShared::init_dumptime_info_from_preimage(InstanceKlass* k) } else if (SystemDictionary::is_system_class_loader(k->class_loader())) { AOTClassLocationConfig::dumptime_set_has_app_classes(); } + + if (k->defined_by_other_loaders() && !k->is_defined_by_aot_safe_custom_loader()) { + SystemDictionaryShared::copy_unregistered_class_size_and_crc32(k); + } } // Check if a class or any of its supertypes has been redefined. @@ -1391,7 +1396,7 @@ void SystemDictionaryShared::serialize_vm_classes(SerializeClosure* soc) { } const RunTimeClassInfo* -SystemDictionaryShared::find_record(RunTimeSharedDictionary* static_dict, RunTimeSharedDictionary* dynamic_dict, Symbol* name) { +SystemDictionaryShared::find_record(RunTimeSharedDictionary* static_dict, RunTimeSharedDictionary* dynamic_dict, Symbol* name, bool is_aot_unsafe_loader_class) { if (!CDSConfig::is_using_archive() || !name->in_aot_cache()) { // The names of all shared classes must also be a shared Symbol. return nullptr; @@ -1413,11 +1418,11 @@ SystemDictionaryShared::find_record(RunTimeSharedDictionary* static_dict, RunTim if (!AOTMetaspace::in_aot_cache_dynamic_region(name)) { // The names of all shared classes in the static dict must also be in the // static archive - record = static_dict->lookup(name, hash, 0); + record = static_dict->lookup(name, hash, (int)is_aot_unsafe_loader_class); } if (record == nullptr && DynamicArchive::is_mapped()) { - record = dynamic_dict->lookup(name, hash, 0); + record = dynamic_dict->lookup(name, hash, (int)is_aot_unsafe_loader_class); } return record; @@ -1426,7 +1431,7 @@ SystemDictionaryShared::find_record(RunTimeSharedDictionary* static_dict, RunTim InstanceKlass* SystemDictionaryShared::find_builtin_class(Symbol* name) { const RunTimeClassInfo* record = find_record(&_static_archive._builtin_dictionary, &_dynamic_archive._builtin_dictionary, - name); + name, /*is_aot_unsafe_loader_class*/ false); if (record != nullptr) { assert(!record->klass()->is_hidden(), "hidden class cannot be looked up by name"); DEBUG_ONLY(check_klass_after_loading(record->klass());) diff --git a/src/hotspot/share/classfile/systemDictionaryShared.hpp b/src/hotspot/share/classfile/systemDictionaryShared.hpp index f1895587e11..15b0716c642 100644 --- a/src/hotspot/share/classfile/systemDictionaryShared.hpp +++ b/src/hotspot/share/classfile/systemDictionaryShared.hpp @@ -212,7 +212,7 @@ class SystemDictionaryShared: public SystemDictionary { static const RunTimeClassInfo* find_record(RunTimeSharedDictionary* static_dict, RunTimeSharedDictionary* dynamic_dict, - Symbol* name); + Symbol* name, bool is_aot_unsafe_loader_class = false); static bool has_platform_or_app_classes(); diff --git a/src/hotspot/share/oops/constantPool.cpp b/src/hotspot/share/oops/constantPool.cpp index 5196d18b2c0..98c12a40996 100644 --- a/src/hotspot/share/oops/constantPool.cpp +++ b/src/hotspot/share/oops/constantPool.cpp @@ -441,6 +441,7 @@ void ConstantPool::remove_unshareable_info() { } bool update_resolved_reference = true; +#if 0 if (CDSConfig::is_dumping_final_static_archive()) { ConstantPool* src_cp = ArchiveBuilder::current()->get_source_addr(this); InstanceKlass* src_holder = src_cp->pool_holder(); @@ -453,7 +454,7 @@ void ConstantPool::remove_unshareable_info() { update_resolved_reference = false; } } - +#endif // resolved_references(): remember its length. If it cannot be restored // from the archived heap objects at run time, we need to dynamically allocate it. if (update_resolved_reference && cache() != nullptr) { diff --git a/src/hotspot/share/oops/instanceKlass.hpp b/src/hotspot/share/oops/instanceKlass.hpp index 2c177f02ce4..c33e54e6956 100644 --- a/src/hotspot/share/oops/instanceKlass.hpp +++ b/src/hotspot/share/oops/instanceKlass.hpp @@ -327,6 +327,7 @@ class InstanceKlass: public Klass { bool defined_by_boot_loader() const { return _misc_flags.defined_by_boot_loader(); } bool defined_by_platform_loader() const { return _misc_flags.defined_by_platform_loader(); } bool defined_by_app_loader() const { return _misc_flags.defined_by_app_loader(); } + bool defined_by_builtin_loader() const { return _misc_flags.defined_by_builtin_loader(); } bool defined_by_other_loaders() const { return _misc_flags.defined_by_other_loaders(); } void set_class_loader_type() { _misc_flags.set_class_loader_type(_class_loader_data); } diff --git a/src/hotspot/share/oops/instanceKlassFlags.hpp b/src/hotspot/share/oops/instanceKlassFlags.hpp index d576805b30f..0880370daa6 100644 --- a/src/hotspot/share/oops/instanceKlassFlags.hpp +++ b/src/hotspot/share/oops/instanceKlassFlags.hpp @@ -102,6 +102,10 @@ class InstanceKlassFlags { IK_FLAGS_DO(IK_FLAGS_GET_SET) #undef IK_FLAGS_GET_SET + bool defined_by_builtin_loader() const { + return (_flags & builtin_loader_type_bits()) != 0; + } + bool defined_by_other_loaders() const { return (_flags & builtin_loader_type_bits()) == 0; } diff --git a/src/hotspot/share/prims/jvm.cpp b/src/hotspot/share/prims/jvm.cpp index 7a9cd4c159c..377aa5ce59e 100644 --- a/src/hotspot/share/prims/jvm.cpp +++ b/src/hotspot/share/prims/jvm.cpp @@ -2270,7 +2270,7 @@ JVM_ENTRY_PROF(jboolean, JVM_RegisterURLClassLoaderAsAOTSafeLoader, JVM_Register } loader_data->set_aot_identity(aot_id_symbol); } else if (CDSConfig::is_using_aot_linked_classes()) { - if (!URLClassLoaderClasspathSupport::verify_archived_classpath(loader_data, aot_id_symbol, classpath_str)) { + if (!URLClassLoaderClasspathSupport::claim_and_verify_archived_classpath(loader_data, aot_id_symbol, classpath_str)) { return JNI_FALSE; } loader_data->set_aot_identity(aot_id_symbol); diff --git a/src/hotspot/share/prims/whitebox.cpp b/src/hotspot/share/prims/whitebox.cpp index fbe7e361dc1..79c358816c3 100644 --- a/src/hotspot/share/prims/whitebox.cpp +++ b/src/hotspot/share/prims/whitebox.cpp @@ -2230,6 +2230,34 @@ WB_ENTRY(jboolean, WB_IsSharedClass(JNIEnv* env, jobject wb, jclass clazz)) return (jboolean)AOTMetaspace::in_aot_cache(java_lang_Class::as_Klass(JNIHandles::resolve_non_null(clazz))); WB_END +WB_ENTRY(jboolean, WB_IsLoadedByBuiltinLoader(JNIEnv* env, jobject wb, jclass clazz)) + Klass* klass = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(clazz)); + assert(klass->is_instance_klass(), "must be InstanceKlass"); + return InstanceKlass::cast(klass)->defined_by_builtin_loader(); +WB_END + +WB_ENTRY(jboolean, WB_IsLoadedByAOTSafeCustomLoader(JNIEnv* env, jobject wb, jclass clazz)) + Klass* klass = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(clazz)); + assert(klass->is_instance_klass(), "must be InstanceKlass"); + return InstanceKlass::cast(klass)->is_defined_by_aot_safe_custom_loader(); +WB_END + +WB_ENTRY(jboolean, WB_IsLoadedByAOTUnsafeCustomLoader(JNIEnv* env, jobject wb, jclass clazz)) + Klass* klass = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(clazz)); + assert(klass->is_instance_klass(), "must be InstanceKlass"); + InstanceKlass* ik = InstanceKlass::cast(klass); + return !ik->defined_by_builtin_loader() && !ik->is_defined_by_aot_safe_custom_loader(); +WB_END + +WB_ENTRY(jboolean, WB_IsAOTSafeCustomLoader(JNIEnv* env, jobject wb, jobject loader)) + oop class_loader_oop = JNIHandles::resolve(loader); + if (SystemDictionary::is_builtin_class_loader(class_loader_oop)) { + return false; + } + ClassLoaderData* cld = java_lang_ClassLoader::loader_data_acquire(class_loader_oop); + return cld->aot_identity() != nullptr; +WB_END + WB_ENTRY(jboolean, WB_AreSharedStringsMapped(JNIEnv* env)) return AOTMappedHeapLoader::is_mapped(); WB_END @@ -3060,6 +3088,10 @@ static JNINativeMethod methods[] = { {CC"isSharingEnabled", CC"()Z", (void*)&WB_IsSharingEnabled}, {CC"isSharedInternedString", CC"(Ljava/lang/String;)Z", (void*)&WB_IsSharedInternedString }, {CC"isSharedClass", CC"(Ljava/lang/Class;)Z", (void*)&WB_IsSharedClass }, + {CC"isLoadedByBuiltinLoader", CC"(Ljava/lang/Class;)Z", (void*)&WB_IsLoadedByBuiltinLoader}, + {CC"isLoadedByAOTSafeCustomLoader", CC"(Ljava/lang/Class;)Z", (void*)&WB_IsLoadedByAOTSafeCustomLoader}, + {CC"isLoadedByAOTUnsafeCustomLoader", CC"(Ljava/lang/Class;)Z", (void*)&WB_IsLoadedByAOTUnsafeCustomLoader}, + {CC"isAOTSafeCustomLoader", CC"(Ljava/lang/ClassLoader;)Z", (void*)&WB_IsAOTSafeCustomLoader}, {CC"areSharedStringsMapped", CC"()Z", (void*)&WB_AreSharedStringsMapped }, {CC"linkClass", CC"(Ljava/lang/Class;)V", (void*)&WB_LinkClass}, {CC"areOpenArchiveHeapObjectsMapped", CC"()Z", (void*)&WB_AreOpenArchiveHeapObjectsMapped}, diff --git a/src/hotspot/share/runtime/mutexLocker.cpp b/src/hotspot/share/runtime/mutexLocker.cpp index a2872acfd73..9d1e22ee79c 100644 --- a/src/hotspot/share/runtime/mutexLocker.cpp +++ b/src/hotspot/share/runtime/mutexLocker.cpp @@ -164,6 +164,7 @@ Mutex* LambdaFormInvokers_lock = nullptr; Mutex* ScratchObjects_lock = nullptr; Mutex* ArchivedObjectTables_lock = nullptr; Mutex* FinalImageRecipes_lock = nullptr; +Mutex* URLClassLoaderClasspath_lock = nullptr; #endif // INCLUDE_CDS Mutex* Bootclasspath_lock = nullptr; @@ -328,6 +329,7 @@ void mutex_init() { MUTEX_DEFL(ScratchObjects_lock , PaddedMutex , DumpTimeTable_lock); MUTEX_DEFN(ArchivedObjectTables_lock , PaddedMutex , nosafepoint); MUTEX_DEFN(FinalImageRecipes_lock , PaddedMutex , nosafepoint); + MUTEX_DEFN(URLClassLoaderClasspath_lock , PaddedMutex , nosafepoint); #endif // INCLUDE_CDS MUTEX_DEFN(Bootclasspath_lock , PaddedMutex , nosafepoint); diff --git a/src/hotspot/share/runtime/mutexLocker.hpp b/src/hotspot/share/runtime/mutexLocker.hpp index 8a0138633f9..569115af897 100644 --- a/src/hotspot/share/runtime/mutexLocker.hpp +++ b/src/hotspot/share/runtime/mutexLocker.hpp @@ -142,6 +142,7 @@ extern Mutex* LambdaFormInvokers_lock; // Protecting LambdaFormInvoker extern Mutex* ScratchObjects_lock; // Protecting _scratch_xxx_table in heapShared.cpp extern Mutex* ArchivedObjectTables_lock; // Protecting the table used by HeapShared::get_archived_object_permanent_index() extern Mutex* FinalImageRecipes_lock; // Protecting the tables used by FinalImageRecipes. +extern Mutex* URLClassLoaderClasspath_lock; #endif // INCLUDE_CDS #if INCLUDE_JFR extern Mutex* JfrStacktrace_lock; // used to guard access to the JFR stacktrace table diff --git a/src/java.base/share/classes/java/net/URLClassLoader.java b/src/java.base/share/classes/java/net/URLClassLoader.java index 89a2339d49a..6edbeb14959 100644 --- a/src/java.base/share/classes/java/net/URLClassLoader.java +++ b/src/java.base/share/classes/java/net/URLClassLoader.java @@ -682,9 +682,9 @@ private String convertToAOTId(String classpath) { private boolean registerAsAOTSafe(final URL[] urls) { String classpath = createClassPath(urls); - if (!hasBuiltinLoaderAsParent() && getParent().getAOTIdentity() == null) { + if (!hasBuiltinLoaderAsParent()) { if (DEBUG) { - System.out.println("DEBUG: URLClassLoader with classpath \"" + classpath + "\" cannot be registered as AOT-safe (reason=parent not aot-safe)"); + System.out.println("DEBUG: URLClassLoader with classpath \"" + classpath + "\" cannot be registered as AOT-safe (reason=parent is not built-in loader)"); } return false; } diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/MultipleURLClassLoadersWithSameClasspathTest.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/MultipleURLClassLoadersWithSameClasspathTest.java index a21e4ef71bc..fcbf71b15ed 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/MultipleURLClassLoadersWithSameClasspathTest.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/MultipleURLClassLoadersWithSameClasspathTest.java @@ -24,9 +24,7 @@ /* * @test - * @summary Testing the loading of a class with the same name in two different instances of URLClassLoader - * with partially overlapping classpaths. The test is run with support for URLClassLoader enabled - * in AOTCache. + * @summary Test for multiple instances of URLClassLoader with same classpath. * * @requires vm.cds * @requires vm.cds.custom.loaders @@ -35,10 +33,10 @@ * @compile test-classes/CustomLoadee.java * test-classes/CustomLoadee3.java * test-classes/CustomLoadee5.java - * test-classes/SameNameUnrelatedLoaders.java + * test-classes/MultipleURLClassLoadersSameClasspath.java * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar WhiteBox.jar jdk.test.whitebox.WhiteBox - * @run driver SameNameInTwoLoadersTest + * @run driver MultipleURLClassLoadersWithSameClasspathTest */ import jdk.test.lib.cds.CDSAppTester; @@ -49,8 +47,7 @@ public class MultipleURLClassLoadersWithSameClasspathTest { private static String appJar; - private static String customJar1; - private static String customJar2; + private static String customJar; private static final String mainClass = "MultipleURLClassLoadersSameClasspath"; public static void main(String[] args) throws Exception { diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/SameNameInTwoLoadersTest.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/SameNameInTwoLoadersTest.java index 51122d8569e..da261423adc 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/SameNameInTwoLoadersTest.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/SameNameInTwoLoadersTest.java @@ -49,6 +49,7 @@ public class SameNameInTwoLoadersTest { private static String appJar; + private static String commonJar; private static String customJar1; private static String customJar2; private static final String mainClass = "SameNameUnrelatedLoaders"; @@ -56,8 +57,9 @@ public class SameNameInTwoLoadersTest { public static void main(String[] args) throws Exception { appJar = JarBuilder.build("SameNameInTwoLoadersTest", "SameNameUnrelatedLoaders"); - customJar1 = JarBuilder.build("SameNameInTwoLoadersTest_customjar1", "CustomLoadee", "CustomLoadee3"); - customJar2 = JarBuilder.build("SameNameInTwoLoadersTest_customjar2", "CustomLoadee", "CustomLoadee5"); + commonJar = JarBuilder.build("SameNameInTwoLoadersTest_commonjar", "CustomLoadee"); + customJar1 = JarBuilder.build("SameNameInTwoLoadersTest_customjar1", "CustomLoadee3"); + customJar2 = JarBuilder.build("SameNameInTwoLoadersTest_customjar2", "CustomLoadee5"); Tester tester = new Tester(); tester.runAOTWorkflow("AOT", "--two-step-training"); @@ -86,6 +88,7 @@ public String[] vmArgs(RunMode runMode) { public String[] appCommandLine(RunMode runMode) { return new String[] { mainClass, + commonJar, customJar1, customJar2 }; diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/SingleURLClassLoaderTest.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/SingleURLClassLoaderTest.java index 813f352086c..a8f82baa665 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/SingleURLClassLoaderTest.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/SingleURLClassLoaderTest.java @@ -25,6 +25,7 @@ /* * @test * @summary Testing the loading of a class with an instances of URLClassLoader. + * URLClassLoader instance has system loader as its parent, and shares its classpath with the parent. * The test is run with support for URLClassLoader enabled in AOTCache. * It verifies the class is stored in AOTCache and loaded from the AOTCache in production run. * diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/test-classes/MultipleURClassLoadersSameClasspath.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/test-classes/MultipleURClassLoadersSameClasspath.java deleted file mode 100644 index 3bc25d9a9d5..00000000000 --- a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/test-classes/MultipleURClassLoadersSameClasspath.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (c) 2026, 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. - * - */ - -import java.io.File; -import java.net.URL; -import java.net.URLClassLoader; -import jdk.test.whitebox.WhiteBox; - -public class MultipleURLClassLoadersSameClasspath { - static URLClassLoader ldr01, ldr02, ldr03; - - public static void main(String args[]) throws Exception { - if (args.length < 2) { - throw new RuntimeException("insufficient arguments"); - } - String path = args[0]; - URL url = new File(path).toURI().toURL(); - URL[] urls = new URL[] {url}; - - ldr01 = new URLClassLoader(urls); - ldr02 = new URLClassLoader(urls); - ldr03 = new URLClassLoader(urls); - - Class class01 = ldr01.loadClass("CustomLoadee"); - Class class02 = ldr02.loadClass("CustomLoadee"); - Class class03 = ldr03.loadClass("CustomLoadee"); - - System.out.println("class01 = " + class01); - System.out.println("class02 = " + class02); - System.out.println("class03 = " + class03); - - if (class01.getClassLoader() != ldr01) { - throw new RuntimeException("class01 loaded by wrong loader"); - } - if (class02.getClassLoader() != ldr02) { - throw new RuntimeException("class02 loaded by wrong loader"); - } - if (class03.getClassLoader() != ldr03) { - throw new RuntimeException("class03 loaded by wrong loader"); - } - - WhiteBox wb = WhiteBox.getWhiteBox(); - if (wb.isSharedClass(MultipleURClassLoadersSameClasspath.class)) { - boolean class1Shared = wb.isSharedClass(class01); - boolean class2Shared = wb.isSharedClass(class02); - boolean class3Shared = wb.isSharedClass(class03); - if (!class1Shared) { - throw new RuntimeException("first class is not shared"); - } - if (!class2Shared) { - throw new RuntimeException("second class is not shared"); - } - if (class3Shared) { - throw new RuntimeException("third class should not be shared"); - } - } - } -} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/test-classes/SameNameUnrelatedLoaders.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/test-classes/SameNameUnrelatedLoaders.java index aec241ce63a..74d29d011fa 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/test-classes/SameNameUnrelatedLoaders.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/test-classes/SameNameUnrelatedLoaders.java @@ -28,33 +28,29 @@ import jdk.test.whitebox.WhiteBox; public class SameNameUnrelatedLoaders { - static URLClassLoader ldr01, ldr02; + static URLClassLoader loader1, loader2; public static void main(String args[]) throws Exception { - if (args.length < 2) { + if (args.length < 3) { throw new RuntimeException("insufficient arguments"); } - String path = args[0]; - URL url = new File(path).toURI().toURL(); - URL[] ldr01_urls = new URL[] {url}; + URL commonJarUrl = new File(args[0]).toURI().toURL(); + URL jar1Url = new File(args[1]).toURI().toURL(); + URL jar2Url = new File(args[2]).toURI().toURL(); - path = args[1]; - url = new File(path).toURI().toURL(); - URL[] ldr02_urls = new URL[] {url}; + loader1 = new URLClassLoader(new URL[]{commonJarUrl, jar1Url}); + loader2 = new URLClassLoader(new URL[]{commonJarUrl, jar2Url}); - ldr01 = new URLClassLoader(ldr01_urls); - ldr02 = new URLClassLoader(ldr02_urls); - - Class class01 = ldr01.loadClass("CustomLoadee"); - Class class02 = ldr02.loadClass("CustomLoadee"); + Class class01 = loader1.loadClass("CustomLoadee"); + Class class02 = loader2.loadClass("CustomLoadee"); System.out.println("class01 = " + class01); System.out.println("class02 = " + class02); - if (class01.getClassLoader() != ldr01) { + if (class01.getClassLoader() != loader1) { throw new RuntimeException("class01 loaded by wrong loader"); } - if (class02.getClassLoader() != ldr02) { + if (class02.getClassLoader() != loader2) { throw new RuntimeException("class02 loaded by wrong loader"); } @@ -75,16 +71,27 @@ public static void main(String args[]) throws Exception { } WhiteBox wb = WhiteBox.getWhiteBox(); + + if (!wb.isAOTSafeCustomLoader(loader1)) { + throw new RuntimeException("loader1 should be marked as aot-safe"); + } + if (!wb.isAOTSafeCustomLoader(loader2)) { + throw new RuntimeException("loader2 should be marked as aot-safe"); + } + if (wb.isSharedClass(SameNameUnrelatedLoaders.class)) { - boolean class1Shared = wb.isSharedClass(class01); - boolean class2Shared = wb.isSharedClass(class02); - if (!class1Shared) { + if (!wb.isSharedClass(class01)) { throw new RuntimeException("first class is not shared"); } - - if (!class2Shared) { + if (!wb.isSharedClass(class02)) { throw new RuntimeException("second class is not shared"); } + if (!wb.isLoadedByAOTSafeCustomLoader(class01)) { + throw new RuntimeException("first class should have been loaded by AOT-safe loader"); + } + if (!wb.isLoadedByAOTSafeCustomLoader(class02)) { + throw new RuntimeException("second class should have been loaded by AOT-safe loader"); + } } } } diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/test-classes/SingleURLClassLoader.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/test-classes/SingleURLClassLoader.java index 889ccd880b0..f26d117ce60 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/test-classes/SingleURLClassLoader.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/test-classes/SingleURLClassLoader.java @@ -57,15 +57,24 @@ public static void main(String args[]) throws Exception { } WhiteBox wb = WhiteBox.getWhiteBox(); + + if (!wb.isAOTSafeCustomLoader(loader)) { + throw new RuntimeException("loader should be marked as aot-safe"); + } + if (wb.isSharedClass(SingleURLClassLoader.class)) { - boolean class1Shared = wb.isSharedClass(class01); - if (!class1Shared) { + if (!wb.isSharedClass(class01)) { throw new RuntimeException("CustomLoadee class is not shared"); } - boolean class2Shared = wb.isSharedClass(class02); - if (!class2Shared) { + if (!wb.isSharedClass(class02)) { throw new RuntimeException("CustomLoadee3 class is not shared"); } + if (!wb.isLoadedByBuiltinLoader(class01)) { + throw new RuntimeException("CustomLoadee should be loaded by builtin loader"); + } + if (!wb.isLoadedByAOTSafeCustomLoader(class02)) { + throw new RuntimeException("CustomLoadee3 should be loaded by aot-safe loader"); + } } } } diff --git a/test/lib/jdk/test/whitebox/WhiteBox.java b/test/lib/jdk/test/whitebox/WhiteBox.java index cc570caef7c..d0aaa053385 100644 --- a/test/lib/jdk/test/whitebox/WhiteBox.java +++ b/test/lib/jdk/test/whitebox/WhiteBox.java @@ -799,6 +799,10 @@ public Object getMethodOption(Executable method, String name) { public native boolean cdsMemoryMappingFailed(); public native boolean isSharingEnabled(); public native boolean isSharedClass(Class c); + public native boolean isLoadedByBuiltinLoader(Class c); + public native boolean isLoadedByAOTSafeCustomLoader(Class c); + public native boolean isLoadedByAOTUnsafeCustomLoader(Class c); + public native boolean isAOTSafeCustomLoader(ClassLoader loader); public native boolean areSharedStringsMapped(); public native boolean isSharedInternedString(String s); public native boolean isCDSIncluded(); From 77930e3b972a9a4553d0b984f024c402ac8aa091 Mon Sep 17 00:00:00 2001 From: Ashutosh Mehra Date: Mon, 9 Mar 2026 14:12:34 -0400 Subject: [PATCH 28/30] Add missing test files Signed-off-by: Ashutosh Mehra --- .../MultiLevelDelegationTest.java | 115 ++++++++++++++++++ .../test-classes/MultiLevelDelegation.java | 85 +++++++++++++ .../MultipleURLClassLoadersSameClasspath.java | 93 ++++++++++++++ 3 files changed, 293 insertions(+) create mode 100644 test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/MultiLevelDelegationTest.java create mode 100644 test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/test-classes/MultiLevelDelegation.java create mode 100644 test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/test-classes/MultipleURLClassLoadersSameClasspath.java diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/MultiLevelDelegationTest.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/MultiLevelDelegationTest.java new file mode 100644 index 00000000000..18aaaec3a20 --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/MultiLevelDelegationTest.java @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2026, 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 Test multi-level delegation with URLClassLoader L1 delegates to system loader + * and another URLClassLoader L2 delegating to L1 (L2 --> L1 --> SL). + * Classes loaded by L1 should get full AOTCache support but + * classes loaded by L2 would be added to "unregistered" category. + * Only those URLClassLoaders with builtin loader as the parent are currently fully + * supported in AOTCache. + * + * @requires vm.cds + * @requires vm.cds.custom.loaders + * + * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds + * @compile test-classes/CustomLoadee.java + * test-classes/CustomLoadee3.java + * test-classes/MultiLevelDelegation.java + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar WhiteBox.jar jdk.test.whitebox.WhiteBox + * @run driver MultiLevelDelegationTest + */ + +import jdk.test.lib.cds.CDSAppTester; +import jdk.test.lib.helpers.ClassFileInstaller; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.whitebox.WhiteBox; + +public class MultiLevelDelegationTest { + private static String appJar; + private static String customJar; + private static String customJar2; + private static final String mainClass = "MultiLevelDelegation"; + + public static void main(String[] args) throws Exception { + appJar = JarBuilder.build("MultiLevelDelegationTest", "MultiLevelDelegation"); + // jar file for loader L1 + customJar = JarBuilder.build("MultiLevelDelegationTest_customjar", "CustomLoadee"); + // jar file for loader L2 + customJar2 = JarBuilder.build("MultiLevelDelegationTest_customjar2", "CustomLoadee3"); + + Tester tester = new Tester(); + tester.runAOTWorkflow("AOT", "--two-step-training"); + } + + static class Tester extends CDSAppTester { + public Tester() { + super(mainClass); + useWhiteBox(ClassFileInstaller.getJarPath("WhiteBox.jar")); + } + + @Override + public String classpath(RunMode runMode) { + return appJar; + } + + @Override + public String[] vmArgs(RunMode runMode) { + return new String[] { + "-Xlog:aot+load", + "-XX:+AOTCacheSupportForCustomLoader" + }; + } + + @Override + public String[] appCommandLine(RunMode runMode) { + return new String[] { + mainClass, + customJar, + customJar2, + }; + } + + @Override + public void checkExecution(OutputAnalyzer out, RunMode runMode) throws Exception { + if (isAOTWorkflow()) { + if (runMode == RunMode.TRAINING) { + // L1's class (CustomLoadee from customJar) should be in a custom loader category + out.shouldMatch("category .*MultiLevelDelegationTest_customjar.jar\\[0\\] CustomLoadee"); + // L2's class (CustomLoadee3 from customJar2) should be in "unregistered" category + // because its parent is not the system class loader + out.shouldMatch("category unreg\\[0\\] CustomLoadee3"); + } + if (runMode == RunMode.PRODUCTION) { + // L1's class should be eagerly loaded from the AOT cache + out.shouldMatch("MultiLevelDelegationTest_customjar.jar CustomLoadee"); + // L2's class should NOT be eagerly loaded from the AOT cache + out.shouldNotMatch("MultiLevelDelegationTest_customjar2.jar CustomLoadee3"); + } + } + } + } +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/test-classes/MultiLevelDelegation.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/test-classes/MultiLevelDelegation.java new file mode 100644 index 00000000000..e9b1784a579 --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/test-classes/MultiLevelDelegation.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2026, 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. + * + */ + +import java.io.File; +import java.net.URL; +import java.net.URLClassLoader; +import jdk.test.whitebox.WhiteBox; + +public class MultiLevelDelegation { + static URLClassLoader loader1; + static URLClassLoader loader2; + + public static void main(String args[]) throws Exception { + if (args.length < 2) { + throw new RuntimeException("insufficient arguments"); + } + URL url1 = new File(args[0]).toURI().toURL(); + URL url2 = new File(args[1]).toURI().toURL(); + + // loader1 has system loader as its parent + loader1 = new URLClassLoader(new URL[] {url1}); + + // loader2 has loader1 has its parent + loader2 = new URLClassLoader(new URL[] {url2}, loader1); + + Class cls1 = loader1.loadClass("CustomLoadee"); + System.out.println("CustomLoadee loaded by: " + cls1.getClassLoader()); + + if (cls1.getClassLoader() != loader1) { + throw new RuntimeException("CustomLoadee should be loaded by loader1"); + } + + Class cls2 = loader2.loadClass("CustomLoadee3"); + System.out.println("CustomLoadee3 loaded by: " + cls2.getClassLoader()); + + if (cls2.getClassLoader() != loader2) { + throw new RuntimeException("CustomLoadee3 should be loaded by loader2"); + } + + WhiteBox wb = WhiteBox.getWhiteBox(); + + if (!wb.isAOTSafeCustomLoader(loader1)) { + throw new RuntimeException("loader1 should be marked as aot-safe"); + } + if (wb.isAOTSafeCustomLoader(loader2)) { + throw new RuntimeException("loader2 should not be marked as aot-safe"); + } + + if (wb.isSharedClass(MultiLevelDelegation.class)) { + if (!wb.isSharedClass(cls1)) { + throw new RuntimeException("CustomLoadee (loaded by loader1) should be shared"); + } + if (!wb.isSharedClass(cls2)) { + throw new RuntimeException("CustomLoadee3 (loaded by loader2) should be shared"); + } + if (!wb.isLoadedByAOTSafeCustomLoader(cls1)) { + throw new RuntimeException("CustomLoadee should have been loaded by AOT-safe loader"); + } + if (wb.isLoadedByAOTSafeCustomLoader(cls2)) { + throw new RuntimeException("CustomLoadee3 should not have been loaded by AOT-safe loader"); + } + } + } +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/test-classes/MultipleURLClassLoadersSameClasspath.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/test-classes/MultipleURLClassLoadersSameClasspath.java new file mode 100644 index 00000000000..32f1a3e685a --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/test-classes/MultipleURLClassLoadersSameClasspath.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2026, 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. + * + */ + +import java.io.File; +import java.net.URL; +import java.net.URLClassLoader; +import jdk.test.whitebox.WhiteBox; + +public class MultipleURLClassLoadersSameClasspath { + static URLClassLoader loader1, loader2, loader3; + + public static void main(String args[]) throws Exception { + if (args.length < 1) { + throw new RuntimeException("insufficient arguments"); + } + String path = args[0]; + URL url = new File(path).toURI().toURL(); + URL[] urls = new URL[] {url}; + + loader1 = new URLClassLoader(urls); + loader2 = new URLClassLoader(urls); + loader3 = new URLClassLoader(urls); + + Class class01 = loader1.loadClass("CustomLoadee"); + Class class02 = loader2.loadClass("CustomLoadee"); + Class class03 = loader3.loadClass("CustomLoadee"); + + System.out.println("class01 = " + class01); + System.out.println("class02 = " + class02); + System.out.println("class03 = " + class03); + + if (class01.getClassLoader() != loader1) { + throw new RuntimeException("class01 loaded by wrong loader"); + } + if (class02.getClassLoader() != loader2) { + throw new RuntimeException("class02 loaded by wrong loader"); + } + if (class03.getClassLoader() != loader3) { + throw new RuntimeException("class03 loaded by wrong loader"); + } + + WhiteBox wb = WhiteBox.getWhiteBox(); + + if (!wb.isAOTSafeCustomLoader(loader1)) { + throw new RuntimeException("loader1 should be marked as aot-safe"); + } + if (wb.isAOTSafeCustomLoader(loader2)) { + throw new RuntimeException("loader2 should not be marked as aot-safe"); + } + if (wb.isAOTSafeCustomLoader(loader3)) { + throw new RuntimeException("loader3 should not be marked as aot-safe"); + } + + if (wb.isSharedClass(MultipleURLClassLoadersSameClasspath.class)) { + if (!wb.isSharedClass(class01)) { + throw new RuntimeException("first class is not shared"); + } + if (!wb.isSharedClass(class02)) { + throw new RuntimeException("second class is not shared"); + } + if (wb.isSharedClass(class03)) { + throw new RuntimeException("third class should not be shared"); + } + if (!wb.isLoadedByAOTSafeCustomLoader(class01)) { + throw new RuntimeException("first class should have been loaded by AOT-safe loader"); + } + if (wb.isLoadedByAOTSafeCustomLoader(class02)) { + throw new RuntimeException("second class should not have been loaded by AOT-safe loader"); + } + } + } +} From d57711f58bb8b65769f68bbc4285fcad0046c44f Mon Sep 17 00:00:00 2001 From: Ashutosh Mehra Date: Mon, 9 Mar 2026 14:20:49 -0400 Subject: [PATCH 29/30] Fix whitespace erros Signed-off-by: Ashutosh Mehra --- src/hotspot/share/cds/customLoaderSupport.hpp | 2 +- src/hotspot/share/cds/finalImageRecipes.cpp | 2 +- .../appcds/aotCache/customLoader/MultiLevelDelegationTest.java | 2 +- .../appcds/aotCache/customLoader/SingleURLClassLoaderTest.java | 2 +- .../customLoader/test-classes/SingleURLClassLoader.java | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/hotspot/share/cds/customLoaderSupport.hpp b/src/hotspot/share/cds/customLoaderSupport.hpp index db37af55b86..70d923fa87b 100644 --- a/src/hotspot/share/cds/customLoaderSupport.hpp +++ b/src/hotspot/share/cds/customLoaderSupport.hpp @@ -44,7 +44,7 @@ typedef GrowableArrayCHeap ClassList; class ClassLoaderIdToClassTableMap : public ResizeableHashTable { - using ResizeableHashTableBase = ResizeableHashTable; + using ResizeableHashTableBase = ResizeableHashTable; private: class CopyClassTableToArchive : StackObj { private: diff --git a/src/hotspot/share/cds/finalImageRecipes.cpp b/src/hotspot/share/cds/finalImageRecipes.cpp index 8b0c1bae6b0..ad89ca9caf5 100644 --- a/src/hotspot/share/cds/finalImageRecipes.cpp +++ b/src/hotspot/share/cds/finalImageRecipes.cpp @@ -777,5 +777,5 @@ void FinalImageRecipes::serialize(SerializeClosure* soc) { if (CDSConfig::is_dumping_preimage_static_archive() || (CDSConfig::is_dumping_final_static_archive() && soc->reading())) { _archived_aot_safe_loader_classes_map.serialize_header(soc); } - soc->do_ptr((void**)&_final_image_recipes); +soc->do_ptr((void**)&_final_image_recipes); } diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/MultiLevelDelegationTest.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/MultiLevelDelegationTest.java index 18aaaec3a20..b6258054117 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/MultiLevelDelegationTest.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/MultiLevelDelegationTest.java @@ -28,7 +28,7 @@ * and another URLClassLoader L2 delegating to L1 (L2 --> L1 --> SL). * Classes loaded by L1 should get full AOTCache support but * classes loaded by L2 would be added to "unregistered" category. - * Only those URLClassLoaders with builtin loader as the parent are currently fully + * Only those URLClassLoaders with builtin loader as the parent are currently fully * supported in AOTCache. * * @requires vm.cds diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/SingleURLClassLoaderTest.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/SingleURLClassLoaderTest.java index a8f82baa665..0cccebb67e9 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/SingleURLClassLoaderTest.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/SingleURLClassLoaderTest.java @@ -39,7 +39,7 @@ * test-classes/SingleURLClassLoader.java * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar WhiteBox.jar jdk.test.whitebox.WhiteBox - * @run driver SingleURLClassLoaderTest + * @run driver SingleURLClassLoaderTest */ import jdk.test.lib.cds.CDSAppTester; diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/test-classes/SingleURLClassLoader.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/test-classes/SingleURLClassLoader.java index f26d117ce60..d8cdf25eb9d 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/test-classes/SingleURLClassLoader.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/customLoader/test-classes/SingleURLClassLoader.java @@ -50,7 +50,7 @@ public static void main(String args[]) throws Exception { System.out.println("class02 = " + class02); if (class01.getClassLoader() != ClassLoader.getSystemClassLoader()) { - throw new RuntimeException("CustomLoadee loaded by wrong loader"); + throw new RuntimeException("CustomLoadee loaded by wrong loader"); } if (class02.getClassLoader() != loader) { throw new RuntimeException("CustomLoadee3 loaded by wrong loader"); From 1e35626ccec2f1c4be914690f69bc4deaabcbf35 Mon Sep 17 00:00:00 2001 From: Ashutosh Mehra Date: Mon, 9 Mar 2026 23:05:56 -0400 Subject: [PATCH 30/30] Fix bug after merge Signed-off-by: Ashutosh Mehra --- src/hotspot/share/cds/aotClassLocation.cpp | 3 +-- src/hotspot/share/cds/customLoaderSupport.hpp | 3 +-- src/hotspot/share/cds/finalImageRecipes.cpp | 3 +-- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/hotspot/share/cds/aotClassLocation.cpp b/src/hotspot/share/cds/aotClassLocation.cpp index 80f3cc0f620..687e9f0ebfb 100644 --- a/src/hotspot/share/cds/aotClassLocation.cpp +++ b/src/hotspot/share/cds/aotClassLocation.cpp @@ -1183,8 +1183,7 @@ class URLClassLoaderClasspathArchiver : StackObj { ArchivePtrMarker::mark_pointer(ucc->loader_id_addr()); ArchivePtrMarker::mark_pointer(ucc->class_locations_addr()); unsigned int hash = Symbol::symbol_hash(loader_id); - u4 delta = _builder->buffer_to_offset_u4((address)ucc); - _writer->add(hash, delta); + _writer->add(hash, AOTCompressedPointers::encode_not_null((address)ucc)); return true; } }; diff --git a/src/hotspot/share/cds/customLoaderSupport.hpp b/src/hotspot/share/cds/customLoaderSupport.hpp index 70d923fa87b..aa04032634f 100644 --- a/src/hotspot/share/cds/customLoaderSupport.hpp +++ b/src/hotspot/share/cds/customLoaderSupport.hpp @@ -62,8 +62,7 @@ class ClassLoaderIdToClassTableMap : public ResizeableHashTableinit(buffered_loader_id, ArchiveUtils::archive_array(table)); tableForLoader->mark_pointers(); unsigned int hash = Symbol::symbol_hash(loader_id); - u4 delta = _builder->buffer_to_offset_u4((address)tableForLoader); - _writer->add(hash, delta); + _writer->add(hash, AOTCompressedPointers::encode_not_null((address)tableForLoader)); return true; } }; diff --git a/src/hotspot/share/cds/finalImageRecipes.cpp b/src/hotspot/share/cds/finalImageRecipes.cpp index ad89ca9caf5..ff0265dd781 100644 --- a/src/hotspot/share/cds/finalImageRecipes.cpp +++ b/src/hotspot/share/cds/finalImageRecipes.cpp @@ -119,8 +119,7 @@ class ClassLoaderIdToClassRecipesTableMap : public ResizeableHashTableinit(buffered_loader_id, ArchiveUtils::archive_array(table)); tableForLoader->mark_pointers(); unsigned int hash = Symbol::symbol_hash(loader_id); - u4 delta = _builder->buffer_to_offset_u4((address)tableForLoader); - _writer->add(hash, delta); + _writer->add(hash, AOTCompressedPointers::encode_not_null((address)tableForLoader)); return true; } };