--- old/src/hotspot/share/classfile/classLoader.cpp 2020-07-22 12:00:25.959589167 -0700 +++ new/src/hotspot/share/classfile/classLoader.cpp 2020-07-22 12:00:25.775582241 -0700 @@ -1641,7 +1641,7 @@ vm_exit_during_initialization("No ModuleEntryTable for the boot class loader"); } - { + if (ModuleEntryTable::javabase_moduleEntry() == NULL) { // may have been inited by CDS. MutexLocker ml(THREAD, Module_lock); ModuleEntry* jb_module = null_cld_modules->locked_create_entry(Handle(), false, vmSymbols::java_base(), NULL, NULL, null_cld); --- old/src/hotspot/share/classfile/classLoaderData.cpp 2020-07-22 12:00:26.539611001 -0700 +++ new/src/hotspot/share/classfile/classLoaderData.cpp 2020-07-22 12:00:26.359604225 -0700 @@ -59,9 +59,12 @@ #include "logging/logStream.hpp" #include "memory/allocation.inline.hpp" #include "memory/metadataFactory.hpp" +#include "memory/metaspaceShared.hpp" +#include "memory/iterator.hpp" #include "memory/resourceArea.hpp" #include "memory/universe.hpp" #include "oops/access.inline.hpp" +#include "oops/array.hpp" #include "oops/oop.inline.hpp" #include "oops/oopHandle.inline.hpp" #include "oops/weakHandle.inline.hpp" @@ -73,6 +76,146 @@ #include "utilities/macros.hpp" #include "utilities/ostream.hpp" + +#if INCLUDE_CDS_JAVA_HEAP +// Support for archiving full module graph in CDS + +class ArchivedClassLoaderData { + Array* _packages; + Array* _modules; + +public: + ArchivedClassLoaderData() : _packages(NULL), _modules(NULL) {} + + 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); + f->do_ptr((void**)&_modules); + } + + void load_archived_entries(ClassLoaderData* loader_data); + void restore_archived_oops(ClassLoaderData* loader_data); +}; + +static ArchivedClassLoaderData _archived_boot_loader_data; +static ArchivedClassLoaderData _archived_platform_loader_data; +static ArchivedClassLoaderData _archived_system_loader_data; +static ModuleEntry* _archived_javabase_moduleEntry = NULL; + +void ArchivedClassLoaderData::allocate(ClassLoaderData* loader_data) { + assert(DumpSharedSpaces, "must be"); + + // We can't create a hashtable at dump time because the hashcode dependes on the + // address of the Symbols, which may be relocated at run time due to ASLR. + if (loader_data) { + _packages = loader_data->packages()->allocate_archived_entries(); + _modules = loader_data->modules() ->allocate_archived_entries(); + } +} + +void ArchivedClassLoaderData::init_archived_entries(ClassLoaderData* loader_data) { + assert(DumpSharedSpaces, "must be"); + if (loader_data) { + loader_data->packages()->init_archived_entries(_packages); + loader_data->modules() ->init_archived_entries(_modules); + } +} + +void ArchivedClassLoaderData::init_archived_oops(ClassLoaderData* loader_data) { + assert(DumpSharedSpaces, "must be"); + if (loader_data) { + loader_data->modules()->init_archived_oops(_modules); + } +} + +void ArchivedClassLoaderData::load_archived_entries(ClassLoaderData* loader_data) { + assert(UseSharedSpaces, "must be"); + if (_modules) { + loader_data->modules()->load_archived_entries(loader_data, _modules); + loader_data->packages()->load_archived_entries(_packages); + } +} + +void ArchivedClassLoaderData::restore_archived_oops(ClassLoaderData* loader_data) { + assert(UseSharedSpaces, "must be"); + if (_modules) { + loader_data->modules()->restore_archived_oops(loader_data, _modules); + } +} + +// ------------------------------ + +void ClassLoaderData::allocate_archived_tables() { + assert(DumpSharedSpaces, "must be"); + if (MetaspaceShared::use_full_module_graph()) { + _archived_boot_loader_data.allocate (_the_null_class_loader_data); + _archived_platform_loader_data.allocate(class_loader_data_or_null(SystemDictionary::java_platform_loader())); + _archived_system_loader_data.allocate (class_loader_data_or_null(SystemDictionary::java_system_loader())); + } +} + +void ClassLoaderData::init_archived_tables() { + assert(DumpSharedSpaces, "must be"); + if (MetaspaceShared::use_full_module_graph()) { + _archived_boot_loader_data.init_archived_entries (_the_null_class_loader_data); + _archived_platform_loader_data.init_archived_entries(class_loader_data_or_null(SystemDictionary::java_platform_loader())); + _archived_system_loader_data.init_archived_entries (class_loader_data_or_null(SystemDictionary::java_system_loader())); + _archived_javabase_moduleEntry = ModuleEntry::get_archived_entry(ModuleEntryTable::javabase_moduleEntry()); + } +} + +void ClassLoaderData::init_archived_oops() { + assert(DumpSharedSpaces, "must be"); + if (MetaspaceShared::use_full_module_graph()) { + _archived_boot_loader_data.init_archived_oops (_the_null_class_loader_data); + _archived_platform_loader_data.init_archived_oops(class_loader_data_or_null(SystemDictionary::java_platform_loader())); + _archived_system_loader_data.init_archived_oops (class_loader_data_or_null(SystemDictionary::java_system_loader())); + } +} + +void ClassLoaderData::serialize(class SerializeClosure* f) { + _archived_boot_loader_data.serialize(f); + _archived_platform_loader_data.serialize(f); + _archived_system_loader_data.serialize(f); + f->do_ptr((void**)&_archived_javabase_moduleEntry); + + if (f->reading() && MetaspaceShared::use_full_module_graph()) { + // Must be done before ClassLoader::create_javabase() + _archived_boot_loader_data.load_archived_entries(_the_null_class_loader_data); + ModuleEntryTable::set_javabase_moduleEntry(_archived_javabase_moduleEntry); + log_info(cds)("use_full_module_graph = true; java.base = " INTPTR_FORMAT, + p2i(_archived_javabase_moduleEntry)); + } +} + +void ClassLoaderData::restore_archived_oops_for_null_class_loader_data() { + assert(UseSharedSpaces, "must be"); + if (MetaspaceShared::use_full_module_graph()) { + _archived_boot_loader_data.restore_archived_oops(_the_null_class_loader_data); + } +} + +void ClassLoaderData::restore_java_platform_loader_from_archive() { + assert(UseSharedSpaces, "must be"); + assert(MetaspaceShared::use_full_module_graph(), "must be"); + assert(class_loader() == SystemDictionary::java_platform_loader(), "must be"); + _archived_platform_loader_data.load_archived_entries(this); + _archived_platform_loader_data.restore_archived_oops(this); +} + +void ClassLoaderData::restore_java_system_loader_from_archive() { + assert(UseSharedSpaces, "must be"); + assert(MetaspaceShared::use_full_module_graph(), "must be"); + assert(class_loader() == SystemDictionary::java_system_loader(), "must be"); + _archived_system_loader_data.load_archived_entries(this); + _archived_system_loader_data.restore_archived_oops(this); +} + +#endif // INCLUDE_JAVA_HEAP + ClassLoaderData * ClassLoaderData::_the_null_class_loader_data = NULL; void ClassLoaderData::init_null_class_loader_data() { --- old/src/hotspot/share/classfile/classLoaderData.hpp 2020-07-22 12:00:27.111632533 -0700 +++ new/src/hotspot/share/classfile/classLoaderData.hpp 2020-07-22 12:00:26.927625606 -0700 @@ -332,6 +332,16 @@ const char* loader_name_and_id() const; Symbol* name_and_id() const { return _name_and_id; } +#if INCLUDE_CDS_JAVA_HEAP + static void allocate_archived_tables(); + static void init_archived_tables(); + static void init_archived_oops(); + static void serialize(class SerializeClosure* f); + static void restore_archived_oops_for_null_class_loader_data(); + void restore_java_platform_loader_from_archive(); + void restore_java_system_loader_from_archive(); +#endif + JFR_ONLY(DEFINE_TRACE_ID_METHODS;) }; --- old/src/hotspot/share/classfile/javaClasses.cpp 2020-07-22 12:00:27.691654366 -0700 +++ new/src/hotspot/share/classfile/javaClasses.cpp 2020-07-22 12:00:27.503647289 -0700 @@ -139,9 +139,9 @@ // Helpful routine for computing field offsets at run time rather than hardcoding them // Finds local fields only, including static fields. Static field offsets are from the // beginning of the mirror. -static void compute_offset(int &dest_offset, - InstanceKlass* ik, Symbol* name_symbol, Symbol* signature_symbol, - bool is_static = false) { +void JavaClasses::compute_offset(int &dest_offset, + InstanceKlass* ik, Symbol* name_symbol, Symbol* signature_symbol, + bool is_static) { fieldDescriptor fd; if (ik == NULL) { ResourceMark rm; @@ -165,9 +165,9 @@ } // Overloading to pass name as a string. -static void compute_offset(int& dest_offset, InstanceKlass* ik, - const char* name_string, Symbol* signature_symbol, - bool is_static = false) { +void JavaClasses::compute_offset(int& dest_offset, InstanceKlass* ik, + const char* name_string, Symbol* signature_symbol, + bool is_static) { TempNewSymbol name = SymbolTable::probe(name_string, (int)strlen(name_string)); if (name == NULL) { ResourceMark rm; @@ -184,7 +184,7 @@ #endif #define FIELD_COMPUTE_OFFSET(offset, klass, name, signature, is_static) \ - compute_offset(offset, klass, name, vmSymbols::signature(), is_static) + JavaClasses::compute_offset(offset, klass, name, vmSymbols::signature(), is_static) // java_lang_String @@ -3413,12 +3413,17 @@ module->obj_field_put(_name_offset, value); } -ModuleEntry* java_lang_Module::module_entry(oop module) { +ModuleEntry* java_lang_Module::module_entry_raw(oop module) { assert(_module_entry_offset != 0, "Uninitialized module_entry_offset"); assert(module != NULL, "module can't be null"); assert(oopDesc::is_oop(module), "module must be oop"); ModuleEntry* module_entry = (ModuleEntry*)module->address_field(_module_entry_offset); + return module_entry; +} + +ModuleEntry* java_lang_Module::module_entry(oop module) { + ModuleEntry* module_entry = module_entry_raw(module); if (module_entry == NULL) { // If the inject field containing the ModuleEntry* is null then return the // class loader's unnamed module. @@ -4842,7 +4847,6 @@ Klass* klass = obj->klass(); if (klass == SystemDictionary::ClassLoader_klass() || // ClassLoader::loader_data is malloc'ed. - klass == SystemDictionary::Module_klass() || // Module::module_entry is malloc'ed // The next 3 classes are used to implement java.lang.invoke, and are not used directly in // regular Java code. The implementation of java.lang.invoke uses generated anonymoys classes // (e.g., as referenced by ResolvedMethodName::vmholder) that are not yet supported by CDS. --- old/src/hotspot/share/classfile/javaClasses.hpp 2020-07-22 12:00:28.311677704 -0700 +++ new/src/hotspot/share/classfile/javaClasses.hpp 2020-07-22 12:00:28.131670929 -0700 @@ -796,6 +796,7 @@ static void set_name(oop module, oop value); static ModuleEntry* module_entry(oop module); + static ModuleEntry* module_entry_raw(oop module); static void set_module_entry(oop module, ModuleEntry* module_entry); friend class JavaClasses; @@ -1707,6 +1708,12 @@ static void serialize_offsets(SerializeClosure* soc) NOT_CDS_RETURN; static InjectedField* get_injected(Symbol* class_name, int* field_count); static bool is_supported_for_archiving(oop obj) NOT_CDS_JAVA_HEAP_RETURN_(false); + static void compute_offset(int &dest_offset, + InstanceKlass* ik, Symbol* name_symbol, Symbol* signature_symbol, + bool is_static = false); + static void compute_offset(int& dest_offset, InstanceKlass* ik, + const char* name_string, Symbol* signature_symbol, + bool is_static = false); }; #undef DECLARE_INJECTED_FIELD_ENUM --- old/src/hotspot/share/classfile/moduleEntry.cpp 2020-07-22 12:00:28.899699839 -0700 +++ new/src/hotspot/share/classfile/moduleEntry.cpp 2020-07-22 12:00:28.715692913 -0700 @@ -29,7 +29,10 @@ #include "classfile/javaClasses.inline.hpp" #include "classfile/moduleEntry.hpp" #include "logging/log.hpp" +#include "memory/archiveUtils.hpp" #include "memory/filemap.hpp" +#include "memory/heapShared.hpp" +#include "memory/metaspaceShared.hpp" #include "memory/resourceArea.hpp" #include "memory/universe.hpp" #include "oops/oopHandle.inline.hpp" @@ -40,6 +43,7 @@ #include "utilities/growableArray.hpp" #include "utilities/hashtable.inline.hpp" #include "utilities/ostream.hpp" +#include "utilities/resourceHash.hpp" ModuleEntry* ModuleEntryTable::_javabase_module = NULL; @@ -108,15 +112,15 @@ // Returns the shared ProtectionDomain oop ModuleEntry::shared_protection_domain() { - return _pd.resolve(); + return _shared_pd.resolve(); } // Set the shared ProtectionDomain atomically void ModuleEntry::set_shared_protection_domain(ClassLoaderData *loader_data, Handle pd_h) { // Create a handle for the shared ProtectionDomain and save it atomically. - // init_handle_locked checks if someone beats us setting the _pd cache. - loader_data->init_handle_locked(_pd, pd_h); + // init_handle_locked checks if someone beats us setting the _shared_pd cache. + loader_data->init_handle_locked(_shared_pd, pd_h); } // Returns true if this module can read module m @@ -362,6 +366,188 @@ assert(new_entry_free_list() == NULL, "entry present on ModuleEntryTable's free list"); } +#if INCLUDE_CDS_JAVA_HEAP +typedef ResourceHashtable< + const ModuleEntry*, + ModuleEntry*, + primitive_hash, + primitive_equals, + 557, // prime number + ResourceObj::C_HEAP> ArchivedModuleEntries; +static ArchivedModuleEntries* _archive_modules_entries = NULL; + +ModuleEntry* ModuleEntry::allocate_archived_entry() const { + assert(is_named(), "unnamed packages/modules are not archived"); + ModuleEntry* archived_entry = (ModuleEntry*)MetaspaceShared::read_write_space_alloc(sizeof(ModuleEntry)); + memcpy((void*)archived_entry, (void*)this, sizeof(ModuleEntry)); + + if (_archive_modules_entries == NULL) { + _archive_modules_entries = new (ResourceObj::C_HEAP, mtClass)ArchivedModuleEntries(); + } + assert(_archive_modules_entries->get(this) == NULL, "Each ModuleEntry must not be shared across ModuleEntryTables"); + _archive_modules_entries->put(this, archived_entry); + + return archived_entry; +} + +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"); + return *ptr; +} + +// GrowableArrays cannot be directly archived, as they need to be expandable at runtime. +// Write it out as an Array, and conver it back to GrowableArray at runtime. +Array* ModuleEntry::write_archived_entry_array(GrowableArray* array) { + Array* archived_array = NULL; + int length = (array == NULL) ? 0 : array->length(); + if (length > 0) { + archived_array = MetaspaceShared::new_ro_array(length); + for (int i = 0; i < length; i++) { + ModuleEntry* archived_entry = get_archived_entry(array->at(i)); + archived_array->at_put(i, archived_entry); + ArchivePtrMarker::mark_pointer((address*)archived_array->adr_at(i)); + } + } + + return archived_array; +} + +GrowableArray* ModuleEntry::read_archived_entry_array(Array* archived_array) { + GrowableArray* array = NULL; + int length = (archived_array == NULL) ? 0 : archived_array->length(); + if (length > 0) { + array = new (ResourceObj::C_HEAP, mtModule)GrowableArray(length, mtModule); + for (int i = 0; i < length; i++) { + ModuleEntry* archived_entry = archived_array->at(i); + array->append(archived_entry); + } + } + + return array; +} + +void ModuleEntry::init_as_archived_entry() { + Array* archived_reads = write_archived_entry_array(_reads); + + set_next(NULL); + set_hash(0x0); // re-init at runtime + _loader_data = NULL; // re-init at runtime + _shared_path_index = FileMapInfo::get_module_shared_path_index(_location); + if (literal() != NULL) { + set_literal(MetaspaceShared::get_relocated_symbol(literal())); + ArchivePtrMarker::mark_pointer((address*)literal_addr()); + } + _reads = (GrowableArray*)archived_reads; + if (_version != NULL) { + _version = MetaspaceShared::get_relocated_symbol(_version); + } + if (_location != NULL) { + _location = MetaspaceShared::get_relocated_symbol(_location); + } + + ArchivePtrMarker::mark_pointer((address*)&_reads); + ArchivePtrMarker::mark_pointer((address*)&_version); + ArchivePtrMarker::mark_pointer((address*)&_location); +} + +void ModuleEntry::init_archived_oops() { + 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_narrow_oop = CompressedOops::encode(m); + } + 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; +} + +void ModuleEntry::load_from_archive(ClassLoaderData* loader_data) { + set_loader_data(loader_data); + _reads = read_archived_entry_array((Array*)_reads); + JFR_ONLY(INIT_ID(this);) +} + +void ModuleEntry::restore_archive_oops(ClassLoaderData* loader_data) { + Handle module_handle(Thread::current(), HeapShared::materialize_archived_object(_archived_module_narrow_oop)); + assert(module_handle.not_null(), "huh"); + set_module(loader_data->add_handle(module_handle)); + + // This was cleared to zero during dump time -- we didn't save the value + // 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()); + } +} + +static int compare_module_by_name(ModuleEntry** a, ModuleEntry** b) { + return a[0]->name()->fast_compare(b[0]->name()); +} + +Array* ModuleEntryTable::allocate_archived_entries() { + Array* archived_modules = MetaspaceShared::new_rw_array(number_of_entries()); + int n = 0; + for (int i = 0; i < table_size(); ++i) { + for (ModuleEntry* m = bucket(i); m != NULL; m = m->next()) { + archived_modules->at_put(n++, m); + } + } + if (n > 0) { + // Always allocate in the same order to produce deterministic archive. + qsort(archived_modules->adr_at(0), n, sizeof(ModuleEntry*), (_sort_Fn)compare_module_by_name); + for (int i = 0; i < n; i++) { + archived_modules->at_put(i, archived_modules->at(i)->allocate_archived_entry()); + ArchivePtrMarker::mark_pointer((address*)archived_modules->adr_at(i)); + } + } + return archived_modules; +} + +void ModuleEntryTable::init_archived_entries(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_as_archived_entry(); + } +} + +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"); + + MutexLocker m1(Module_lock); + for (int i = 0; i < archived_modules->length(); i++) { + ModuleEntry* archived_entry = archived_modules->at(i); + archived_entry->load_from_archive(loader_data); + + unsigned int hash = compute_hash(archived_entry->name()); + archived_entry->set_hash(hash); + add_entry(hash_to_index(hash), archived_entry); + } +} + +void ModuleEntryTable::restore_archived_oops(ClassLoaderData* loader_data, Array* archived_modules) { + assert(UseSharedSpaces, "runtime only"); + for (int i = 0; i < archived_modules->length(); i++) { + ModuleEntry* archived_entry = archived_modules->at(i); + archived_entry->restore_archive_oops(loader_data); + } +} +#endif // INCLUDE_CDS_JAVA_HEAP + ModuleEntry* ModuleEntryTable::new_entry(unsigned int hash, Handle module_handle, bool is_open, Symbol* name, Symbol* version, Symbol* location, --- old/src/hotspot/share/classfile/moduleEntry.hpp 2020-07-22 12:00:29.479721672 -0700 +++ new/src/hotspot/share/classfile/moduleEntry.hpp 2020-07-22 12:00:29.303715047 -0700 @@ -47,6 +47,7 @@ #define JAVA_BASE_NAME "java.base" #define JAVA_BASE_NAME_LEN 9 +template class Array; class ModuleClosure; // A ModuleEntry describes a module that has been defined by a call to JVM_DefineModule. @@ -63,7 +64,7 @@ class ModuleEntry : public HashtableEntry { private: OopHandle _module; // java.lang.Module - OopHandle _pd; // java.security.ProtectionDomain, cached + OopHandle _shared_pd; // java.security.ProtectionDomain, cached // for shared classes from this module ClassLoaderData* _loader_data; GrowableArray* _reads; // list of modules that are readable by this module @@ -75,13 +76,15 @@ bool _must_walk_reads; // walk module's reads list at GC safepoints to purge out dead modules bool _is_open; // whether the packages in the module are all unqualifiedly exported bool _is_patched; // whether the module is patched via --patch-module + CDS_JAVA_HEAP_ONLY(narrowOop _archived_module_narrow_oop;) + JFR_ONLY(DEFINE_TRACE_ID_FIELD;) enum {MODULE_READS_SIZE = 101}; // Initial size of list of modules that the module can read. public: void init() { _module = OopHandle(); - _pd = OopHandle(); + _shared_pd = OopHandle(); _loader_data = NULL; _reads = NULL; _version = NULL; @@ -188,6 +191,17 @@ CDS_ONLY(int shared_path_index() { return _shared_path_index;}) JFR_ONLY(DEFINE_TRACE_ID_METHODS;) + +#if INCLUDE_CDS_JAVA_HEAP + ModuleEntry* allocate_archived_entry() const; + void init_as_archived_entry(); + void init_archived_oops(); + static ModuleEntry* get_archived_entry(ModuleEntry* orig_entry); + static Array* write_archived_entry_array(GrowableArray* array); + static GrowableArray* read_archived_entry_array(Array* archived_array); + void load_from_archive(ClassLoaderData* loader_data); + void restore_archive_oops(ClassLoaderData* loader_data); +#endif }; // Iterator interface @@ -270,6 +284,16 @@ void print(outputStream* st = tty); void verify(); + +#if INCLUDE_CDS_JAVA_HEAP + 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, + Array* archived_modules); +#endif }; #endif // SHARE_CLASSFILE_MODULEENTRY_HPP --- old/src/hotspot/share/classfile/packageEntry.cpp 2020-07-22 12:00:30.055743354 -0700 +++ new/src/hotspot/share/classfile/packageEntry.cpp 2020-07-22 12:00:29.871736428 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,13 +26,17 @@ #include "classfile/moduleEntry.hpp" #include "classfile/packageEntry.hpp" #include "logging/log.hpp" +#include "memory/archiveUtils.hpp" +#include "memory/metaspaceShared.hpp" #include "memory/resourceArea.hpp" +#include "oops/array.hpp" #include "oops/symbol.hpp" #include "runtime/handles.inline.hpp" #include "utilities/events.hpp" #include "utilities/growableArray.hpp" #include "utilities/hashtable.inline.hpp" #include "utilities/ostream.hpp" +#include "utilities/resourceHash.hpp" // Returns true if this package specifies m as a qualified export, including through an unnamed export bool PackageEntry::is_qexported_to(ModuleEntry* m) const { @@ -188,6 +192,114 @@ assert(new_entry_free_list() == NULL, "entry present on PackageEntryTable's free list"); } +#if INCLUDE_CDS_JAVA_HEAP +typedef ResourceHashtable< + const PackageEntry*, + PackageEntry*, + primitive_hash, + primitive_equals, + 557, // prime number + ResourceObj::C_HEAP> ArchivedPackageEntries; +static ArchivedPackageEntries* _archived_packages_entries = NULL; + +PackageEntry* PackageEntry::allocate_archived_entry() const { + assert(!in_unnamed_module(), "unnamed packages/modules are not archived"); + PackageEntry* archived_entry = (PackageEntry*)MetaspaceShared::read_write_space_alloc(sizeof(PackageEntry)); + memcpy((void*)archived_entry, (void*)this, sizeof(PackageEntry)); + + if (_archived_packages_entries == NULL) { + _archived_packages_entries = new (ResourceObj::C_HEAP, mtClass)ArchivedPackageEntries(); + } + assert(_archived_packages_entries->get(this) == NULL, "Each PackageEntry must not be shared across PackageEntryTables"); + _archived_packages_entries->put(this, archived_entry); + + return archived_entry; +} + +PackageEntry* PackageEntry::get_archived_entry(PackageEntry* orig_entry) { + PackageEntry** ptr = _archived_packages_entries->get(orig_entry); + assert(ptr != NULL && *ptr != NULL, "must have been allocated"); + return *ptr; +} + +void PackageEntry::init_as_archived_entry() { + Array* archived_qualified_exports = ModuleEntry::write_archived_entry_array(_qualified_exports); + + set_next(NULL); + set_literal(MetaspaceShared::get_relocated_symbol(literal())); + set_hash(0x0); // re-init at runtime + _module = ModuleEntry::get_archived_entry(_module); + _qualified_exports = (GrowableArray*)archived_qualified_exports; + _defined_by_cds_in_class_path = 0; + + ArchivePtrMarker::mark_pointer((address*)literal_addr()); + ArchivePtrMarker::mark_pointer((address*)&_module); + ArchivePtrMarker::mark_pointer((address*)&_qualified_exports); +} + +void PackageEntry::load_from_archive() { + _qualified_exports = ModuleEntry::read_archived_entry_array((Array*)_qualified_exports); + JFR_ONLY(INIT_ID(this);) +} + +static int compare_package_by_name(PackageEntry** a, PackageEntry** b) { + return a[0]->name()->fast_compare(b[0]->name()); +} + +Array* PackageEntryTable::allocate_archived_entries() { + // First count the packages in named modules + int n, i; + for (n = 0, i = 0; i < table_size(); ++i) { + for (PackageEntry* p = bucket(i); p != NULL; p = p->next()) { + if (p->module()->name() != NULL) { + n++; + } + } + } + + Array* archived_packages = MetaspaceShared::new_rw_array(n); + for (n = 0, i = 0; i < table_size(); ++i) { + for (PackageEntry* p = bucket(i); p != NULL; p = p->next()) { + if (p->module()->name() != NULL) { + // We don't archive unnamed modules, or packages in unnamed modules. They will be + // created on-demand at runtime as classes in such packages are loaded. + archived_packages->at_put(n++, p); + } + } + } + if (n > 0) { + qsort(archived_packages->adr_at(0), n, sizeof(PackageEntry*), (_sort_Fn)compare_package_by_name); + for (i = 0; i < n; i++) { + archived_packages->at_put(i, archived_packages->at(i)->allocate_archived_entry()); + ArchivePtrMarker::mark_pointer((address*)archived_packages->adr_at(i)); + } + } + return archived_packages; +} + +void PackageEntryTable::init_archived_entries(Array* archived_packages) { + for (int i = 0; i < archived_packages->length(); i++) { + PackageEntry* archived_entry = archived_packages->at(i); + archived_entry->init_as_archived_entry(); + } +} + +void PackageEntryTable::load_archived_entries(Array* archived_packages) { + assert(UseSharedSpaces, "runtime only"); + + MutexLocker m1(Module_lock); + for (int i = 0; i < archived_packages->length(); i++) { + PackageEntry* archived_entry = archived_packages->at(i); + archived_entry->load_from_archive(); + + unsigned int hash = compute_hash(archived_entry->name()); + archived_entry->set_hash(hash); + add_entry(hash_to_index(hash), archived_entry); + } +} + +#endif + PackageEntry* PackageEntryTable::new_entry(unsigned int hash, Symbol* name, ModuleEntry* module) { assert(Module_lock->owned_by_self(), "should have the Module_lock"); PackageEntry* entry = (PackageEntry*)Hashtable::allocate_new_entry(hash, name); --- old/src/hotspot/share/classfile/packageEntry.hpp 2020-07-22 12:00:30.631765037 -0700 +++ new/src/hotspot/share/classfile/packageEntry.hpp 2020-07-22 12:00:30.451758261 -0700 @@ -36,6 +36,7 @@ #include "jfr/support/jfrTraceIdExtension.hpp" #endif +template class Array; // A PackageEntry basically represents a Java package. It contains: // - Symbol* containing the package's name. @@ -217,6 +218,13 @@ void print(outputStream* st = tty); void verify(); +#if INCLUDE_CDS_JAVA_HEAP + PackageEntry* allocate_archived_entry() const; + void init_as_archived_entry(); + static PackageEntry* get_archived_entry(PackageEntry* orig_entry); + void load_from_archive(); +#endif + static int max_index_for_defined_in_class_path() { return sizeof(int) * BitsPerByte; } @@ -295,6 +303,12 @@ void print(outputStream* st = tty); void verify(); + +#if INCLUDE_CDS_JAVA_HEAP + Array* allocate_archived_entries(); + void init_archived_entries(Array* archived_packages); + void load_archived_entries(Array* archived_packages); +#endif }; #endif // SHARE_CLASSFILE_PACKAGEENTRY_HPP --- old/src/hotspot/share/classfile/systemDictionary.cpp 2020-07-22 12:00:31.207786719 -0700 +++ new/src/hotspot/share/classfile/systemDictionary.cpp 2020-07-22 12:00:31.023779793 -0700 @@ -184,6 +184,16 @@ CHECK); _java_platform_loader = OopHandle(Universe::vm_global(), (oop)result.get_jobject()); + +#if INCLUDE_CDS_JAVA_HEAP + if (UseSharedSpaces && MetaspaceShared::use_full_module_graph()) { + Handle platform_loader(THREAD, java_platform_loader()); + register_loader(platform_loader)->restore_java_platform_loader_from_archive(); + + Handle system_loader(THREAD, java_system_loader()); + register_loader(system_loader)->restore_java_system_loader_from_archive(); + } +#endif } ClassLoaderData* SystemDictionary::register_loader(Handle class_loader, bool create_mirror_cld) { @@ -2121,6 +2131,7 @@ // call. No mirror objects are accessed/restored in the above call. // Mirrors are restored after java.lang.Class is loaded. HeapShared::fixup_mapped_heap_regions(); + CDS_JAVA_HEAP_ONLY(ClassLoaderData::restore_archived_oops_for_null_class_loader_data()); // Initialize the constant pool for the Object_class assert(Object_klass()->is_shared(), "must be"); --- old/src/hotspot/share/classfile/systemDictionary.hpp 2020-07-22 12:00:31.811809456 -0700 +++ new/src/hotspot/share/classfile/systemDictionary.hpp 2020-07-22 12:00:31.623802378 -0700 @@ -235,10 +235,13 @@ do_klass(ByteArrayInputStream_klass, java_io_ByteArrayInputStream ) \ do_klass(URL_klass, java_net_URL ) \ do_klass(Jar_Manifest_klass, java_util_jar_Manifest ) \ + do_klass(jdk_internal_loader_BuiltinClassLoader_klass,jdk_internal_loader_BuiltinClassLoader ) \ do_klass(jdk_internal_loader_ClassLoaders_klass, jdk_internal_loader_ClassLoaders ) \ do_klass(jdk_internal_loader_ClassLoaders_AppClassLoader_klass, jdk_internal_loader_ClassLoaders_AppClassLoader) \ do_klass(jdk_internal_loader_ClassLoaders_PlatformClassLoader_klass, jdk_internal_loader_ClassLoaders_PlatformClassLoader) \ do_klass(CodeSource_klass, java_security_CodeSource ) \ + do_klass(ConcurrentHashMap_klass, java_util_concurrent_ConcurrentHashMap ) \ + do_klass(ArrayList_klass, java_util_ArrayList ) \ \ do_klass(StackTraceElement_klass, java_lang_StackTraceElement ) \ \ --- old/src/hotspot/share/classfile/vmSymbols.hpp 2020-07-22 12:00:32.419832343 -0700 +++ new/src/hotspot/share/classfile/vmSymbols.hpp 2020-07-22 12:00:32.239825567 -0700 @@ -130,6 +130,7 @@ template(java_lang_Record, "java/lang/Record") \ \ template(jdk_internal_loader_NativeLibraries, "jdk/internal/loader/NativeLibraries") \ + template(jdk_internal_loader_BuiltinClassLoader, "jdk/internal/loader/BuiltinClassLoader") \ template(jdk_internal_loader_ClassLoaders_AppClassLoader, "jdk/internal/loader/ClassLoaders$AppClassLoader") \ template(jdk_internal_loader_ClassLoaders_PlatformClassLoader, "jdk/internal/loader/ClassLoaders$PlatformClassLoader") \ \ @@ -664,6 +665,8 @@ \ /* cds */ \ template(jdk_internal_loader_ClassLoaders, "jdk/internal/loader/ClassLoaders") \ + template(java_util_concurrent_ConcurrentHashMap, "java/util/concurrent/ConcurrentHashMap") \ + template(java_util_ArrayList, "java/util/ArrayList") \ template(toFileURL_name, "toFileURL") \ template(toFileURL_signature, "(Ljava/lang/String;)Ljava/net/URL;") \ template(url_void_signature, "(Ljava/net/URL;)V") \ --- old/src/hotspot/share/memory/filemap.cpp 2020-07-22 12:00:33.011854628 -0700 +++ new/src/hotspot/share/memory/filemap.cpp 2020-07-22 12:00:32.831847852 -0700 @@ -218,6 +218,7 @@ _max_heap_size = MaxHeapSize; _narrow_klass_shift = CompressedKlassPointers::shift(); _use_optimized_module_handling = MetaspaceShared::use_optimized_module_handling(); + _use_full_module_graph = MetaspaceShared::use_full_module_graph(); // The following fields are for sanity checks for whether this archive // will function correctly with this JVM and the bootclasspath it's @@ -2179,7 +2180,12 @@ if (!_use_optimized_module_handling) { MetaspaceShared::disable_optimized_module_handling(); - log_info(cds)("use_optimized_module_handling disabled: archive was created without optimized module handling"); + log_info(cds)("optimized module handling: disabled because archive was created without optimized module handling"); + } + + if (!_use_full_module_graph) { + MetaspaceShared::disable_full_module_graph(); + log_info(cds)("full module graph: disabled because archive was created without full module graph"); } return true; --- old/src/hotspot/share/memory/filemap.hpp 2020-07-22 12:00:33.623877665 -0700 +++ new/src/hotspot/share/memory/filemap.hpp 2020-07-22 12:00:33.439870739 -0700 @@ -234,6 +234,7 @@ bool _allow_archiving_with_java_agent; // setting of the AllowArchivingWithJavaAgent option bool _use_optimized_module_handling;// No module-relation VM options were specified, so we can skip // some expensive operations. + bool _use_full_module_graph; // Can we use the full archived modue graph? size_t _ptrmap_size_in_bits; // Size of pointer relocation bitmap char* from_mapped_offset(size_t offset) const { --- old/src/hotspot/share/memory/heapShared.cpp 2020-07-22 12:00:34.199899348 -0700 +++ new/src/hotspot/share/memory/heapShared.cpp 2020-07-22 12:00:34.019892572 -0700 @@ -24,8 +24,10 @@ #include "precompiled.hpp" #include "classfile/javaClasses.inline.hpp" +#include "classfile/moduleEntry.hpp" #include "classfile/stringTable.hpp" #include "classfile/symbolTable.hpp" +#include "classfile/systemDictionary.hpp" #include "classfile/systemDictionaryShared.hpp" #include "classfile/vmSymbols.hpp" #include "gc/shared/gcLocker.hpp" @@ -45,6 +47,7 @@ #include "oops/fieldStreams.inline.hpp" #include "oops/oop.inline.hpp" #include "runtime/fieldDescriptor.inline.hpp" +#include "runtime/javaCalls.hpp" #include "runtime/safepointVerifiers.hpp" #include "utilities/bitMap.inline.hpp" #if INCLUDE_G1GC @@ -68,20 +71,25 @@ // region. Warning: Objects in the subgraphs should not have reference fields // assigned at runtime. static ArchivableStaticFieldInfo closed_archive_subgraph_entry_fields[] = { - {"java/lang/Integer$IntegerCache", "archivedCache"}, - {"java/lang/Long$LongCache", "archivedCache"}, - {"java/lang/Byte$ByteCache", "archivedCache"}, - {"java/lang/Short$ShortCache", "archivedCache"}, - {"java/lang/Character$CharacterCache", "archivedCache"}, - {"java/util/jar/Attributes$Name", "KNOWN_NAMES"}, - {"sun/util/locale/BaseLocale", "constantBaseLocales"}, + {"java/lang/Integer$IntegerCache", 0, "archivedCache"}, + {"java/lang/Long$LongCache", 0, "archivedCache"}, + {"java/lang/Byte$ByteCache", 0, "archivedCache"}, + {"java/lang/Short$ShortCache", 0, "archivedCache"}, + {"java/lang/Character$CharacterCache", 0, "archivedCache"}, + {"java/util/jar/Attributes$Name", 0, "KNOWN_NAMES"}, + {"sun/util/locale/BaseLocale", 0, "constantBaseLocales"}, }; // Entry fields for subgraphs archived in the open archive heap region. static ArchivableStaticFieldInfo open_archive_subgraph_entry_fields[] = { - {"jdk/internal/module/ArchivedModuleGraph", "archivedModuleGraph"}, - {"java/util/ImmutableCollections", "archivedObjects"}, - {"java/lang/module/Configuration", "EMPTY_CONFIGURATION"}, - {"jdk/internal/math/FDBigInteger", "archivedCaches"}, + {"jdk/internal/loader/BuiltinClassLoader$ArchivedData", 1, "packageToModule"}, + {"jdk/internal/loader/BootLoader$ArchivedData", 1, "servicesCatalog"}, + {"jdk/internal/loader/ClassLoaders$ArchivedData", 1, "singleton"}, + {"jdk/internal/module/ModuleBootstrap$ArchivedBootLayer", 1, "archivedBootLayer"}, + {"jdk/internal/module/ArchivedModuleGraph", 0, "archivedModuleGraph"}, + {"java/util/ImmutableCollections", 0, "archivedObjects"}, + {"java/lang/Module$ArchivedData", 1, "singleton"}, + {"java/lang/module/Configuration", 0, "EMPTY_CONFIGURATION"}, + {"jdk/internal/math/FDBigInteger", 0, "archivedCaches"}, }; const static int num_closed_archive_subgraph_entry_fields = @@ -108,6 +116,35 @@ return hash; } +static void reset_states(oop obj, TRAPS) { + Handle h_obj(THREAD, obj); + InstanceKlass* klass = InstanceKlass::cast(obj->klass()); + TempNewSymbol method_name = SymbolTable::new_symbol("resetArchivedStates"); + Symbol* method_sig = vmSymbols::void_method_signature(); + + while (klass != NULL) { + Method* method = klass->find_method(method_name, method_sig); + if (method != NULL) { + assert(method->is_private(), "must be"); + if (log_is_enabled(Debug, cds)) { + ResourceMark rm(THREAD); + log_debug(cds)(" calling %s", method->name_and_sig_as_C_string()); + } + JavaValue result(T_VOID); + JavaCalls::call_special(&result, h_obj, klass, + method_name, method_sig, CHECK); + } + klass = klass->java_super(); + } +} + +void HeapShared::reset_archived_object_states(TRAPS) { + log_debug(cds)("Resetting platform loader"); + reset_states(SystemDictionary::java_platform_loader(), THREAD); + log_debug(cds)("Resetting system loader"); + reset_states(SystemDictionary::java_system_loader(), THREAD); +} + HeapShared::ArchivedObjectCache* HeapShared::_archived_object_cache = NULL; oop HeapShared::find_archived_heap_object(oop obj) { assert(DumpSharedSpaces, "dump-time only"); @@ -233,6 +270,8 @@ log_info(cds)("Dumping objects to open archive heap region ..."); copy_open_archive_heap_objects(open); + ClassLoaderData::init_archived_oops(); + destroy_archived_object_cache(); } @@ -465,6 +504,15 @@ } assert(!DumpSharedSpaces, "Should not be called with DumpSharedSpaces"); + if (!MetaspaceShared::use_full_module_graph()) { + for (int i = 0; i < num_open_archive_subgraph_entry_fields; i++) { + const ArchivableStaticFieldInfo* info = &open_archive_subgraph_entry_fields[i]; + if (info->full_module_graph_only && k->name()->equals(info->klass_name)) { + return; + } + } + } + unsigned int hash = SystemDictionaryShared::hash_for_shared_dictionary(k); const ArchivedKlassSubGraphInfoRecord* record = _run_time_subgraph_info_table.lookup(k, hash, 0); @@ -623,6 +671,25 @@ } } +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. @@ -690,6 +757,21 @@ vm_exit(1); } } + + if (java_lang_Module::is_instance(orig_obj)) { + check_module_oop(orig_obj); + } + + if (java_lang_Module::is_instance(orig_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() || + orig_obj == SystemDictionary::java_system_loader() || + java_lang_ClassLoader::loader_data_raw(orig_obj) == NULL, "must be"); + java_lang_ClassLoader::release_set_loader_data(archived_obj, NULL); + } } assert(archived_obj != NULL, "must be"); @@ -986,9 +1068,12 @@ if (f->klass_name != klass_name) { break; } - archive_reachable_objects_from_static_field(f->klass, f->klass_name, - f->offset, f->field_name, - is_closed_archive, CHECK); + + if (!info->full_module_graph_only || MetaspaceShared::use_full_module_graph()) { + archive_reachable_objects_from_static_field(f->klass, f->klass_name, + f->offset, f->field_name, + is_closed_archive, CHECK); + } } done_recording_subgraph(info->klass, klass_name); } --- old/src/hotspot/share/memory/heapShared.hpp 2020-07-22 12:00:34.791921632 -0700 +++ new/src/hotspot/share/memory/heapShared.hpp 2020-07-22 12:00:34.607914706 -0700 @@ -40,7 +40,8 @@ #if INCLUDE_CDS_JAVA_HEAP struct ArchivableStaticFieldInfo { const char* klass_name; - const char* field_name; + int full_module_graph_only; + const char* field_name; InstanceKlass* klass; int offset; BasicType type; @@ -236,7 +237,10 @@ 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); + public: + static void reset_archived_object_states(TRAPS); static void create_archived_object_cache() { _archived_object_cache = new (ResourceObj::C_HEAP, mtClass)ArchivedObjectCache(); --- old/src/hotspot/share/memory/metaspaceShared.cpp 2020-07-22 12:00:35.363943164 -0700 +++ new/src/hotspot/share/memory/metaspaceShared.cpp 2020-07-22 12:00:35.183936388 -0700 @@ -90,6 +90,7 @@ intx MetaspaceShared::_relocation_delta; char* MetaspaceShared::_requested_base_address; bool MetaspaceShared::_use_optimized_module_handling = true; +bool MetaspaceShared::_use_full_module_graph = true; // The CDS archive is divided into the following regions: // mc - misc code (the method entry trampolines, c++ vtables) @@ -240,6 +241,10 @@ return _ro_region.allocate(num_bytes); } +char* MetaspaceShared::read_write_space_alloc(size_t num_bytes) { + return _rw_region.allocate(num_bytes); +} + size_t MetaspaceShared::reserved_space_alignment() { return os::vm_allocation_granularity(); } static bool shared_base_valid(char* shared_base) { @@ -589,6 +594,8 @@ serialize_cloned_cpp_vtptrs(soc); soc->do_tag(--tag); + CDS_JAVA_HEAP_ONLY(ClassLoaderData::serialize(soc)); + soc->do_tag(666); } @@ -1096,6 +1103,7 @@ f(SymbolBucket) \ f(StringHashentry) \ f(StringBucket) \ + f(ModulesNatives) \ f(Other) enum Type { @@ -1131,6 +1139,11 @@ _bytes [which][type] += byte_size; } + void record_modules(int byte_size, bool read_only) { + int which = (read_only) ? RO : RW; + _bytes [which][ModulesNativesType] += byte_size; + } + void record_other_type(int byte_size, bool read_only) { int which = (read_only) ? RO : RW; _bytes [which][OtherType] += byte_size; @@ -1420,6 +1433,13 @@ ResourceMark rm; ShallowCopier rw_copier(false); iterate_roots(&rw_copier); + +#if INCLUDE_CDS_JAVA_HEAP + // Archive the ModuleEntry's and PackageEntry's of the 3 built-in loaders + char* start = _rw_region.top(); + ClassLoaderData::allocate_archived_tables(); + ArchiveCompactor::alloc_stats()->record_modules(_rw_region.top() - start, /*read_only*/false); +#endif } { // allocate and shallow-copy of RO object, immediately following the RW region @@ -1429,6 +1449,11 @@ ResourceMark rm; ShallowCopier ro_copier(true); iterate_roots(&ro_copier); +#if INCLUDE_CDS_JAVA_HEAP + char* start = _ro_region.top(); + ClassLoaderData::init_archived_tables(); + ArchiveCompactor::alloc_stats()->record_modules(_ro_region.top() - start, /*read_only*/true); +#endif } { log_info(cds)("Relocating embedded pointers ... "); @@ -1523,6 +1548,13 @@ assert(klass->is_klass(), "must be"); return klass; } + + static Symbol* get_relocated_symbol(Symbol* orig_symbol) { + assert(DumpSharedSpaces, "dump time only"); + address* pp = _new_loc_table->lookup((address)orig_symbol); + assert(pp != NULL, "must be"); + return (Symbol*)(*pp); + } }; DumpAllocStats* ArchiveCompactor::_alloc_stats; @@ -1827,6 +1859,10 @@ return k; } +Symbol* MetaspaceShared::get_relocated_symbol(Symbol* orig_symbol) { + return ArchiveCompactor::get_relocated_symbol(orig_symbol); +} + class LinkSharedClassesClosure : public KlassClosure { Thread* THREAD; bool _made_progress; @@ -1941,6 +1977,12 @@ link_and_cleanup_shared_classes(CATCH); log_info(cds)("Rewriting and linking classes: done"); +#if INCLUDE_CDS_JAVA_HEAP + if (use_full_module_graph()) { + HeapShared::reset_archived_object_states(THREAD); + } +#endif + VM_PopulateDumpSharedSpace op; MutexLocker ml(THREAD, HeapShared::is_heap_object_archiving_allowed() ? Heap_lock : NULL); // needed by HeapShared::run_gc() @@ -2358,7 +2400,8 @@ static_mapinfo->map_heap_regions(); } }); - log_info(cds)("Using optimized module handling %s", MetaspaceShared::use_optimized_module_handling() ? "enabled" : "disabled"); + log_info(cds)("optimized module handling: %s", MetaspaceShared::use_optimized_module_handling() ? "enabled" : "disabled"); + log_info(cds)("full module graph: %s", MetaspaceShared::use_full_module_graph() ? "enabled" : "disabled"); } else { unmap_archive(static_mapinfo); unmap_archive(dynamic_mapinfo); @@ -2683,6 +2726,11 @@ - intx(SharedBaseAddress); // .. but the base archive is mapped at here at dump time } +bool MetaspaceShared::use_full_module_graph() { + return _use_optimized_module_handling && _use_full_module_graph && + (UseSharedSpaces || DumpSharedSpaces) && HeapShared::is_heap_object_archiving_allowed(); +} + void MetaspaceShared::print_on(outputStream* st) { if (UseSharedSpaces || DumpSharedSpaces) { st->print("CDS archive(s) mapped at: "); @@ -2705,8 +2753,3 @@ } st->cr(); } - - - - - --- old/src/hotspot/share/memory/metaspaceShared.hpp 2020-07-22 12:00:35.967965901 -0700 +++ new/src/hotspot/share/memory/metaspaceShared.hpp 2020-07-22 12:00:35.787959125 -0700 @@ -185,6 +185,7 @@ static intx _relocation_delta; static char* _requested_base_address; static bool _use_optimized_module_handling; + static bool _use_full_module_graph; public: enum { // core archive spaces @@ -322,17 +323,23 @@ // Allocate a block of memory from the "mc" or "ro" regions. static char* misc_code_space_alloc(size_t num_bytes); static char* read_only_space_alloc(size_t num_bytes); + static char* read_write_space_alloc(size_t num_bytes); template static Array* new_ro_array(int length) { -#if INCLUDE_CDS size_t byte_size = Array::byte_sizeof(length, sizeof(T)); Array* array = (Array*)read_only_space_alloc(byte_size); array->initialize(length); return array; -#else + } + + template + static Array* new_rw_array(int length) { + size_t byte_size = Array::byte_sizeof(length, sizeof(T)); + Array* array = (Array*)read_write_space_alloc(byte_size); + array->initialize(length); + return array; return NULL; -#endif } template @@ -352,6 +359,7 @@ static void relocate_klass_ptr(oop o); static Klass* get_relocated_klass(Klass *k, bool is_final=false); + static Symbol* get_relocated_symbol(Symbol* orig_symbol); static void allocate_cloned_cpp_vtptrs(); static intptr_t* get_archived_cpp_vtable(MetaspaceObj::Type msotype, address obj); @@ -377,9 +385,13 @@ GrowableArray* open_oopmaps); // Can we skip some expensive operations related to modules? - static bool use_optimized_module_handling() { return _use_optimized_module_handling; } + static bool use_optimized_module_handling() { return NOT_CDS(false) CDS_ONLY(_use_optimized_module_handling); } static void disable_optimized_module_handling() { _use_optimized_module_handling = false; } + // Can we use the full archived modue graph? + static bool use_full_module_graph() NOT_CDS_RETURN_(false); + static void disable_full_module_graph() { _use_full_module_graph = false; } + private: #if INCLUDE_CDS static void write_region(FileMapInfo* mapinfo, int region_idx, DumpRegion* dump_region, --- old/src/hotspot/share/oops/instanceKlass.cpp 2020-07-22 12:00:36.543987583 -0700 +++ new/src/hotspot/share/oops/instanceKlass.cpp 2020-07-22 12:00:36.359980657 -0700 @@ -47,6 +47,7 @@ #include "logging/logMessage.hpp" #include "logging/logStream.hpp" #include "memory/allocation.inline.hpp" +#include "memory/archiveUtils.hpp" #include "memory/iterator.inline.hpp" #include "memory/metadataFactory.hpp" #include "memory/metaspaceClosure.hpp" @@ -2541,7 +2542,7 @@ _oop_map_cache = NULL; // clear _nest_host to ensure re-load at runtime _nest_host = NULL; - _package_entry = NULL; + _package_entry = NULL; // TODO -- point it to the archived PackageEntry (JDK-8249262) _dep_context_last_cleaned = 0; } --- old/src/hotspot/share/oops/instanceKlass.hpp 2020-07-22 12:00:37.164010922 -0700 +++ new/src/hotspot/share/oops/instanceKlass.hpp 2020-07-22 12:00:36.976003845 -0700 @@ -1327,6 +1327,7 @@ virtual void remove_unshareable_info(); virtual void remove_java_mirror(); void restore_unshareable_info(ClassLoaderData* loader_data, Handle protection_domain, PackageEntry* pkg_entry, TRAPS); + void init_shared_package_entry(); // jvm support jint compute_modifier_flags(TRAPS) const; --- old/src/hotspot/share/runtime/arguments.cpp 2020-07-22 12:00:37.748032906 -0700 +++ new/src/hotspot/share/runtime/arguments.cpp 2020-07-22 12:00:37.564025979 -0700 @@ -1457,7 +1457,14 @@ if (is_internal_module_property(key) || strcmp(key, "jdk.module.main") == 0) { MetaspaceShared::disable_optimized_module_handling(); - log_info(cds)("Using optimized module handling disabled due to incompatible property: %s=%s", key, value); + log_info(cds)("optimized module handling: disabled due to incompatible property: %s=%s", key, value); + } + if (strcmp(key, "jdk.module.showModuleResolution") == 0 || + strcmp(key, "jdk.module.illegalAccess") == 0 || + strcmp(key, "jdk.module.validation") == 0 || + strcmp(key, "java.system.class.loader") == 0) { + MetaspaceShared::disable_full_module_graph(); + log_info(cds)("full module graph: disabled due to incompatible property: %s=%s", key, value); } #endif @@ -2506,7 +2513,7 @@ Arguments::append_sysclasspath(tail); #if INCLUDE_CDS MetaspaceShared::disable_optimized_module_handling(); - log_info(cds)("Using optimized module handling disabled due to bootclasspath was appended"); + log_info(cds)("optimized module handling: disabled due to bootclasspath was appended"); #endif // -bootclasspath/p: } else if (match_option(option, "-Xbootclasspath/p:", &tail)) { --- old/src/java.base/share/classes/java/lang/ClassLoader.java 2020-07-22 12:00:38.368056245 -0700 +++ new/src/java.base/share/classes/java/lang/ClassLoader.java 2020-07-22 12:00:38.184049318 -0700 @@ -2714,6 +2714,16 @@ offset = unsafe.objectFieldOffset(k, name); return unsafe.compareAndSetReference(this, offset, null, obj); } + + + // Called from VM only, during -Xshare:dump + private void resetArchivedStates() { + parallelLockMap.clear(); + packages.clear(); + package2certs.clear(); + classes.clear(); + classLoaderValueMap = null; + } } /* --- old/src/java.base/share/classes/java/lang/Module.java 2020-07-22 12:00:38.960078529 -0700 +++ new/src/java.base/share/classes/java/lang/Module.java 2020-07-22 12:00:38.780071753 -0700 @@ -55,6 +55,7 @@ import jdk.internal.loader.BuiltinClassLoader; import jdk.internal.loader.BootLoader; import jdk.internal.loader.ClassLoaders; +import jdk.internal.misc.VM; import jdk.internal.module.IllegalAccessLogger; import jdk.internal.module.ModuleLoaderMap; import jdk.internal.module.ServicesCatalog; @@ -246,12 +247,63 @@ // -- // special Module to mean "all unnamed modules" - private static final Module ALL_UNNAMED_MODULE = new Module(null); - private static final Set ALL_UNNAMED_MODULE_SET = Set.of(ALL_UNNAMED_MODULE); + private static final Module ALL_UNNAMED_MODULE; + private static final Set ALL_UNNAMED_MODULE_SET; // special Module to mean "everyone" - private static final Module EVERYONE_MODULE = new Module(null); - private static final Set EVERYONE_SET = Set.of(EVERYONE_MODULE); + private static final Module EVERYONE_MODULE; + private static final Set EVERYONE_SET; + + private static class ArchivedData { + private static ArchivedData singleton; + + private final Module ALL_UNNAMED_MODULE; + private final Set ALL_UNNAMED_MODULE_SET; + private final Module EVERYONE_MODULE; + private final Set EVERYONE_SET; + + public ArchivedData(Module ALL_UNNAMED_MODULE, + Set ALL_UNNAMED_MODULE_SET, + Module EVERYONE_MODULE, + Set EVERYONE_SET) { + this.ALL_UNNAMED_MODULE = ALL_UNNAMED_MODULE; + this.ALL_UNNAMED_MODULE_SET = ALL_UNNAMED_MODULE_SET; + this.EVERYONE_MODULE = EVERYONE_MODULE; + this.EVERYONE_SET = EVERYONE_SET; + } + + static void archive(ArchivedData archivedData) { + singleton = archivedData; + } + + static ArchivedData get() { + return singleton; + } + + static { + VM.initializeFromArchive(ArchivedData.class); + } + } + + static { + ArchivedData archivedData = ArchivedData.get(); + if (archivedData != null) { + ALL_UNNAMED_MODULE = archivedData.ALL_UNNAMED_MODULE; + ALL_UNNAMED_MODULE_SET = archivedData.ALL_UNNAMED_MODULE_SET; + EVERYONE_MODULE = archivedData.EVERYONE_MODULE; + EVERYONE_SET = archivedData.EVERYONE_SET; + } else { + ALL_UNNAMED_MODULE = new Module(null); + ALL_UNNAMED_MODULE_SET = Set.of(ALL_UNNAMED_MODULE); + EVERYONE_MODULE = new Module(null); + EVERYONE_SET = Set.of(EVERYONE_MODULE); + + ArchivedData.archive(new ArchivedData(ALL_UNNAMED_MODULE, + ALL_UNNAMED_MODULE_SET, + EVERYONE_MODULE, + EVERYONE_SET)); + } + } /** * The holder of data structures to support readability, exports, and --- old/src/java.base/share/classes/java/lang/System.java 2020-07-22 12:00:39.548100664 -0700 +++ new/src/java.base/share/classes/java/lang/System.java 2020-07-22 12:00:39.364093737 -0700 @@ -2248,6 +2248,9 @@ public ServicesCatalog getServicesCatalog(ModuleLayer layer) { return layer.getServicesCatalog(); } + public void bindToLoader(ModuleLayer layer, ClassLoader loader) { + layer.bindToLoader(loader); + } public Stream layers(ModuleLayer layer) { return layer.layers(); } --- old/src/java.base/share/classes/java/security/SecureClassLoader.java 2020-07-22 12:00:40.140122948 -0700 +++ new/src/java.base/share/classes/java/security/SecureClassLoader.java 2020-07-22 12:00:39.960116172 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, 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 @@ -269,4 +269,9 @@ return cs.matchCerts(csk.cs, true); } } + + // Called from VM only, during -Xshare:dump + private void resetArchivedStates() { + pdcache.clear(); + } } --- old/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java 2020-07-22 12:00:40.732145233 -0700 +++ new/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java 2020-07-22 12:00:40.544138155 -0700 @@ -257,6 +257,12 @@ ServicesCatalog getServicesCatalog(ModuleLayer layer); /** + * Record that this layer has at least one module defined to the given + * class loader. + */ + void bindToLoader(ModuleLayer layer, ClassLoader loader); + + /** * Returns an ordered stream of layers. The first element is the * given layer, the remaining elements are its parents, in DFS order. */ --- old/src/java.base/share/classes/jdk/internal/loader/BootLoader.java 2020-07-22 12:00:41.312167066 -0700 +++ new/src/java.base/share/classes/jdk/internal/loader/BootLoader.java 2020-07-22 12:00:41.128160140 -0700 @@ -43,6 +43,7 @@ import jdk.internal.access.JavaLangAccess; import jdk.internal.access.SharedSecrets; +import jdk.internal.misc.VM; import jdk.internal.module.Modules; import jdk.internal.module.ServicesCatalog; import jdk.internal.util.StaticProperty; @@ -65,7 +66,22 @@ } // ServiceCatalog for the boot class loader - private static final ServicesCatalog SERVICES_CATALOG = ServicesCatalog.create(); + private static final ServicesCatalog SERVICES_CATALOG; + static { + if (ArchivedData.servicesCatalog != null) { + SERVICES_CATALOG = ArchivedData.servicesCatalog; + } else { + SERVICES_CATALOG = ServicesCatalog.create(); + ArchivedData.servicesCatalog = SERVICES_CATALOG; + } + } + + static class ArchivedData { + static ServicesCatalog servicesCatalog; + static { + VM.initializeFromArchive(ArchivedData.class); + } + } // ClassLoaderValue map for the boot class loader private static final ConcurrentHashMap CLASS_LOADER_VALUE_MAP --- old/src/java.base/share/classes/jdk/internal/loader/BuiltinClassLoader.java 2020-07-22 12:00:41.884188598 -0700 +++ new/src/java.base/share/classes/jdk/internal/loader/BuiltinClassLoader.java 2020-07-22 12:00:41.704181822 -0700 @@ -104,8 +104,11 @@ private final BuiltinClassLoader parent; // the URL class path, or null if there is no class path - private final URLClassPath ucp; + private @Stable URLClassPath ucp; + void setClassPath(URLClassPath ucp) { + this.ucp = ucp; + } /** * A module defined/loaded by a built-in class loader. @@ -156,10 +159,22 @@ } } + static class ArchivedData { + static Map packageToModule; + } // maps package name to loaded module for modules in the boot layer - private static final Map packageToModule - = new ConcurrentHashMap<>(1024); + private static final Map packageToModule; + + static { + VM.initializeFromArchive(ArchivedData.class); + if (ArchivedData.packageToModule != null) { + packageToModule = ArchivedData.packageToModule; + } else { + packageToModule = new ConcurrentHashMap<>(1024); + ArchivedData.packageToModule = packageToModule; + } + } // maps a module name to a module reference private final Map nameToModule; @@ -1042,4 +1057,9 @@ private static URL checkURL(URL url) { return URLClassPath.checkURL(url); } + + // Called from VM only, during -Xshare:dump + private void resetArchivedStates() { + ucp = null; + } } --- old/src/java.base/share/classes/jdk/internal/loader/ClassLoaders.java 2020-07-22 12:00:42.468210582 -0700 +++ new/src/java.base/share/classes/jdk/internal/loader/ClassLoaders.java 2020-07-22 12:00:42.292203957 -0700 @@ -36,6 +36,7 @@ import jdk.internal.access.JavaLangAccess; import jdk.internal.access.SharedSecrets; import jdk.internal.misc.VM; +import jdk.internal.vm.annotation.Stable; /** * Creates and provides access to the built-in platform and application class @@ -54,16 +55,45 @@ private static final PlatformClassLoader PLATFORM_LOADER; private static final AppClassLoader APP_LOADER; + static class ArchivedData { + private final BootClassLoader boot_loader; + private final PlatformClassLoader platform_loader; + private final AppClassLoader app_loader; + + private ArchivedData() { + boot_loader = BOOT_LOADER; + platform_loader = PLATFORM_LOADER; + app_loader = APP_LOADER; + } + + static void archive() { + singleton = new ArchivedData(); + } + + static ArchivedData get() { + return singleton; + } + + private static ArchivedData singleton; + } + // Creates the built-in class loaders. static { - // -Xbootclasspath/a or -javaagent with Boot-Class-Path attribute - String append = VM.getSavedProperty("jdk.boot.class.path.append"); - BOOT_LOADER = - new BootClassLoader((append != null && !append.isEmpty()) - ? new URLClassPath(append, true) - : null); - PLATFORM_LOADER = new PlatformClassLoader(BOOT_LOADER); - + VM.initializeFromArchive(ArchivedData.class); + ArchivedData archivedData = ArchivedData.get(); + if (archivedData != null) { + // assert VM.getSavedProperty("jdk.boot.class.path.append") == null + BOOT_LOADER = archivedData.boot_loader; + PLATFORM_LOADER = archivedData.platform_loader; + } else { + // -Xbootclasspath/a or -javaagent with Boot-Class-Path attribute + String append = VM.getSavedProperty("jdk.boot.class.path.append"); + BOOT_LOADER = + new BootClassLoader((append != null && !append.isEmpty()) + ? new URLClassPath(append, true) + : null); + PLATFORM_LOADER = new PlatformClassLoader(BOOT_LOADER); + } // A class path is required when no initial module is specified. // In this case the class path defaults to "", meaning the current // working directory. When an initial module is specified, on the @@ -75,7 +105,13 @@ cp = (initialModuleName == null) ? "" : null; } URLClassPath ucp = new URLClassPath(cp, false); - APP_LOADER = new AppClassLoader(PLATFORM_LOADER, ucp); + if (archivedData != null) { + APP_LOADER = archivedData.app_loader; + APP_LOADER.setClassPath(ucp); + } else { + APP_LOADER = new AppClassLoader(PLATFORM_LOADER, ucp); + ArchivedData.archive(); + } } /** @@ -144,7 +180,11 @@ throw new InternalError(); } - final URLClassPath ucp; + @Stable URLClassPath ucp; + void setClassPath(URLClassPath ucp) { + super.setClassPath(ucp); + this.ucp = ucp; + } AppClassLoader(PlatformClassLoader parent, URLClassPath ucp) { super("app", parent, ucp); @@ -190,6 +230,11 @@ protected Package defineOrCheckPackage(String pn, Manifest man, URL url) { return super.defineOrCheckPackage(pn, man, url); } + + // Called from VM only, during -Xshare:dump + private void resetArchivedStates() { + ucp = null; + } } /** --- old/src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.java 2020-07-22 12:00:43.036231963 -0700 +++ new/src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.java 2020-07-22 12:00:42.856225187 -0700 @@ -51,6 +51,8 @@ import jdk.internal.loader.BootLoader; import jdk.internal.loader.BuiltinClassLoader; +import jdk.internal.loader.ClassLoaders; +import jdk.internal.misc.VM; import jdk.internal.access.JavaLangAccess; import jdk.internal.access.JavaLangModuleAccess; import jdk.internal.access.SharedSecrets; @@ -133,6 +135,103 @@ } } + private static class ArchivedBootLayer { + private static ArchivedBootLayer archivedBootLayer; + + private final ModuleLayer bootLayer; + private final ModuleFinder limitedFinder; + private final IllegalAccessLogger.Builder builder; + private final ModuleFinder unlimitedFinder; + private final ServicesCatalog platformCatalog; + private final ServicesCatalog appCatalog; + + public ArchivedBootLayer(ModuleLayer bootLayer, + ModuleFinder limitedFinder, + ModuleFinder unlimitedFinder, + IllegalAccessLogger.Builder builder) { + this.bootLayer = bootLayer; + this.limitedFinder = limitedFinder; + this.unlimitedFinder = unlimitedFinder; + this.builder = builder; + + this.platformCatalog = ServicesCatalog.getServicesCatalog(ClassLoaders.platformClassLoader()); + this.appCatalog = ServicesCatalog.getServicesCatalog(ClassLoaders.appClassLoader()); + } + + static ArchivedBootLayer get() { + // The VM will initialize archivedBootLayer only if MetaspaceShared::use_full_module_graph() is true. + return archivedBootLayer; + } + + static void archive(ArchivedBootLayer layer) { + archivedBootLayer = layer; + } + + static { + VM.initializeFromArchive(ArchivedBootLayer.class); + } + } + + private static boolean hasProperty(String key) { + return System.getProperty(key) != null; + } + + private static boolean mayUseArchivedBootLayer() { + // If these properties are set, we cannot use the archived boot layer. + // The VM should have already checked this before initializing + // ArchivedBootLayer::archivedBootLayer. + if (hasProperty("jdk.module.upgrade.path") || + hasProperty("jdk.module.main") || + hasProperty("jdk.module.limitmods") || + hasProperty("jdk.module.validation") || + hasProperty("jdk.module.showModuleResolution") || + hasProperty("jdk.module.illegalAccess") || + hasProperty("java.system.class.loader") || + hasProperty("jdk.module.addexports.0") || + hasProperty("jdk.module.addopens.0") || + hasProperty("jdk.module.addreads.0") || + hasProperty("jdk.module.patch.0") || + hasProperty("jdk.module.addmods.0")) { + return false; + } else { + return true; + } + } + + private static ModuleLayer getArchivedBootLayer() { + ArchivedBootLayer archivedBootLayer = ArchivedBootLayer.get(); + if (archivedBootLayer != null) { + assert mayUseArchivedBootLayer(); + Counters.add("jdk.module.boot.0.archivedBootLayer"); + limitedFinder = archivedBootLayer.limitedFinder; + unlimitedFinder = archivedBootLayer.unlimitedFinder; + ModuleLayer bootLayer = archivedBootLayer.bootLayer; + + // Trigger BootLoader. + BootLoader.getUnnamedModule(); + + // BootLoader.SERVICES_CATALOG is saved/restored separately in BootLoader.java + ServicesCatalog.setServicesCatalog(ClassLoaders.platformClassLoader(), + archivedBootLayer.platformCatalog); + ServicesCatalog.setServicesCatalog(ClassLoaders.appClassLoader(), + archivedBootLayer.appCatalog); + + JavaLangAccess jla = SharedSecrets.getJavaLangAccess(); + jla.bindToLoader(bootLayer, ClassLoaders.appClassLoader()); + + IllegalAccessLogger.Builder builder = archivedBootLayer.builder; + if (builder != null) { + builder.complete(); + } + + Counters.publish("jdk.module.boot.totalTime"); + return bootLayer; + } + + return null; + } + + /** * Initialize the module system, returning the boot layer. * @@ -141,6 +240,10 @@ public static ModuleLayer boot() throws Exception { Counters.start(); + ModuleLayer bootLayer = getArchivedBootLayer(); + if (bootLayer != null) { + return bootLayer; + } // Step 0: Command line options @@ -405,7 +508,7 @@ // Step 6: Define all modules to the VM - ModuleLayer bootLayer = ModuleLayer.empty().defineModules(cf, clf); + bootLayer = ModuleLayer.empty().defineModules(cf, clf); Counters.add("jdk.module.boot.6.layerCreateTime"); // Step 7: Miscellaneous @@ -428,11 +531,12 @@ concealedPackagesToOpen = systemModules.concealedPackagesToOpen(); exportedPackagesToOpen = systemModules.exportedPackagesToOpen(); } - addIllegalAccess(upgradeModulePath, - concealedPackagesToOpen, - exportedPackagesToOpen, - bootLayer, - extraExportsOrOpens); + IllegalAccessLogger.Builder builder = + addIllegalAccess(upgradeModulePath, + concealedPackagesToOpen, + exportedPackagesToOpen, + bootLayer, + extraExportsOrOpens); Counters.add("jdk.module.boot.7.adjustModulesTime"); // save module finders for later use @@ -453,6 +557,11 @@ clf, concealedPackagesToOpen, exportedPackagesToOpen)); + ArchivedBootLayer.archive( + new ArchivedBootLayer(bootLayer, + limitedFinder, + unlimitedFinder, + builder)); } // total time to initialize @@ -751,17 +860,18 @@ * Process the --illegal-access option (and its default) to open packages * of system modules in the boot layer to code in unnamed modules. */ - private static void addIllegalAccess(ModuleFinder upgradeModulePath, - Map> concealedPackagesToOpen, - Map> exportedPackagesToOpen, - ModuleLayer bootLayer, - boolean extraExportsOrOpens) { + private static IllegalAccessLogger.Builder + addIllegalAccess(ModuleFinder upgradeModulePath, + Map> concealedPackagesToOpen, + Map> exportedPackagesToOpen, + ModuleLayer bootLayer, + boolean extraExportsOrOpens) { String value = getAndRemoveProperty("jdk.module.illegalAccess"); IllegalAccessLogger.Mode mode = IllegalAccessLogger.Mode.ONESHOT; if (value != null) { switch (value) { case "deny": - return; + return null; case "permit": break; case "warn": @@ -773,7 +883,7 @@ default: fail("Value specified to --illegal-access not recognized:" + " '" + value + "'"); - return; + return null; } } IllegalAccessLogger.Builder builder @@ -841,6 +951,7 @@ } builder.complete(); + return builder; } /** --- old/src/java.base/share/classes/jdk/internal/module/ServicesCatalog.java 2020-07-22 12:00:43.628254247 -0700 +++ new/src/java.base/share/classes/jdk/internal/module/ServicesCatalog.java 2020-07-22 12:00:43.448247472 -0700 @@ -171,6 +171,13 @@ return catalog; } + static void setServicesCatalog(ClassLoader loader, ServicesCatalog catalog) { + ServicesCatalog previous = CLV.putIfAbsent(loader, catalog); + if (previous != null) { + throw new RuntimeException("unexpected: " + previous); + } + } + // the ServicesCatalog registered to a class loader private static final ClassLoaderValue CLV = new ClassLoaderValue<>(); } --- old/test/hotspot/jtreg/runtime/NMT/CheckForProperDetailStackTrace.java 2020-07-22 12:00:44.208276081 -0700 +++ new/test/hotspot/jtreg/runtime/NMT/CheckForProperDetailStackTrace.java 2020-07-22 12:00:44.020269004 -0700 @@ -28,12 +28,15 @@ * @library /test/lib * @modules java.base/jdk.internal.misc * java.management + * @compile ../modules/CompilerUtils.java * @run driver CheckForProperDetailStackTrace */ import jdk.test.lib.Platform; import jdk.test.lib.process.ProcessTools; import jdk.test.lib.process.OutputAnalyzer; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -49,6 +52,11 @@ * through this test and update it accordingly. */ public class CheckForProperDetailStackTrace { + private static final String TEST_SRC = System.getProperty("test.src"); + private static final String TEST_CLASSES = System.getProperty("test.classes"); + + private static final Path SRC_DIR = Paths.get(TEST_SRC, "src"); + private static final Path MODS_DIR = Paths.get(TEST_CLASSES, "mods"); /* The stack trace we look for by default. Note that :: has been replaced by .* to make sure it matches even if the symbol is not unmangled. @@ -83,11 +91,24 @@ private static String expectedSymbol = "locked_create_entry"; public static void main(String args[]) throws Exception { + boolean compiled; + // Compile module jdk.test declaration + compiled = CompilerUtils.compile( + SRC_DIR.resolve("jdk.test"), + MODS_DIR.resolve("jdk.test")); + if (!compiled) { + throw new RuntimeException("Test failed to compile module jdk.test"); + } + + // If modules in the system image have been archived in CDS, they will not be + // created again at run time. Explicitly use an external module to make sure + // we have a runtime-defined ModuleEntry ProcessBuilder pb = ProcessTools.createJavaProcessBuilder( "-XX:+UnlockDiagnosticVMOptions", "-XX:NativeMemoryTracking=detail", "-XX:+PrintNMTStatistics", - "-version"); + "-p", MODS_DIR.toString(), + "-m", "jdk.test/test.Main"); OutputAnalyzer output = new OutputAnalyzer(pb.start()); output.shouldHaveExitValue(0); --- old/test/hotspot/jtreg/runtime/cds/appcds/SpecifySysLoaderProp.java 2020-07-22 12:00:44.780297613 -0700 +++ new/test/hotspot/jtreg/runtime/cds/appcds/SpecifySysLoaderProp.java 2020-07-22 12:00:44.604290987 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2020, 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 @@ -72,6 +72,7 @@ TestCommon.run( "-verbose:class", "-cp", appJar, + "-Xlog:cds", "-Djava.system.class.loader=TestClassLoader", "ReportMyLoader") .assertNormalExit("ReportMyLoader's loader = jdk.internal.loader.ClassLoaders$AppClassLoader@", //<-this is still printed because TestClassLoader simply delegates to Launcher$AppLoader, but ... @@ -80,6 +81,7 @@ .assertNormalExit(output -> { output.shouldMatch(".class,load. TestClassLoader source: file:"); output.shouldMatch(".class,load. ReportMyLoader source: file:.*" + jarFileName); + output.shouldContain("full module graph: disabled due to incompatible property: java.system.class.loader="); }); // (3) Try to change the java.system.class.loader programmatically after --- old/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/modulepath/OptimizeModuleHandlingTest.java 2020-07-22 12:00:45.356319295 -0700 +++ new/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/modulepath/OptimizeModuleHandlingTest.java 2020-07-22 12:00:45.176312519 -0700 @@ -24,7 +24,7 @@ /** * @test - * @requires vm.cds & !vm.jvmci + * @requires vm.cds & !vm.graal.enabled * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds * @run driver OptimizeModuleHandlingTest * @summary test module path changes for optimization of @@ -63,8 +63,8 @@ private static String CLASS_FOUND_MESSAGE = "com.foos.Test found"; private static String CLASS_NOT_FOUND_MESSAGE = "java.lang.ClassNotFoundException: com.foos.Test"; - private static String OPTIMIZE_ENABLED = "Using optimized module handling enabled"; - private static String OPTIMIZE_DISABLED = "Using optimized module handling disabled"; + private static String OPTIMIZE_ENABLED = "optimized module handling: enabled"; + private static String OPTIMIZE_DISABLED = "optimized module handling: disabled"; private static String MAIN_FROM_JAR = "class,load.*com.bars.Main.*[.]jar"; private static String MAIN_FROM_CDS = "class,load.*com.bars.Main.*shared objects file"; private static String TEST_FROM_JAR = "class,load.*com.foos.Test.*[.]jar"; --- old/test/hotspot/jtreg/runtime/logging/ModulesTest.java 2020-07-22 12:00:45.928340827 -0700 +++ new/test/hotspot/jtreg/runtime/logging/ModulesTest.java 2020-07-22 12:00:45.748334051 -0700 @@ -34,13 +34,18 @@ import jdk.test.lib.process.ProcessTools; public class ModulesTest { + // If modules in the system image have been archived in CDS, no Modules will + // be dynamically created at runtime. Disable CDS so all of the expected messages + // are printed. + private static String XSHARE_OFF = "-Xshare:off"; + public static void main(String[] args) throws Exception { - testModuleTrace("-Xlog:module=trace", "-version"); - testModuleLoad("-Xlog:module+load", "-version"); - testModuleUnload("-Xlog:module+unload", "-version"); + testModuleTrace("-Xlog:module=trace", XSHARE_OFF, "-version"); + testModuleLoad("-Xlog:module+load", XSHARE_OFF, "-version"); + testModuleUnload("-Xlog:module+unload", XSHARE_OFF, "-version"); // same as -Xlog:module+load -Xlog:module+unload - testModuleLoad("-verbose:module", "-version"); + testModuleLoad("-verbose:module", XSHARE_OFF, "-version"); } static void testModuleTrace(String... args) throws Exception { --- /dev/null 2019-11-19 22:05:02.069813242 -0800 +++ new/test/hotspot/jtreg/runtime/NMT/src/jdk.test/module-info.java 2020-07-22 12:00:46.312355282 -0700 @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2020, 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. + */ + +module jdk.test { +} --- /dev/null 2019-11-19 22:05:02.069813242 -0800 +++ new/test/hotspot/jtreg/runtime/NMT/src/jdk.test/test/Main.java 2020-07-22 12:00:47.028382234 -0700 @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2020, 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. + */ + +package test; + +public class Main { + public static void main(String[] args) throws Exception { + System.out.println("jdk.test/test/Main: hello"); + } +} --- /dev/null 2019-11-19 22:05:02.069813242 -0800 +++ new/test/hotspot/jtreg/runtime/cds/ServiceLoaderTest.java 2020-07-22 12:00:47.744409187 -0700 @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2020, 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 + * @bug 8244778 + * @summary Make sure that the ServicesCatalogs for boot/platform/app loaders are properly archived. + * @requires vm.cds + * @modules java.naming + * @library /test/lib + * @run driver ServiceLoaderTest + */ + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; +import java.util.ServiceLoader; +import java.util.spi.ToolProvider; +import javax.naming.spi.InitialContextFactory; +import jdk.test.lib.cds.CDSTestUtils; +import jdk.test.lib.cds.CDSOptions; +import jdk.test.lib.process.OutputAnalyzer; + +public class ServiceLoaderTest { + public static void main(String[] args) throws Exception { + CDSOptions opts = new CDSOptions(); + + CDSTestUtils.createArchiveAndCheck(opts); + + // Some mach5 tiers run with -vmoptions:-Xlog:cds=debug. This would cause the outputs to mismatch. + // Force -Xlog:cds=warning to supress the CDS logs. + opts.setUseVersion(false); + opts.addSuffix("-showversion", "-Xlog:cds=warning", "ServiceLoaderApp"); + OutputAnalyzer out1 = CDSTestUtils.runWithArchive(opts); + + opts.setXShareMode("off"); + OutputAnalyzer out2 = CDSTestUtils.runWithArchive(opts); + + compare(out1, out2); + } + + static void compare(OutputAnalyzer out1, OutputAnalyzer out2) { + String[] arr1 = splitLines(out1); + String[] arr2 = splitLines(out2); + + int max = arr1.length > arr2.length ? arr1.length : arr2.length; + for (int i = 0; i < max; i++) { + if (i >= arr1.length) { + mismatch(i, "", arr2[i]); + } + if (i >= arr2.length) { + mismatch(i, arr1[i], ""); + } + if (!arr1[i].equals(arr2[i])) { + mismatch(i, arr1[i], arr2[i]); + } + } + } + + static String[] splitLines(OutputAnalyzer out) { + return out.getStdout().split("\n"); + } + + static void mismatch(int i, String s1, String s2) { + System.out.println("Mismatched line: " + i); + System.out.println("cds on : " + s1); + System.out.println("cds off: " + s2); + throw new RuntimeException("Mismatched line " + i + ": \"" + s1 + "\" vs \"" + s2 + "\""); + } +} + +class ServiceLoaderApp { + public static void main(String args[]) throws Exception { + doTest(ToolProvider.class); + doTest(InitialContextFactory.class); + } + + static void doTest(Class c) throws Exception { + System.out.println("============================================================"); + System.out.println("Testing : " + c.getName()); + System.out.println("============================================================"); + + dump("default", ServiceLoader.load(c)); + dump("null loader", ServiceLoader.load(c, null)); + dump("platform loader", ServiceLoader.load(c, ServiceLoaderApp.class.getClassLoader().getParent())); + dump("system loader", ServiceLoader.load(c, ServiceLoaderApp.class.getClassLoader())); + } + + static void dump(String testCase, ServiceLoader loader) throws Exception { + System.out.println("[TEST CASE] " + testCase); + System.out.println("[svcloader] " + asString(loader)); + Iterator it = loader.iterator(); + ArrayList list = new ArrayList<>(); + while (it.hasNext()) { + list.add(asString(it.next().toString())); + } + Collections.sort(list); + for (String s : list) { + System.out.println(s); + } + } + + static String asString(Object o) { + String s = o.toString(); + int n = s.indexOf("@"); + if (n >= 0) { + s = s.substring(0, n); + } + return s; + } +} \ No newline at end of file