diff --git a/src/hotspot/share/cds/heapShared.cpp b/src/hotspot/share/cds/heapShared.cpp index 9da6f85c766..0f5cee30939 100644 --- a/src/hotspot/share/cds/heapShared.cpp +++ b/src/hotspot/share/cds/heapShared.cpp @@ -30,9 +30,8 @@ #include "cds/heapShared.hpp" #include "cds/metaspaceShared.hpp" #include "classfile/classLoaderData.hpp" -#include "classfile/classLoaderDataShared.hpp" #include "classfile/javaClasses.inline.hpp" -#include "classfile/moduleEntry.hpp" +#include "classfile/modules.hpp" #include "classfile/stringTable.hpp" #include "classfile/symbolTable.hpp" #include "classfile/systemDictionary.hpp" @@ -554,7 +553,7 @@ void HeapShared::copy_open_objects(GrowableArray* open_regions) { archive_object_subgraphs(fmg_open_archive_subgraph_entry_fields, false /* is_closed_archive */, true /* is_full_module_graph */); - ClassLoaderDataShared::init_archived_oops(); + Modules::verify_archived_modules(); } copy_roots(); @@ -1189,25 +1188,6 @@ void HeapShared::check_closed_region_object(InstanceKlass* k) { } } -void HeapShared::check_module_oop(oop orig_module_obj) { - assert(DumpSharedSpaces, "must be"); - assert(java_lang_Module::is_instance(orig_module_obj), "must be"); - ModuleEntry* orig_module_ent = java_lang_Module::module_entry_raw(orig_module_obj); - if (orig_module_ent == NULL) { - // These special Module objects are created in Java code. They are not - // defined via Modules::define_module(), so they don't have a ModuleEntry: - // java.lang.Module::ALL_UNNAMED_MODULE - // java.lang.Module::EVERYONE_MODULE - // jdk.internal.loader.ClassLoaders$BootClassLoader::unnamedModule - assert(java_lang_Module::name(orig_module_obj) == NULL, "must be unnamed"); - log_info(cds, heap)("Module oop with No ModuleEntry* @[" PTR_FORMAT "]", p2i(orig_module_obj)); - } else { - ClassLoaderData* loader_data = orig_module_ent->loader_data(); - assert(loader_data->is_builtin_class_loader_data(), "must be"); - } -} - - // (1) If orig_obj has not been archived yet, archive it. // (2) If orig_obj has not been seen yet (since start_recording_subgraph() was called), // trace all objects that are reachable from it, and make sure these objects are archived. @@ -1276,9 +1256,10 @@ oop HeapShared::archive_reachable_objects_from(int level, } if (java_lang_Module::is_instance(orig_obj)) { - check_module_oop(orig_obj); + if (Modules::check_module_oop(orig_obj)) { + Modules::update_oops_in_archived_module(orig_obj, append_root(archived_obj)); + } java_lang_Module::set_module_entry(archived_obj, NULL); - java_lang_Module::set_loader(archived_obj, NULL); } else if (java_lang_ClassLoader::is_instance(orig_obj)) { // class_data will be restored explicitly at run time. guarantee(orig_obj == SystemDictionary::java_platform_loader() || diff --git a/src/hotspot/share/cds/heapShared.hpp b/src/hotspot/share/cds/heapShared.hpp index 067897bd821..c77009e5107 100644 --- a/src/hotspot/share/cds/heapShared.hpp +++ b/src/hotspot/share/cds/heapShared.hpp @@ -301,7 +301,6 @@ private: static bool has_been_seen_during_subgraph_recording(oop obj); static void set_has_been_seen_during_subgraph_recording(oop obj); - static void check_module_oop(oop orig_module_obj); static void copy_roots(); static void resolve_classes_for_subgraphs(JavaThread* current, ArchivableStaticFieldInfo fields[]); diff --git a/src/hotspot/share/classfile/classLoaderDataShared.cpp b/src/hotspot/share/classfile/classLoaderDataShared.cpp index 7954e643c67..92532427446 100644 --- a/src/hotspot/share/classfile/classLoaderDataShared.cpp +++ b/src/hotspot/share/classfile/classLoaderDataShared.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2022, 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 @@ -54,7 +54,6 @@ public: void iterate_symbols(ClassLoaderData* loader_data, MetaspaceClosure* closure); void allocate(ClassLoaderData* loader_data); void init_archived_entries(ClassLoaderData* loader_data); - void init_archived_oops(ClassLoaderData* loader_data); void serialize(SerializeClosure* f) { f->do_ptr((void**)&_packages); @@ -101,14 +100,6 @@ void ArchivedClassLoaderData::init_archived_entries(ClassLoaderData* loader_data } } -void ArchivedClassLoaderData::init_archived_oops(ClassLoaderData* loader_data) { - assert(DumpSharedSpaces, "must be"); - assert_valid(loader_data); - if (loader_data != NULL) { - loader_data->modules()->init_archived_oops(_modules); - } -} - void ArchivedClassLoaderData::restore(ClassLoaderData* loader_data, bool do_entries, bool do_oops) { assert(UseSharedSpaces, "must be"); assert_valid(loader_data); @@ -174,13 +165,6 @@ void ClassLoaderDataShared::init_archived_tables() { _archived_javabase_moduleEntry = ModuleEntry::get_archived_entry(ModuleEntryTable::javabase_moduleEntry()); } -void ClassLoaderDataShared::init_archived_oops() { - assert(DumpSharedSpaces && MetaspaceShared::use_full_module_graph(), "must be"); - _archived_boot_loader_data.init_archived_oops (null_class_loader_data()); - _archived_platform_loader_data.init_archived_oops(java_platform_loader_data_or_null()); - _archived_system_loader_data.init_archived_oops (java_system_loader_data_or_null()); -} - void ClassLoaderDataShared::serialize(SerializeClosure* f) { _archived_boot_loader_data.serialize(f); _archived_platform_loader_data.serialize(f); diff --git a/src/hotspot/share/classfile/classLoaderDataShared.hpp b/src/hotspot/share/classfile/classLoaderDataShared.hpp index f5272f8d59f..957c705afde 100644 --- a/src/hotspot/share/classfile/classLoaderDataShared.hpp +++ b/src/hotspot/share/classfile/classLoaderDataShared.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2022, 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 @@ -38,7 +38,6 @@ public: static void allocate_archived_tables(); static void iterate_symbols(MetaspaceClosure* closure); static void init_archived_tables(); - static void init_archived_oops(); static void serialize(SerializeClosure* f); static void clear_archived_oops(); static oop restore_archived_oops_for_null_class_loader_data(); diff --git a/src/hotspot/share/classfile/moduleEntry.cpp b/src/hotspot/share/classfile/moduleEntry.cpp index 67ca698c09c..28f865be2aa 100644 --- a/src/hotspot/share/classfile/moduleEntry.cpp +++ b/src/hotspot/share/classfile/moduleEntry.cpp @@ -31,8 +31,10 @@ #include "classfile/classLoaderData.inline.hpp" #include "classfile/javaClasses.inline.hpp" #include "classfile/moduleEntry.hpp" +#include "classfile/systemDictionary.hpp" #include "jni.h" #include "logging/log.hpp" +#include "logging/logStream.hpp" #include "memory/resourceArea.hpp" #include "memory/universe.hpp" #include "oops/oopHandle.inline.hpp" @@ -386,20 +388,38 @@ typedef ResourceHashtable< AnyObj::C_HEAP> ArchivedModuleEntries; static ArchivedModuleEntries* _archive_modules_entries = NULL; +#ifndef PRODUCT +static int _num_archived_module_entries = 0; +static int _num_inited_module_entries = 0; +#endif + ModuleEntry* ModuleEntry::allocate_archived_entry() const { assert(is_named(), "unnamed packages/modules are not archived"); ModuleEntry* archived_entry = (ModuleEntry*)ArchiveBuilder::rw_region_alloc(sizeof(ModuleEntry)); memcpy((void*)archived_entry, (void*)this, sizeof(ModuleEntry)); + archived_entry->_archived_module_index = -1; if (_archive_modules_entries == NULL) { _archive_modules_entries = new (mtClass)ArchivedModuleEntries(); } assert(_archive_modules_entries->get(this) == NULL, "Each ModuleEntry must not be shared across ModuleEntryTables"); _archive_modules_entries->put(this, archived_entry); + DEBUG_ONLY(_num_archived_module_entries++); + if (log_is_enabled(Info, cds, module)) { + ResourceMark rm; + LogStream ls(Log(cds, module)::info()); + ls.print("Stored in archive: "); + archived_entry->print(&ls); + } return archived_entry; } +bool ModuleEntry::has_been_archived() { + assert(!ArchiveBuilder::current()->is_in_buffer_space(this), "must be called on original ModuleEntry"); + return _archive_modules_entries->contains(this); +} + ModuleEntry* ModuleEntry::get_archived_entry(ModuleEntry* orig_entry) { ModuleEntry** ptr = _archive_modules_entries->get(orig_entry); assert(ptr != NULL && *ptr != NULL, "must have been allocated"); @@ -467,27 +487,39 @@ void ModuleEntry::init_as_archived_entry() { ArchivePtrMarker::mark_pointer((address*)&_location); } -void ModuleEntry::init_archived_oops() { +void ModuleEntry::update_oops_in_archived_module(int root_oop_index) { assert(DumpSharedSpaces, "static dump only"); - oop module_obj = module(); - if (module_obj != NULL) { - oop m = HeapShared::find_archived_heap_object(module_obj); - assert(m != NULL, "sanity"); - _archived_module_index = HeapShared::append_root(m); - } + assert(_archived_module_index == -1, "must be set exactly once"); + assert(root_oop_index >= 0, "sanity"); + + _archived_module_index = root_oop_index; + assert(shared_protection_domain() == NULL, "never set during -Xshare:dump"); // Clear handles and restore at run time. Handles cannot be archived. OopHandle null_handle; _module = null_handle; + + // For verify_archived_module_entries() + DEBUG_ONLY(_num_inited_module_entries++); } +#ifndef PRODUCT +void ModuleEntry::verify_archived_module_entries() { + assert(_num_archived_module_entries == _num_inited_module_entries, + "%d ModuleEntries have been archived but %d of them have been properly initialized with archived java.lang.Module objects", + _num_archived_module_entries, _num_inited_module_entries); +} +#endif // PRODUCT + void ModuleEntry::load_from_archive(ClassLoaderData* loader_data) { + assert(UseSharedSpaces, "runtime only"); set_loader_data(loader_data); _reads = restore_growable_array((Array*)_reads); JFR_ONLY(INIT_ID(this);) } void ModuleEntry::restore_archived_oops(ClassLoaderData* loader_data) { + assert(UseSharedSpaces, "runtime only"); Handle module_handle(Thread::current(), HeapShared::get_root(_archived_module_index, /*clear=*/true)); assert(module_handle.not_null(), "huh"); set_module(loader_data->add_handle(module_handle)); @@ -496,12 +528,19 @@ void ModuleEntry::restore_archived_oops(ClassLoaderData* loader_data) { // because it may be affected by archive relocation. java_lang_Module::set_module_entry(module_handle(), this); - if (loader_data->class_loader() != NULL) { - java_lang_Module::set_loader(module_handle(), loader_data->class_loader()); + assert(java_lang_Module::loader(module_handle()) == loader_data->class_loader(), + "must be set in dump time"); + + if (log_is_enabled(Info, cds, module)) { + ResourceMark rm; + LogStream ls(Log(cds, module)::info()); + ls.print("Restored from archive: "); + print(&ls); } } void ModuleEntry::clear_archived_oops() { + assert(UseSharedSpaces, "runtime only"); HeapShared::clear_root(_archived_module_index); } @@ -544,14 +583,6 @@ void ModuleEntryTable::init_archived_entries(Array* archived_modul } } -void ModuleEntryTable::init_archived_oops(Array* archived_modules) { - assert(DumpSharedSpaces, "dump time only"); - for (int i = 0; i < archived_modules->length(); i++) { - ModuleEntry* archived_entry = archived_modules->at(i); - archived_entry->init_archived_oops(); - } -} - void ModuleEntryTable::load_archived_entries(ClassLoaderData* loader_data, Array* archived_modules) { assert(UseSharedSpaces, "runtime only"); diff --git a/src/hotspot/share/classfile/moduleEntry.hpp b/src/hotspot/share/classfile/moduleEntry.hpp index 1d8dfc35908..45973c9d833 100644 --- a/src/hotspot/share/classfile/moduleEntry.hpp +++ b/src/hotspot/share/classfile/moduleEntry.hpp @@ -175,13 +175,15 @@ public: void iterate_symbols(MetaspaceClosure* closure); ModuleEntry* allocate_archived_entry() const; void init_as_archived_entry(); - void init_archived_oops(); static ModuleEntry* get_archived_entry(ModuleEntry* orig_entry); + bool has_been_archived(); static Array* write_growable_array(GrowableArray* array); static GrowableArray* restore_growable_array(Array* archived_array); void load_from_archive(ClassLoaderData* loader_data); void restore_archived_oops(ClassLoaderData* loader_data); void clear_archived_oops(); + void update_oops_in_archived_module(int root_oop_index); + static void verify_archived_module_entries() PRODUCT_RETURN; #endif }; @@ -250,7 +252,6 @@ public: void iterate_symbols(MetaspaceClosure* closure); Array* allocate_archived_entries(); void init_archived_entries(Array* archived_modules); - void init_archived_oops(Array* archived_modules); void load_archived_entries(ClassLoaderData* loader_data, Array* archived_modules); void restore_archived_oops(ClassLoaderData* loader_data, diff --git a/src/hotspot/share/classfile/modules.cpp b/src/hotspot/share/classfile/modules.cpp index c37dee82a42..027ecc646fc 100644 --- a/src/hotspot/share/classfile/modules.cpp +++ b/src/hotspot/share/classfile/modules.cpp @@ -477,6 +477,88 @@ void Modules::define_module(Handle module, jboolean is_open, jstring version, } #if INCLUDE_CDS_JAVA_HEAP +static bool _seen_platform_unnamed_module = false; +static bool _seen_system_unnamed_module = false; + +// Validate the states of an java.lang.Module oop to be archived. +// +// Returns true iff the oop has an archived ModuleEntry. +bool Modules::check_module_oop(oop orig_module_obj) { + assert(DumpSharedSpaces, "must be"); + assert(MetaspaceShared::use_full_module_graph(), "must be"); + assert(java_lang_Module::is_instance(orig_module_obj), "must be"); + + ModuleEntry* orig_module_ent = java_lang_Module::module_entry_raw(orig_module_obj); + if (orig_module_ent == nullptr) { + // These special java.lang.Module oops are created in Java code. They are not + // defined via Modules::define_module(), so they don't have a ModuleEntry: + // java.lang.Module::ALL_UNNAMED_MODULE + // java.lang.Module::EVERYONE_MODULE + // jdk.internal.loader.ClassLoaders$BootClassLoader::unnamedModule + log_info(cds, module)("Archived java.lang.Module oop " PTR_FORMAT " with no ModuleEntry*", p2i(orig_module_obj)); + assert(java_lang_Module::name(orig_module_obj) == nullptr, "must be unnamed"); + return false; + } else { + // This java.lang.Module oop has an ModuleEntry*. Check if the latter is archived. + if (log_is_enabled(Info, cds, module)) { + ResourceMark rm; + LogStream ls(Log(cds, module)::info()); + ls.print("Archived java.lang.Module oop " PTR_FORMAT " for ", p2i(orig_module_obj)); + orig_module_ent->print(&ls); + } + + // 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"); + + if (orig_module_ent->name() != nullptr) { + // For each named module, we archive both the java.lang.Module oop and the ModuleEntry. + assert(orig_module_ent->has_been_archived(), "sanity"); + return true; + } else { + // We only archive two unnamed module oops (for platform and system loaders). These do NOT have an archived + // ModuleEntry. + // + // At runtime, these oops are fetched from java_lang_ClassLoader::unnamedModule(loader) and + // are initialized in ClassLoaderData::ClassLoaderData() => ModuleEntry::create_unnamed_module(), where + // a new ModuleEntry is allocated. + assert(!loader_data->is_boot_class_loader_data(), "unnamed module for boot loader should be not archived"); + assert(!orig_module_ent->has_been_archived(), "sanity"); + + if (SystemDictionary::is_platform_class_loader(loader_data->class_loader())) { + assert(!_seen_platform_unnamed_module, "only once"); + _seen_platform_unnamed_module = true; + } else if (SystemDictionary::is_system_class_loader(loader_data->class_loader())) { + assert(!_seen_system_unnamed_module, "only once"); + _seen_system_unnamed_module = true; + } else { + // The java.lang.Module oop and ModuleEntry of the unnamed module of the boot loader are + // not in the archived module graph. These are always allocated at runtime. + ShouldNotReachHere(); + } + return false; + } + } +} + +void Modules::update_oops_in_archived_module(oop orig_module_obj, int archived_module_root_index) { + // This java.lang.Module oop must have an archived ModuleEntry + assert(check_module_oop(orig_module_obj) == true, "sanity"); + + // We remember the oop inside the ModuleEntry::_archived_module_index. At runtime, we use + // this index to reinitialize the ModuleEntry inside ModuleEntry::restore_archived_oops(). + // + // ModuleEntry::verify_archived_module_entries(), called below, ensures that every archived + // ModuleEntry has been assigned an _archived_module_index. + ModuleEntry* orig_module_ent = java_lang_Module::module_entry_raw(orig_module_obj); + ModuleEntry::get_archived_entry(orig_module_ent)->update_oops_in_archived_module(archived_module_root_index); +} + +void Modules::verify_archived_modules() { + ModuleEntry::verify_archived_module_entries(); +} + void Modules::define_archived_modules(Handle h_platform_loader, Handle h_system_loader, TRAPS) { assert(UseSharedSpaces && MetaspaceShared::use_full_module_graph(), "must be"); diff --git a/src/hotspot/share/classfile/modules.hpp b/src/hotspot/share/classfile/modules.hpp index 5c52a323b0c..d444ea3bb82 100644 --- a/src/hotspot/share/classfile/modules.hpp +++ b/src/hotspot/share/classfile/modules.hpp @@ -53,8 +53,12 @@ public: static void define_module(Handle module, jboolean is_open, jstring version, jstring location, jobjectArray packages, TRAPS); + static bool check_module_oop(oop orig_module_obj) NOT_CDS_JAVA_HEAP_RETURN_(false); + static void update_oops_in_archived_module(oop orig_module_obj, int archived_module_root_index) + NOT_CDS_JAVA_HEAP_RETURN; static void define_archived_modules(Handle h_platform_loader, Handle h_system_loader, TRAPS) NOT_CDS_JAVA_HEAP_RETURN; + static void verify_archived_modules() NOT_CDS_JAVA_HEAP_RETURN; // Provides the java.lang.Module for the unnamed module defined // to the boot loader.