--- old/make/hotspot/lib/JvmFeatures.gmk 2020-08-12 15:00:38.803849969 -0700 +++ new/make/hotspot/lib/JvmFeatures.gmk 2020-08-12 15:00:38.475837622 -0700 @@ -117,6 +117,7 @@ JVM_CFLAGS_FEATURES += -DINCLUDE_CDS=0 JVM_EXCLUDE_FILES += \ classListParser.cpp \ + classLoaderDataShared.cpp \ classLoaderExt.cpp \ dynamicArchive.cpp \ filemap.cpp \ --- old/make/hotspot/symbols/symbols-unix 2020-08-12 15:00:39.835888817 -0700 +++ new/make/hotspot/symbols/symbols-unix 2020-08-12 15:00:39.503876320 -0700 @@ -198,5 +198,6 @@ JVM_AddModuleExportsToAll JVM_AddModuleExportsToAllUnnamed JVM_AddReadsModule +JVM_DefineArchivedModules JVM_DefineModule JVM_SetBootLoaderUnnamedModule --- old/src/hotspot/share/classfile/classLoader.cpp 2020-08-12 15:00:40.895928719 -0700 +++ new/src/hotspot/share/classfile/classLoader.cpp 2020-08-12 15:00:40.551915770 -0700 @@ -1643,12 +1643,14 @@ { MutexLocker ml(THREAD, Module_lock); - ModuleEntry* jb_module = null_cld_modules->locked_create_entry(Handle(), + if (ModuleEntryTable::javabase_moduleEntry() == NULL) { // may have been inited by CDS. + ModuleEntry* jb_module = null_cld_modules->locked_create_entry(Handle(), false, vmSymbols::java_base(), NULL, NULL, null_cld); - if (jb_module == NULL) { - vm_exit_during_initialization("Unable to create ModuleEntry for " JAVA_BASE_NAME); + if (jb_module == NULL) { + vm_exit_during_initialization("Unable to create ModuleEntry for " JAVA_BASE_NAME); + } + ModuleEntryTable::set_javabase_moduleEntry(jb_module); } - ModuleEntryTable::set_javabase_moduleEntry(jb_module); } } --- old/src/hotspot/share/classfile/classLoaderData.cpp 2020-08-12 15:00:42.063972686 -0700 +++ new/src/hotspot/share/classfile/classLoaderData.cpp 2020-08-12 15:00:41.715959586 -0700 @@ -59,9 +59,11 @@ #include "logging/logStream.hpp" #include "memory/allocation.inline.hpp" #include "memory/metadataFactory.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" --- old/src/hotspot/share/classfile/javaClasses.cpp 2020-08-12 15:00:44.000045564 -0700 +++ new/src/hotspot/share/classfile/javaClasses.cpp 2020-08-12 15:00:43.716034873 -0700 @@ -3393,12 +3393,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. @@ -4822,7 +4827,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-08-12 15:00:44.768074474 -0700 +++ new/src/hotspot/share/classfile/javaClasses.hpp 2020-08-12 15:00:44.528065439 -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; --- old/src/hotspot/share/classfile/moduleEntry.cpp 2020-08-12 15:00:45.396098114 -0700 +++ new/src/hotspot/share/classfile/moduleEntry.cpp 2020-08-12 15:00:45.148088778 -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,8 @@ #include "utilities/growableArray.hpp" #include "utilities/hashtable.inline.hpp" #include "utilities/ostream.hpp" +#include "utilities/quickSort.hpp" +#include "utilities/resourceHash.hpp" ModuleEntry* ModuleEntryTable::_javabase_module = NULL; @@ -108,15 +113,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 +367,187 @@ 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 convert 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->name()->fast_compare(b->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 > 1) { + // Always allocate in the same order to produce deterministic archive. + QuickSort::sort(archived_modules->data(), n, (_sort_Fn)compare_module_by_name, true); + } + 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"); + + 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-08-12 15:00:46.044122506 -0700 +++ new/src/hotspot/share/classfile/moduleEntry.hpp 2020-08-12 15:00:45.796113171 -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/modules.cpp 2020-08-12 15:00:46.912155181 -0700 +++ new/src/hotspot/share/classfile/modules.cpp 2020-08-12 15:00:46.628144490 -0700 @@ -27,6 +27,7 @@ #include "classfile/classFileParser.hpp" #include "classfile/classLoader.hpp" #include "classfile/classLoaderData.inline.hpp" +#include "classfile/classLoaderDataShared.hpp" #include "classfile/javaAssertions.hpp" #include "classfile/javaClasses.hpp" #include "classfile/javaClasses.inline.hpp" @@ -39,6 +40,7 @@ #include "classfile/vmSymbols.hpp" #include "logging/log.hpp" #include "logging/logStream.hpp" +#include "memory/metaspaceShared.hpp" #include "memory/resourceArea.hpp" #include "runtime/handles.inline.hpp" #include "runtime/javaCalls.hpp" @@ -450,6 +452,28 @@ } } +#if INCLUDE_CDS_JAVA_HEAP +void Modules::define_archived_modules(jobject platform_loader, jobject system_loader, TRAPS) { + if (platform_loader == NULL) { + THROW_MSG(vmSymbols::java_lang_NullPointerException(), "Null platform loader object"); + } + + if (system_loader == NULL) { + THROW_MSG(vmSymbols::java_lang_NullPointerException(), "Null system loader object"); + } + + if (UseSharedSpaces && MetaspaceShared::use_full_module_graph()) { + Handle h_platform_loader(THREAD, JNIHandles::resolve_non_null(platform_loader)); + ClassLoaderData* platform_loader_data = SystemDictionary::register_loader(h_platform_loader); + ClassLoaderDataShared::restore_java_platform_loader_from_archive(platform_loader_data); + + Handle h_system_loader(THREAD, JNIHandles::resolve_non_null(system_loader)); + ClassLoaderData* system_loader_data = SystemDictionary::register_loader(h_system_loader); + ClassLoaderDataShared::restore_java_system_loader_from_archive(system_loader_data); + } +} +#endif + void Modules::set_bootloader_unnamed_module(jobject module, TRAPS) { ResourceMark rm(THREAD); --- old/src/hotspot/share/classfile/modules.hpp 2020-08-12 15:00:47.836189963 -0700 +++ new/src/hotspot/share/classfile/modules.hpp 2020-08-12 15:00:47.572180025 -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 @@ -52,6 +52,9 @@ static void define_module(jobject module, jboolean is_open, jstring version, jstring location, jobjectArray packages, TRAPS); + static void define_archived_modules(jobject platform_loader, jobject system_loader, + TRAPS) NOT_CDS_JAVA_HEAP_RETURN; + // Provides the java.lang.Module for the unnamed module defined // to the boot loader. // --- old/src/hotspot/share/classfile/packageEntry.cpp 2020-08-12 15:00:48.472213904 -0700 +++ new/src/hotspot/share/classfile/packageEntry.cpp 2020-08-12 15:00:48.244205321 -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,18 @@ #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/quickSort.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 +193,113 @@ 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->name()->fast_compare(b->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 > 1) { + QuickSort::sort(archived_packages->data(), n, (_sort_Fn)compare_package_by_name, true); + } + 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"); + + 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 // INCLUDE_CDS_JAVA_HEAP + 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); @@ -273,7 +385,6 @@ } } } - } // iteration of qualified exports --- old/src/hotspot/share/classfile/packageEntry.hpp 2020-08-12 15:00:49.128238598 -0700 +++ new/src/hotspot/share/classfile/packageEntry.hpp 2020-08-12 15:00:48.872228961 -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-08-12 15:00:49.892267357 -0700 +++ new/src/hotspot/share/classfile/systemDictionary.cpp 2020-08-12 15:00:49.652258322 -0700 @@ -30,6 +30,7 @@ #include "classfile/classLoader.hpp" #include "classfile/classLoaderData.inline.hpp" #include "classfile/classLoaderDataGraph.inline.hpp" +#include "classfile/classLoaderDataShared.hpp" #include "classfile/classLoaderExt.hpp" #include "classfile/dictionary.hpp" #include "classfile/javaClasses.inline.hpp" @@ -2121,6 +2122,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(ClassLoaderDataShared::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-08-12 15:00:50.748299580 -0700 +++ new/src/hotspot/share/classfile/systemDictionary.hpp 2020-08-12 15:00:50.492289943 -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-08-12 15:00:51.600331652 -0700 +++ new/src/hotspot/share/classfile/vmSymbols.hpp 2020-08-12 15:00:51.348322166 -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/include/jvm.h 2020-08-12 15:00:52.500365531 -0700 +++ new/src/hotspot/share/include/jvm.h 2020-08-12 15:00:52.236355593 -0700 @@ -491,6 +491,14 @@ JVM_AddReadsModule(JNIEnv *env, jobject from_module, jobject source_module); /* + * Define all modules that have been stored in the CDS archived heap. + * platform_loader: the built-in platform class loader + * system_loader: the built-in system class loader + */ +JNIEXPORT void JNICALL +JVM_DefineArchivedModules(JNIEnv *env, jobject platform_loader, jobject system_loader); + +/* * Reflection support functions */ --- old/src/hotspot/share/memory/filemap.cpp 2020-08-12 15:00:53.476402271 -0700 +++ new/src/hotspot/share/memory/filemap.cpp 2020-08-12 15:00:53.144389773 -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-08-12 15:00:54.396436902 -0700 +++ new/src/hotspot/share/memory/filemap.hpp 2020-08-12 15:00:54.128426814 -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 module 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-08-12 15:00:55.300470932 -0700 +++ new/src/hotspot/share/memory/heapShared.cpp 2020-08-12 15:00:55.032460843 -0700 @@ -23,9 +23,13 @@ */ #include "precompiled.hpp" +#include "classfile/classLoaderData.hpp" +#include "classfile/classLoaderDataShared.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 +49,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 +73,23 @@ // 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/ArchivedClassLoaders", 1, "archivedClassLoaders"}, + {"jdk/internal/module/ArchivedBootLayer", 1, "archivedBootLayer"}, + {"jdk/internal/module/ArchivedModuleGraph", 0, "archivedModuleGraph"}, + {"java/util/ImmutableCollections", 0, "archivedObjects"}, + {"java/lang/Module$ArchivedData", 1, "archivedData"}, + {"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,36 @@ 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) { + assert(DumpSharedSpaces, "dump-time only"); + 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 +271,8 @@ log_info(cds)("Dumping objects to open archive heap region ..."); copy_open_archive_heap_objects(open); + ClassLoaderDataShared::init_archived_oops(); + destroy_archived_object_cache(); } @@ -465,6 +505,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 +672,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 +758,18 @@ vm_exit(1); } } + + if (java_lang_Module::is_instance(orig_obj)) { + check_module_oop(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 +1066,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-08-12 15:00:56.152503004 -0700 +++ new/src/hotspot/share/memory/heapShared.hpp 2020-08-12 15:00:55.864492163 -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-08-12 15:00:57.172541400 -0700 +++ new/src/hotspot/share/memory/metaspaceShared.cpp 2020-08-12 15:00:56.860529655 -0700 @@ -25,6 +25,7 @@ #include "precompiled.hpp" #include "jvm.h" #include "classfile/classLoaderDataGraph.hpp" +#include "classfile/classLoaderDataShared.hpp" #include "classfile/classListParser.hpp" #include "classfile/classLoaderExt.hpp" #include "classfile/dictionary.hpp" @@ -90,6 +91,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 +242,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 +595,8 @@ serialize_cloned_cpp_vtptrs(soc); soc->do_tag(--tag); + CDS_JAVA_HEAP_ONLY(ClassLoaderDataShared::serialize(soc)); + soc->do_tag(666); } @@ -1083,6 +1091,7 @@ f(SymbolBucket) \ f(StringHashentry) \ f(StringBucket) \ + f(ModulesNatives) \ f(Other) enum Type { @@ -1118,6 +1127,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; @@ -1407,6 +1421,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(); + ClassLoaderDataShared::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 @@ -1416,6 +1437,11 @@ ResourceMark rm; ShallowCopier ro_copier(true); iterate_roots(&ro_copier); +#if INCLUDE_CDS_JAVA_HEAP + char* start = _ro_region.top(); + ClassLoaderDataShared::init_archived_tables(); + ArchiveCompactor::alloc_stats()->record_modules(_ro_region.top() - start, /*read_only*/true); +#endif } { log_info(cds)("Relocating embedded pointers ... "); @@ -1510,6 +1536,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; @@ -1814,6 +1847,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; @@ -1928,6 +1965,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() @@ -2345,7 +2388,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); @@ -2670,6 +2714,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: "); @@ -2692,8 +2741,3 @@ } st->cr(); } - - - - - --- old/src/hotspot/share/memory/metaspaceShared.hpp 2020-08-12 15:00:58.092576032 -0700 +++ new/src/hotspot/share/memory/metaspaceShared.hpp 2020-08-12 15:00:57.832566244 -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,22 @@ // 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 - return NULL; -#endif + } + + 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; } template @@ -352,6 +358,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 +384,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-08-12 15:00:59.068612771 -0700 +++ new/src/hotspot/share/oops/instanceKlass.cpp 2020-08-12 15:00:58.704599069 -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" @@ -2544,7 +2545,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-08-12 15:01:00.140653125 -0700 +++ new/src/hotspot/share/oops/instanceKlass.hpp 2020-08-12 15:00:59.844641983 -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/prims/jvm.cpp 2020-08-12 15:01:00.904681884 -0700 +++ new/src/hotspot/share/prims/jvm.cpp 2020-08-12 15:01:00.672673151 -0700 @@ -1229,6 +1229,11 @@ Modules::add_reads_module(from_module, source_module, CHECK); JVM_END +JVM_ENTRY(void, JVM_DefineArchivedModules(JNIEnv *env, jobject platform_loader, jobject system_loader)) + JVMWrapper("JVM_DefineArchivedModules"); + Modules::define_archived_modules(platform_loader, system_loader, CHECK); +JVM_END + // Reflection support ////////////////////////////////////////////////////////////////////////////// JVM_ENTRY(jstring, JVM_InitClassName(JNIEnv *env, jclass cls)) --- old/src/hotspot/share/runtime/arguments.cpp 2020-08-12 15:01:01.708712150 -0700 +++ new/src/hotspot/share/runtime/arguments.cpp 2020-08-12 15:01:01.456702663 -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-08-12 15:01:02.684748889 -0700 +++ new/src/java.base/share/classes/java/lang/ClassLoader.java 2020-08-12 15:01:02.372737145 -0700 @@ -2714,6 +2714,17 @@ offset = unsafe.objectFieldOffset(k, name); return unsafe.compareAndSetReference(this, offset, null, obj); } + + /** + * Called by the VM, 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-08-12 15:01:03.700787134 -0700 +++ new/src/java.base/share/classes/java/lang/Module.java 2020-08-12 15:01:03.404775992 -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,55 @@ // -- // 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 archivedData; + private final Module allUnnamedModule; + private final Set allUnnamedModules; + private final Module everyoneModule; + private final Set everyoneSet; + + private ArchivedData() { + this.allUnnamedModule = ALL_UNNAMED_MODULE; + this.allUnnamedModules = ALL_UNNAMED_MODULE_SET; + this.everyoneModule = EVERYONE_MODULE; + this.everyoneSet = EVERYONE_SET; + } + + static void archive() { + archivedData = new ArchivedData(); + } + + static ArchivedData get() { + return archivedData; + } + + static { + VM.initializeFromArchive(ArchivedData.class); + } + } + + static { + ArchivedData archivedData = ArchivedData.get(); + if (archivedData != null) { + ALL_UNNAMED_MODULE = archivedData.allUnnamedModule; + ALL_UNNAMED_MODULE_SET = archivedData.allUnnamedModules; + EVERYONE_MODULE = archivedData.everyoneModule; + EVERYONE_SET = archivedData.everyoneSet; + } 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(); + } + } /** * The holder of data structures to support readability, exports, and --- old/src/java.base/share/classes/java/lang/System.java 2020-08-12 15:01:04.628822068 -0700 +++ new/src/java.base/share/classes/java/lang/System.java 2020-08-12 15:01:04.320810473 -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-08-12 15:01:07.176917983 -0700 +++ new/src/java.base/share/classes/java/security/SecureClassLoader.java 2020-08-12 15:01:05.692862120 -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,11 @@ return cs.matchCerts(csk.cs, true); } } + + /** + * Called by the VM, during -Xshare:dump + */ + private void resetArchivedStates() { + pdcache.clear(); + } } --- old/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java 2020-08-12 15:01:12.325111770 -0700 +++ new/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java 2020-08-12 15:01:11.749090087 -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-08-12 15:01:14.573196392 -0700 +++ new/src/java.base/share/classes/jdk/internal/loader/BootLoader.java 2020-08-12 15:01:14.073177570 -0700 @@ -65,7 +65,15 @@ } // ServiceCatalog for the boot class loader - private static final ServicesCatalog SERVICES_CATALOG = ServicesCatalog.create(); + private static final ServicesCatalog SERVICES_CATALOG; + static { + ArchivedClassLoaders archivedClassLoaders = ArchivedClassLoaders.get(); + if (archivedClassLoaders != null) { + SERVICES_CATALOG = archivedClassLoaders.servicesCatalog(null); + } else { + SERVICES_CATALOG = ServicesCatalog.create(); + } + } // 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-08-12 15:01:16.785279658 -0700 +++ new/src/java.base/share/classes/jdk/internal/loader/BuiltinClassLoader.java 2020-08-12 15:01:16.177256771 -0700 @@ -104,8 +104,7 @@ private final BuiltinClassLoader parent; // the URL class path, or null if there is no class path - private final URLClassPath ucp; - + private @Stable URLClassPath ucp; /** * A module defined/loaded by a built-in class loader. @@ -156,10 +155,26 @@ } } - // 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 { + ArchivedClassLoaders archivedClassLoaders = ArchivedClassLoaders.get(); + if (archivedClassLoaders != null) { + @SuppressWarnings("unchecked") + Map map + = (Map) archivedClassLoaders.packageToModule(); + packageToModule = map; + } else { + packageToModule = new ConcurrentHashMap<>(1024); + } + } + + /** + * Invoked by ArchivedClassLoaders to archive the package-to-module map. + */ + static Map packageToModule() { + return packageToModule; + } // maps a module name to a module reference private final Map nameToModule; @@ -186,6 +201,21 @@ } /** + * Appends to the given file path to the class path. + */ + void appendClassPath(String path) { + // assert ucp != null; + ucp.addFile(path); + } + + /** + * Sets the class path, called to reset the class path during -Xshare:dump + */ + void setClassPath(URLClassPath ucp) { + this.ucp = ucp; + } + + /** * Returns {@code true} if there is a class path associated with this * class loader. */ @@ -1042,4 +1072,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-08-12 15:01:19.693389125 -0700 +++ new/src/java.base/share/classes/jdk/internal/loader/ClassLoaders.java 2020-08-12 15:01:19.029364130 -0700 @@ -36,6 +36,7 @@ import jdk.internal.access.JavaLangAccess; import jdk.internal.access.SharedSecrets; import jdk.internal.misc.VM; +import jdk.internal.module.ServicesCatalog; /** * Creates and provides access to the built-in platform and application class @@ -56,14 +57,22 @@ // 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); - + ArchivedClassLoaders archivedClassLoaders = ArchivedClassLoaders.get(); + if (archivedClassLoaders != null) { + // assert VM.getSavedProperty("jdk.boot.class.path.append") == null + BOOT_LOADER = (BootClassLoader) archivedClassLoaders.bootLoader(); + PLATFORM_LOADER = (PlatformClassLoader) archivedClassLoaders.platformLoader(); + ServicesCatalog catalog = archivedClassLoaders.servicesCatalog(PLATFORM_LOADER); + ServicesCatalog.putServicesCatalog(PLATFORM_LOADER, catalog); + } else { + // -Xbootclasspath/a or -javaagent with Boot-Class-Path attribute + String append = VM.getSavedProperty("jdk.boot.class.path.append"); + URLClassPath ucp = (append != null && !append.isEmpty()) + ? new URLClassPath(append, true) + : null; + BOOT_LOADER = new BootClassLoader(ucp); + 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 +84,15 @@ cp = (initialModuleName == null) ? "" : null; } URLClassPath ucp = new URLClassPath(cp, false); - APP_LOADER = new AppClassLoader(PLATFORM_LOADER, ucp); + if (archivedClassLoaders != null) { + APP_LOADER = (AppClassLoader) archivedClassLoaders.appLoader(); + ServicesCatalog catalog = archivedClassLoaders.servicesCatalog(APP_LOADER); + ServicesCatalog.putServicesCatalog(APP_LOADER, catalog); + APP_LOADER.setClassPath(ucp); + } else { + APP_LOADER = new AppClassLoader(PLATFORM_LOADER, ucp); + ArchivedClassLoaders.archive(); + } } /** @@ -144,11 +161,8 @@ throw new InternalError(); } - final URLClassPath ucp; - - AppClassLoader(PlatformClassLoader parent, URLClassPath ucp) { + AppClassLoader(BuiltinClassLoader parent, URLClassPath ucp) { super("app", parent, ucp); - this.ucp = ucp; } @Override @@ -181,7 +195,7 @@ * @see java.lang.instrument.Instrumentation#appendToSystemClassLoaderSearch */ void appendToClassPathForInstrumentation(String path) { - ucp.addFile(path); + appendClassPath(path); } /** @@ -190,6 +204,13 @@ protected Package defineOrCheckPackage(String pn, Manifest man, URL url) { return super.defineOrCheckPackage(pn, man, url); } + + /** + * Called by the VM, during -Xshare:dump + */ + private void resetArchivedStates() { + setClassPath(null); + } } /** --- old/src/java.base/share/classes/jdk/internal/misc/VM.java 2020-08-12 15:01:22.537496182 -0700 +++ new/src/java.base/share/classes/jdk/internal/misc/VM.java 2020-08-12 15:01:21.745466369 -0700 @@ -468,6 +468,8 @@ */ public static native void initializeFromArchive(Class c); + public static native void defineArchivedModules(ClassLoader platformLoader, ClassLoader systemLoader); + public static native long getRandomSeedForCDSDump(); /** --- old/src/java.base/share/classes/jdk/internal/module/ArchivedModuleGraph.java 2020-08-12 15:01:25.405604143 -0700 +++ new/src/java.base/share/classes/jdk/internal/module/ArchivedModuleGraph.java 2020-08-12 15:01:24.613574329 -0700 @@ -22,21 +22,20 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ - package jdk.internal.module; -import java.lang.module.Configuration; -import java.lang.module.ModuleFinder; import java.util.Map; import java.util.Set; import java.util.function.Function; - +import java.lang.module.Configuration; +import java.lang.module.ModuleFinder; import jdk.internal.misc.VM; /** - * Used by ModuleBootstrap to obtain the archived system modules and finder. + * Used by ModuleBootstrap for archiving the configuration for the boot layer, + * the system module finder, and the maps used to create the IllegalAccessLogger. */ -final class ArchivedModuleGraph { +class ArchivedModuleGraph { private static ArchivedModuleGraph archivedModuleGraph; private final boolean hasSplitPackages; @@ -47,13 +46,13 @@ private final Map> concealedPackagesToOpen; private final Map> exportedPackagesToOpen; - public ArchivedModuleGraph(boolean hasSplitPackages, - boolean hasIncubatorModules, - ModuleFinder finder, - Configuration configuration, - Function classLoaderFunction, - Map> concealedPackagesToOpen, - Map> exportedPackagesToOpen) { + private ArchivedModuleGraph(boolean hasSplitPackages, + boolean hasIncubatorModules, + ModuleFinder finder, + Configuration configuration, + Function classLoaderFunction, + Map> concealedPackagesToOpen, + Map> exportedPackagesToOpen) { this.hasSplitPackages = hasSplitPackages; this.hasIncubatorModules = hasIncubatorModules; this.finder = finder; @@ -107,8 +106,20 @@ /** * Archive the module graph for the given initial module. */ - static void archive(ArchivedModuleGraph graph) { - archivedModuleGraph = graph; + static void archive(boolean hasSplitPackages, + boolean hasIncubatorModules, + ModuleFinder finder, + Configuration configuration, + Function classLoaderFunction, + Map> concealedPackagesToOpen, + Map> exportedPackagesToOpen) { + archivedModuleGraph = new ArchivedModuleGraph(hasSplitPackages, + hasIncubatorModules, + finder, + configuration, + classLoaderFunction, + concealedPackagesToOpen, + exportedPackagesToOpen); } static { --- old/src/java.base/share/classes/jdk/internal/module/IllegalAccessLogger.java 2020-08-12 15:01:28.745729871 -0700 +++ new/src/java.base/share/classes/jdk/internal/module/IllegalAccessLogger.java 2020-08-12 15:01:27.677689668 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 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 @@ -57,7 +57,7 @@ /** * Logger modes */ - public static enum Mode { + public enum Mode { /** * Prints a warning when an illegal access succeeds and then * discards the logger so that there is no further output. @@ -118,7 +118,7 @@ } /** - * Builds the IllegalAccessLogger and sets it as the system-wise logger. + * Builds the IllegalAccessLogger and sets it as the system-wide logger. */ public void complete() { Map> map1 = unmodifiableMap(moduleToConcealedPackages); --- old/src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.java 2020-08-12 15:01:32.145857858 -0700 +++ new/src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.java 2020-08-12 15:01:31.293825786 -0700 @@ -42,18 +42,19 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import java.util.NoSuchElementException; import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; -import jdk.internal.loader.BootLoader; -import jdk.internal.loader.BuiltinClassLoader; import jdk.internal.access.JavaLangAccess; import jdk.internal.access.JavaLangModuleAccess; import jdk.internal.access.SharedSecrets; +import jdk.internal.loader.BootLoader; +import jdk.internal.loader.BuiltinClassLoader; +import jdk.internal.loader.ClassLoaders; +import jdk.internal.misc.VM; import jdk.internal.perf.PerfCounter; /** @@ -86,8 +87,8 @@ private static final String ALL_MODULE_PATH = "ALL-MODULE-PATH"; // access to java.lang/module - private static final JavaLangModuleAccess JLMA - = SharedSecrets.getJavaLangModuleAccess(); + private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess(); + private static final JavaLangModuleAccess JLMA = SharedSecrets.getJavaLangModuleAccess(); // The ModulePatcher for the initial configuration private static final ModulePatcher patcher = initModulePatcher(); @@ -134,14 +135,58 @@ } /** - * Initialize the module system, returning the boot layer. + * Returns true if the archived boot layer can be used. The system properties + * are checked in the order that they are used by boot2. + */ + private static boolean canUseArchivedBootLayer() { + return getProperty("jdk.module.upgrade.path") == null && + getProperty("jdk.module.path") == null && + getProperty("jdk.module.patch.0") == null && // --patch-module + getProperty("jdk.module.main") == null && + getProperty("jdk.module.addmods.0") == null && // --add-modules + getProperty("jdk.module.limitmods") == null && + getProperty("jdk.module.addreads.0") == null && // --add-reads + getProperty("jdk.module.addexports.0") == null && // --add-exports + getProperty("jdk.module.addopens.0") == null && // --add-opens + getProperty("jdk.module.illegalAccess") == null; + } + + /** + * Initialize the module system, returning the boot layer. The boot layer + * is obtained from the CDS archive if possible, otherwise it is generated + * from the module graph. * * @see java.lang.System#initPhase2(boolean, boolean) */ - public static ModuleLayer boot() throws Exception { - + public static ModuleLayer boot() { Counters.start(); + ModuleLayer bootLayer; + ArchivedBootLayer archivedBootLayer = ArchivedBootLayer.get(); + if (archivedBootLayer != null) { + assert canUseArchivedBootLayer(); + bootLayer = archivedBootLayer.bootLayer(); + BootLoader.getUnnamedModule(); // trigger of BootLoader. + VM.defineArchivedModules(ClassLoaders.platformClassLoader(), ClassLoaders.appClassLoader()); + + // assume boot layer has at least one module providing a service + // that is mapped to the application class loader. + JLA.bindToLoader(bootLayer, ClassLoaders.appClassLoader()); + + // IllegalAccessLogger needs to be set + var illegalAccessLoggerBuilder = archivedBootLayer.illegalAccessLoggerBuilder(); + if (illegalAccessLoggerBuilder != null) { + illegalAccessLoggerBuilder.complete(); + } + } else { + bootLayer = boot2(); + } + + Counters.publish("jdk.module.boot.totalTime"); + return bootLayer; + } + + private static ModuleLayer boot2() { // Step 0: Command line options ModuleFinder upgradeModulePath = finderFor("jdk.module.upgrade.path"); @@ -428,11 +473,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 @@ -442,21 +488,21 @@ limitedFinder = new SafeModuleFinder(finder); } - // Module graph can be archived at CDS dump time. Only allow the - // unnamed module case for now. + // Archive module graph and boot layer can be archived at CDS dump time. + // Only allow the unnamed module case for now. if (canArchive && (mainModule == null)) { - ArchivedModuleGraph.archive( - new ArchivedModuleGraph(hasSplitPackages, - hasIncubatorModules, - systemModuleFinder, - cf, - clf, - concealedPackagesToOpen, - exportedPackagesToOpen)); - } + ArchivedModuleGraph.archive(hasSplitPackages, + hasIncubatorModules, + systemModuleFinder, + cf, + clf, + concealedPackagesToOpen, + exportedPackagesToOpen); - // total time to initialize - Counters.publish("jdk.module.boot.totalTime"); + if (!hasSplitPackages && !hasIncubatorModules) { + ArchivedBootLayer.archive(bootLayer, builder); + } + } return bootLayer; } @@ -751,17 +797,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 +820,7 @@ default: fail("Value specified to --illegal-access not recognized:" + " '" + value + "'"); - return; + return null; } } IllegalAccessLogger.Builder builder @@ -836,11 +883,11 @@ builder.logAccessToExportedPackages(m, exportedPackages); // open the packages to unnamed modules - JavaLangAccess jla = SharedSecrets.getJavaLangAccess(); - jla.addOpensToAllUnnamed(m, concealedPackages, exportedPackages); + JLA.addOpensToAllUnnamed(m, concealedPackages, exportedPackages); } builder.complete(); + return builder; } /** @@ -905,11 +952,19 @@ return decode(prefix, ",", true); } + + /** + * Gets the named system property + */ + private static String getProperty(String key) { + return System.getProperty(key); + } + /** * Gets and remove the named system property */ private static String getAndRemoveProperty(String key) { - return (String)System.getProperties().remove(key); + return (String) System.getProperties().remove(key); } /** --- old/src/java.base/share/classes/jdk/internal/module/ServicesCatalog.java 2020-08-12 15:01:34.537947900 -0700 +++ new/src/java.base/share/classes/jdk/internal/module/ServicesCatalog.java 2020-08-12 15:01:33.909924260 -0700 @@ -171,6 +171,16 @@ return catalog; } + /** + * Associates the given ServicesCatalog with the given class loader. + */ + public static void putServicesCatalog(ClassLoader loader, ServicesCatalog catalog) { + ServicesCatalog previous = CLV.putIfAbsent(loader, catalog); + if (previous != null) { + throw new InternalError(); + } + } + // the ServicesCatalog registered to a class loader private static final ClassLoaderValue CLV = new ClassLoaderValue<>(); } --- old/src/java.base/share/native/libjava/VM.c 2020-08-12 15:01:36.666028005 -0700 +++ new/src/java.base/share/native/libjava/VM.c 2020-08-12 15:01:36.050004817 -0700 @@ -62,6 +62,13 @@ JVM_InitializeFromArchive(env, c); } +JNIEXPORT void JNICALL +Java_jdk_internal_misc_VM_defineArchivedModules(JNIEnv *env, jclass ignore, + jobject platform_loader, + jobject system_loader) { + JVM_DefineArchivedModules(env, platform_loader, system_loader); +} + JNIEXPORT jlong JNICALL Java_jdk_internal_misc_VM_getRandomSeedForCDSDump(JNIEnv *env, jclass ignore) { return JVM_GetRandomSeedForCDSDump(); --- old/test/hotspot/jtreg/runtime/NMT/CheckForProperDetailStackTrace.java 2020-08-12 15:01:39.442132503 -0700 +++ new/test/hotspot/jtreg/runtime/NMT/CheckForProperDetailStackTrace.java 2020-08-12 15:01:38.650102689 -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-08-12 15:01:42.138233988 -0700 +++ new/test/hotspot/jtreg/runtime/cds/appcds/SpecifySysLoaderProp.java 2020-08-12 15:01:41.542211553 -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-08-12 15:01:44.730331559 -0700 +++ new/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/modulepath/OptimizeModuleHandlingTest.java 2020-08-12 15:01:44.110308221 -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-08-12 15:01:46.646403684 -0700 +++ new/test/hotspot/jtreg/runtime/logging/ModulesTest.java 2020-08-12 15:01:46.114383658 -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/src/hotspot/share/classfile/classLoaderDataShared.cpp 2020-08-12 15:01:48.242463763 -0700 @@ -0,0 +1,187 @@ + /* + * 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. + * + */ + +#include "precompiled.hpp" +#include "classfile/classLoaderData.inline.hpp" +#include "classfile/classLoaderDataShared.hpp" +#include "classfile/moduleEntry.hpp" +#include "classfile/packageEntry.hpp" +#include "logging/log.hpp" +#include "memory/metaspaceShared.hpp" + +class ArchivedClassLoaderData { + Array* _packages; + Array* _modules; + + void assert_valid(ClassLoaderData* loader_data) { + // loader_data may be NULL if the boot layer has loaded no modules for the platform or + // system loaders (e.g., if you create a custom JDK image with only java.base). + if (loader_data != NULL) { + assert(!loader_data->has_class_mirror_holder(), + "loaders for non-strong hidden classes or unsafe anonymous classes not supported"); + } + } +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 restore(ClassLoaderData* loader_data, bool do_entries, bool do_oops); +}; + +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"); + assert_valid(loader_data); + if (loader_data != NULL) { + // 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. + _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"); + assert_valid(loader_data); + if (loader_data != NULL) { + 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"); + 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); + if (_modules != NULL) { // Could be NULL if we have archived no modules for platform/system loaders + ModuleEntryTable* modules = loader_data->modules(); + PackageEntryTable* packages = loader_data->packages(); + + MutexLocker m1(Module_lock); + if (do_entries) { + modules->load_archived_entries(loader_data, _modules); + packages->load_archived_entries(_packages); + } + if (do_oops) { + modules->restore_archived_oops(loader_data, _modules); + } + } +} + +// ------------------------------ + +ClassLoaderData* ClassLoaderDataShared::null_class_loader_data() { + ClassLoaderData* loader_data = ClassLoaderData::the_null_class_loader_data(); + assert(loader_data != NULL, "must be"); + return loader_data; +} + +ClassLoaderData* ClassLoaderDataShared::java_platform_loader_data_or_null() { + return ClassLoaderData::class_loader_data_or_null(SystemDictionary::java_platform_loader()); +} + +ClassLoaderData* ClassLoaderDataShared::java_system_loader_data_or_null() { + return ClassLoaderData::class_loader_data_or_null(SystemDictionary::java_system_loader()); +} + +void ClassLoaderDataShared::allocate_archived_tables() { + assert(DumpSharedSpaces, "must be"); + if (MetaspaceShared::use_full_module_graph()) { + _archived_boot_loader_data.allocate (null_class_loader_data()); + _archived_platform_loader_data.allocate(java_platform_loader_data_or_null()); + _archived_system_loader_data.allocate (java_system_loader_data_or_null()); + } +} + +void ClassLoaderDataShared::init_archived_tables() { + assert(DumpSharedSpaces, "must be"); + if (MetaspaceShared::use_full_module_graph()) { + _archived_boot_loader_data.init_archived_entries (null_class_loader_data()); + _archived_platform_loader_data.init_archived_entries(java_platform_loader_data_or_null()); + _archived_system_loader_data.init_archived_entries (java_system_loader_data_or_null()); + _archived_javabase_moduleEntry = ModuleEntry::get_archived_entry(ModuleEntryTable::javabase_moduleEntry()); + } +} + +void ClassLoaderDataShared::init_archived_oops() { + assert(DumpSharedSpaces, "must be"); + if (MetaspaceShared::use_full_module_graph()) { + _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(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.restore(null_class_loader_data(), true, false); + ModuleEntryTable::set_javabase_moduleEntry(_archived_javabase_moduleEntry); + log_info(cds)("use_full_module_graph = true; java.base = " INTPTR_FORMAT, + p2i(_archived_javabase_moduleEntry)); + } +} + +void ClassLoaderDataShared::restore_archived_oops_for_null_class_loader_data() { + assert(UseSharedSpaces, "must be"); + if (MetaspaceShared::use_full_module_graph()) { + _archived_boot_loader_data.restore(null_class_loader_data(), false, true); + } +} + +void ClassLoaderDataShared::restore_java_platform_loader_from_archive(ClassLoaderData* loader_data) { + assert(UseSharedSpaces, "must be"); + assert(MetaspaceShared::use_full_module_graph(), "must be"); + _archived_platform_loader_data.restore(loader_data, true, true); +} + +void ClassLoaderDataShared::restore_java_system_loader_from_archive(ClassLoaderData* loader_data) { + assert(UseSharedSpaces, "must be"); + assert(MetaspaceShared::use_full_module_graph(), "must be"); + _archived_system_loader_data.restore(loader_data, true, true); +} --- /dev/null 2019-11-19 22:05:02.069813242 -0800 +++ new/src/hotspot/share/classfile/classLoaderDataShared.hpp 2020-08-12 15:01:50.622553354 -0700 @@ -0,0 +1,45 @@ + /* + * 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. + * + */ + +#if INCLUDE_CDS_JAVA_HEAP + +class SerializeClosure; +class ClassLoaderData; + +class ClassLoaderDataShared : AllStatic { +private: + static ClassLoaderData* null_class_loader_data(); + static ClassLoaderData* java_platform_loader_data_or_null(); + static ClassLoaderData* java_system_loader_data_or_null(); +public: + static void allocate_archived_tables(); + static void init_archived_tables(); + static void init_archived_oops(); + static void serialize(SerializeClosure* f); + static void restore_archived_oops_for_null_class_loader_data(); + static void restore_java_platform_loader_from_archive(ClassLoaderData* loader_data); + static void restore_java_system_loader_from_archive(ClassLoaderData* loader_data); +}; + +#endif // INCLUDE_CDS_JAVA_HEAP --- /dev/null 2019-11-19 22:05:02.069813242 -0800 +++ new/src/java.base/share/classes/jdk/internal/loader/ArchivedClassLoaders.java 2020-08-12 15:01:53.278653334 -0700 @@ -0,0 +1,96 @@ +/* + * 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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 jdk.internal.loader; + +import java.util.Map; +import jdk.internal.misc.VM; +import jdk.internal.module.ServicesCatalog; + +/** + * Used to archive the built-in class loaders, their services catalogs, and the + * package-to-module map used by the built-in class loaders. + */ +class ArchivedClassLoaders { + private static ArchivedClassLoaders archivedClassLoaders; + + private final ClassLoader bootLoader; + private final ClassLoader platformLoader; + private final ClassLoader appLoader; + private final ServicesCatalog[] servicesCatalogs; + private final Map packageToModule; + + private ArchivedClassLoaders() { + bootLoader = ClassLoaders.bootLoader(); + platformLoader = ClassLoaders.platformClassLoader(); + appLoader = ClassLoaders.appClassLoader(); + + servicesCatalogs = new ServicesCatalog[3]; + servicesCatalogs[0] = BootLoader.getServicesCatalog(); + servicesCatalogs[1] = ServicesCatalog.getServicesCatalog(platformLoader); + servicesCatalogs[2] = ServicesCatalog.getServicesCatalog(appLoader); + + packageToModule = BuiltinClassLoader.packageToModule(); + } + + ClassLoader bootLoader() { + return bootLoader; + } + + ClassLoader platformLoader() { + return platformLoader; + } + + ClassLoader appLoader() { + return appLoader; + } + + ServicesCatalog servicesCatalog(ClassLoader loader) { + if (loader == null) { + return servicesCatalogs[0]; + } else if (loader == platformLoader) { + return servicesCatalogs[1]; + } else if (loader == appLoader) { + return servicesCatalogs[2]; + } else { + throw new InternalError(); + } + } + + Map packageToModule() { + return packageToModule; + } + + static void archive() { + archivedClassLoaders = new ArchivedClassLoaders(); + } + + static ArchivedClassLoaders get() { + return archivedClassLoaders; + } + + static { + VM.initializeFromArchive(ArchivedClassLoaders.class); + } +} --- /dev/null 2019-11-19 22:05:02.069813242 -0800 +++ new/src/java.base/share/classes/jdk/internal/module/ArchivedBootLayer.java 2020-08-12 15:01:55.954754067 -0700 @@ -0,0 +1,64 @@ +/* + * 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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 jdk.internal.module; + +import jdk.internal.misc.VM; + +/** + * Used by ModuleBootstrap for archiving the boot layer and the builder needed to + * set the IllegalAccessLogger. + */ +class ArchivedBootLayer { + private static ArchivedBootLayer archivedBootLayer; + + private final ModuleLayer bootLayer; + private final IllegalAccessLogger.Builder builder; + + private ArchivedBootLayer(ModuleLayer bootLayer, + IllegalAccessLogger.Builder builder) { + this.bootLayer = bootLayer; + this.builder = builder; + } + + ModuleLayer bootLayer() { + return bootLayer; + } + + IllegalAccessLogger.Builder illegalAccessLoggerBuilder() { + return builder; + } + + static ArchivedBootLayer get() { + return archivedBootLayer; + } + + static void archive(ModuleLayer layer, IllegalAccessLogger.Builder builder) { + archivedBootLayer = new ArchivedBootLayer(layer, builder); + } + + static { + VM.initializeFromArchive(ArchivedBootLayer.class); + } +} --- /dev/null 2019-11-19 22:05:02.069813242 -0800 +++ new/test/hotspot/jtreg/runtime/NMT/src/jdk.test/module-info.java 2020-08-12 15:01:58.038832515 -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-08-12 15:02:00.474924214 -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-08-12 15:02:02.987018774 -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; + } +} --- /dev/null 2019-11-19 22:05:02.069813242 -0800 +++ new/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/NewModuleFinderTest.java 2020-08-12 15:02:05.239103546 -0700 @@ -0,0 +1,99 @@ +/* + * 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 + * @requires vm.cds + * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds + * @run driver NewModuleFinderTest + * @summary Make sure the archived module graph can co-exist with modules that are + * dynamically defined at runtime using the ModuleFinder API. + */ + +import java.lang.module.Configuration; +import java.lang.module.ModuleFinder; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Set; +import jdk.test.lib.cds.CDSTestUtils; +import jdk.test.lib.process.ProcessTools; +import jdk.test.lib.process.OutputAnalyzer; + +public class NewModuleFinderTest { + + private static final Path USER_DIR = Paths.get(System.getProperty("user.dir")); + private static final String TEST_SRC = System.getProperty("test.src"); + private static final Path SRC_DIR = Paths.get(TEST_SRC, "modulepath/src"); + private static final Path MODS_DIR = Paths.get("mods"); + + // the module name of the test module + private static final String TEST_MODULE = "com.simple"; + + // the module main class + private static final String MAIN_CLASS = "com.simple.Main"; + + private static final Set modules = Set.of(TEST_MODULE); + + public static void buildTestModule() throws Exception { + // javac -d mods/$TESTMODULE --module-path MOD_DIR modulepath/src/$TESTMODULE/** + JarBuilder.compileModule(SRC_DIR.resolve(TEST_MODULE), + MODS_DIR.resolve(TEST_MODULE), + MODS_DIR.toString()); + } + + public static void main(String... args) throws Exception { + // compile the modules and create the modular jar files + buildTestModule(); + + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder( + "-Xlog:cds", + "-Xlog:module=debug", + "NewModuleFinderTest$Helper"); + OutputAnalyzer out = CDSTestUtils.executeAndLog(pb, "exec"); + out.shouldHaveExitValue(0); + out.shouldContain("define_module(): creation of module: com.simple,"); + } + + static class Helper { + public static void main(String... args) { + ModuleFinder finder = ModuleFinder.of(MODS_DIR); + Configuration parent = ModuleLayer.boot().configuration(); + Configuration cf = parent.resolveAndBind(ModuleFinder.of(), + finder, + modules); + + ClassLoader scl = ClassLoader.getSystemClassLoader(); + ModuleLayer layer = ModuleLayer.boot().defineModulesWithManyLoaders(cf, scl); + + Module m1 = layer.findModule(TEST_MODULE).get(); + System.out.println("Module = " + m1); + if (m1 != null) { + System.out.println("Success"); + } else { + throw new RuntimeException("Module should not be null"); + } + } + } +}