8244778: Archive full module graph in CDS

Reviewed-by: erikj, coleenp, lfoltan, redestad, alanb, mchung
This commit is contained in:
Ioi Lam 2020-09-13 14:45:12 +00:00
parent 998ce78e53
commit 03a4df0acd
59 changed files with 2052 additions and 164 deletions

View File

@ -119,6 +119,7 @@ ifneq ($(call check-jvm-feature, cds), true)
archiveBuilder.cpp \ archiveBuilder.cpp \
archiveUtils.cpp \ archiveUtils.cpp \
classListParser.cpp \ classListParser.cpp \
classLoaderDataShared.cpp \
classLoaderExt.cpp \ classLoaderExt.cpp \
dumpAllocStats.cpp \ dumpAllocStats.cpp \
dynamicArchive.cpp \ dynamicArchive.cpp \

View File

@ -198,5 +198,6 @@ JVM_AddModuleExports
JVM_AddModuleExportsToAll JVM_AddModuleExportsToAll
JVM_AddModuleExportsToAllUnnamed JVM_AddModuleExportsToAllUnnamed
JVM_AddReadsModule JVM_AddReadsModule
JVM_DefineArchivedModules
JVM_DefineModule JVM_DefineModule
JVM_SetBootLoaderUnnamedModule JVM_SetBootLoaderUnnamedModule

View File

@ -1644,6 +1644,7 @@ void ClassLoader::create_javabase() {
{ {
MutexLocker ml(THREAD, Module_lock); MutexLocker ml(THREAD, Module_lock);
if (ModuleEntryTable::javabase_moduleEntry() == NULL) { // may have been inited by CDS.
ModuleEntry* jb_module = null_cld_modules->locked_create_entry(Handle(), ModuleEntry* jb_module = null_cld_modules->locked_create_entry(Handle(),
false, vmSymbols::java_base(), NULL, NULL, null_cld); false, vmSymbols::java_base(), NULL, NULL, null_cld);
if (jb_module == NULL) { if (jb_module == NULL) {
@ -1651,6 +1652,7 @@ void ClassLoader::create_javabase() {
} }
ModuleEntryTable::set_javabase_moduleEntry(jb_module); ModuleEntryTable::set_javabase_moduleEntry(jb_module);
} }
}
} }
// Please keep following two functions at end of this file. With them placed at top or in middle of the file, // Please keep following two functions at end of this file. With them placed at top or in middle of the file,

View File

@ -0,0 +1,202 @@
/*
* 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"
#include "runtime/handles.inline.hpp"
#if INCLUDE_CDS_JAVA_HEAP
class ArchivedClassLoaderData {
Array<PackageEntry*>* _packages;
Array<ModuleEntry*>* _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 iterate_symbols(ClassLoaderData* loader_data, MetaspaceClosure* closure);
void allocate(ClassLoaderData* loader_data);
void init_archived_entries(ClassLoaderData* loader_data);
void init_archived_oops(ClassLoaderData* loader_data);
void serialize(SerializeClosure* f) {
f->do_ptr((void**)&_packages);
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::iterate_symbols(ClassLoaderData* loader_data, MetaspaceClosure* closure) {
assert(DumpSharedSpaces, "must be");
assert_valid(loader_data);
if (loader_data != NULL) {
loader_data->packages()->iterate_symbols(closure);
loader_data->modules() ->iterate_symbols(closure);
}
}
void ArchivedClassLoaderData::allocate(ClassLoaderData* loader_data) {
assert(DumpSharedSpaces, "must be");
assert_valid(loader_data);
if (loader_data != NULL) {
// We can't create hashtables at dump time because the hashcode depends on the
// address of the Symbols, which may be relocated at runtime due to ASLR.
// So we store the packages/modules in Arrays. At runtime, we create
// the hashtables using these arrays.
_packages = loader_data->packages()->allocate_archived_entries();
_modules = loader_data->modules() ->allocate_archived_entries();
}
}
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);
}
}
}
// ------------------------------
static ClassLoaderData* null_class_loader_data() {
ClassLoaderData* loader_data = ClassLoaderData::the_null_class_loader_data();
assert(loader_data != NULL, "must be");
return loader_data;
}
static ClassLoaderData* java_platform_loader_data_or_null() {
return ClassLoaderData::class_loader_data_or_null(SystemDictionary::java_platform_loader());
}
static ClassLoaderData* java_system_loader_data_or_null() {
return ClassLoaderData::class_loader_data_or_null(SystemDictionary::java_system_loader());
}
void ClassLoaderDataShared::iterate_symbols(MetaspaceClosure* closure) {
assert(DumpSharedSpaces && MetaspaceShared::use_full_module_graph(), "must be");
_archived_boot_loader_data.iterate_symbols (null_class_loader_data(), closure);
_archived_platform_loader_data.iterate_symbols(java_platform_loader_data_or_null(), closure);
_archived_system_loader_data.iterate_symbols (java_system_loader_data_or_null(), closure);
}
void ClassLoaderDataShared::allocate_archived_tables() {
assert(DumpSharedSpaces && MetaspaceShared::use_full_module_graph(), "must be");
_archived_boot_loader_data.allocate (null_class_loader_data());
_archived_platform_loader_data.allocate(java_platform_loader_data_or_null());
_archived_system_loader_data.allocate (java_system_loader_data_or_null());
}
void ClassLoaderDataShared::init_archived_tables() {
assert(DumpSharedSpaces && MetaspaceShared::use_full_module_graph(), "must be");
_archived_boot_loader_data.init_archived_entries (null_class_loader_data());
_archived_platform_loader_data.init_archived_entries(java_platform_loader_data_or_null());
_archived_system_loader_data.init_archived_entries (java_system_loader_data_or_null());
_archived_javabase_moduleEntry = ModuleEntry::get_archived_entry(ModuleEntryTable::javabase_moduleEntry());
}
void ClassLoaderDataShared::init_archived_oops() {
assert(DumpSharedSpaces && MetaspaceShared::use_full_module_graph(), "must be");
_archived_boot_loader_data.init_archived_oops (null_class_loader_data());
_archived_platform_loader_data.init_archived_oops(java_platform_loader_data_or_null());
_archived_system_loader_data.init_archived_oops (java_system_loader_data_or_null());
}
void ClassLoaderDataShared::serialize(SerializeClosure* f) {
_archived_boot_loader_data.serialize(f);
_archived_platform_loader_data.serialize(f);
_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));
}
}
oop ClassLoaderDataShared::restore_archived_oops_for_null_class_loader_data() {
assert(UseSharedSpaces && MetaspaceShared::use_full_module_graph(), "must be");
_archived_boot_loader_data.restore(null_class_loader_data(), false, true);
return _archived_javabase_moduleEntry->module();
}
void ClassLoaderDataShared::restore_java_platform_loader_from_archive(ClassLoaderData* loader_data) {
assert(UseSharedSpaces && 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 && MetaspaceShared::use_full_module_graph(), "must be");
_archived_system_loader_data.restore(loader_data, true, true);
}
#endif // INCLUDE_CDS_JAVA_HEAP

View File

@ -0,0 +1,47 @@
/*
* 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.
*
*/
#ifndef SHARE_CLASSFILE_CLASSLOADERDATASHARED_HPP
#define SHARE_CLASSFILE_CLASSLOADERDATASHARED_HPP
#include "memory/allStatic.hpp"
#include "oops/oopsHierarchy.hpp"
class ClassLoaderData;
class MetaspaceClosure;
class SerializeClosure;
class ClassLoaderDataShared : AllStatic {
public:
static void allocate_archived_tables();
static void iterate_symbols(MetaspaceClosure* closure);
static void init_archived_tables();
static void init_archived_oops();
static void serialize(SerializeClosure* f);
static oop 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 // SHARE_CLASSFILE_CLASSLOADERDATASHARED_HPP

View File

@ -3392,12 +3392,17 @@ void java_lang_Module::set_name(oop module, oop value) {
module->obj_field_put(_name_offset, value); 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_entry_offset != 0, "Uninitialized module_entry_offset");
assert(module != NULL, "module can't be null"); assert(module != NULL, "module can't be null");
assert(oopDesc::is_oop(module), "module must be oop"); assert(oopDesc::is_oop(module), "module must be oop");
ModuleEntry* module_entry = (ModuleEntry*)module->address_field(_module_entry_offset); 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 (module_entry == NULL) {
// If the inject field containing the ModuleEntry* is null then return the // If the inject field containing the ModuleEntry* is null then return the
// class loader's unnamed module. // class loader's unnamed module.
@ -4821,9 +4826,8 @@ bool JavaClasses::is_supported_for_archiving(oop obj) {
Klass* klass = obj->klass(); Klass* klass = obj->klass();
if (klass == SystemDictionary::ClassLoader_klass() || // ClassLoader::loader_data is malloc'ed. 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 // 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 // regular Java code. The implementation of java.lang.invoke uses generated anonymous classes
// (e.g., as referenced by ResolvedMethodName::vmholder) that are not yet supported by CDS. // (e.g., as referenced by ResolvedMethodName::vmholder) that are not yet supported by CDS.
// So for now we cannot not support these classes for archiving. // So for now we cannot not support these classes for archiving.
// //

View File

@ -797,6 +797,7 @@ class java_lang_Module {
static void set_name(oop module, oop value); static void set_name(oop module, oop value);
static ModuleEntry* module_entry(oop module); static ModuleEntry* module_entry(oop module);
static ModuleEntry* module_entry_raw(oop module);
static void set_module_entry(oop module, ModuleEntry* module_entry); static void set_module_entry(oop module, ModuleEntry* module_entry);
friend class JavaClasses; friend class JavaClasses;

View File

@ -29,7 +29,11 @@
#include "classfile/javaClasses.inline.hpp" #include "classfile/javaClasses.inline.hpp"
#include "classfile/moduleEntry.hpp" #include "classfile/moduleEntry.hpp"
#include "logging/log.hpp" #include "logging/log.hpp"
#include "memory/archiveBuilder.hpp"
#include "memory/archiveUtils.hpp"
#include "memory/filemap.hpp" #include "memory/filemap.hpp"
#include "memory/heapShared.hpp"
#include "memory/metaspaceShared.hpp"
#include "memory/resourceArea.hpp" #include "memory/resourceArea.hpp"
#include "memory/universe.hpp" #include "memory/universe.hpp"
#include "oops/oopHandle.inline.hpp" #include "oops/oopHandle.inline.hpp"
@ -40,6 +44,8 @@
#include "utilities/growableArray.hpp" #include "utilities/growableArray.hpp"
#include "utilities/hashtable.inline.hpp" #include "utilities/hashtable.inline.hpp"
#include "utilities/ostream.hpp" #include "utilities/ostream.hpp"
#include "utilities/quickSort.hpp"
#include "utilities/resourceHash.hpp"
ModuleEntry* ModuleEntryTable::_javabase_module = NULL; ModuleEntry* ModuleEntryTable::_javabase_module = NULL;
@ -108,15 +114,15 @@ void ModuleEntry::set_version(Symbol* version) {
// Returns the shared ProtectionDomain // Returns the shared ProtectionDomain
oop ModuleEntry::shared_protection_domain() { oop ModuleEntry::shared_protection_domain() {
return _pd.resolve(); return _shared_pd.resolve();
} }
// Set the shared ProtectionDomain atomically // Set the shared ProtectionDomain atomically
void ModuleEntry::set_shared_protection_domain(ClassLoaderData *loader_data, void ModuleEntry::set_shared_protection_domain(ClassLoaderData *loader_data,
Handle pd_h) { Handle pd_h) {
// Create a handle for the shared ProtectionDomain and save it atomically. // Create a handle for the shared ProtectionDomain and save it atomically.
// init_handle_locked checks if someone beats us setting the _pd cache. // init_handle_locked checks if someone beats us setting the _shared_pd cache.
loader_data->init_handle_locked(_pd, pd_h); loader_data->init_handle_locked(_shared_pd, pd_h);
} }
// Returns true if this module can read module m // Returns true if this module can read module m
@ -362,6 +368,203 @@ ModuleEntryTable::~ModuleEntryTable() {
assert(new_entry_free_list() == NULL, "entry present on ModuleEntryTable's free list"); 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<const ModuleEntry*>,
primitive_equals<const ModuleEntry*>,
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;
}
// This function is used to archive ModuleEntry::_reads and PackageEntry::_qualified_exports.
// GrowableArray cannot be directly archived, as it needs to be expandable at runtime.
// Write it out as an Array, and convert it back to GrowableArray at runtime.
Array<ModuleEntry*>* ModuleEntry::write_growable_array(GrowableArray<ModuleEntry*>* array) {
Array<ModuleEntry*>* archived_array = NULL;
int length = (array == NULL) ? 0 : array->length();
if (length > 0) {
archived_array = MetaspaceShared::new_ro_array<ModuleEntry*>(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*>* ModuleEntry::restore_growable_array(Array<ModuleEntry*>* archived_array) {
GrowableArray<ModuleEntry*>* array = NULL;
int length = (archived_array == NULL) ? 0 : archived_array->length();
if (length > 0) {
array = new (ResourceObj::C_HEAP, mtModule)GrowableArray<ModuleEntry*>(length, mtModule);
for (int i = 0; i < length; i++) {
ModuleEntry* archived_entry = archived_array->at(i);
array->append(archived_entry);
}
}
return array;
}
void ModuleEntry::iterate_symbols(MetaspaceClosure* closure) {
closure->push(literal_addr()); // name
closure->push(&_version);
closure->push(&_location);
}
void ModuleEntry::init_as_archived_entry() {
Array<ModuleEntry*>* archived_reads = write_growable_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(ArchiveBuilder::get_relocated_symbol(literal()));
ArchivePtrMarker::mark_pointer((address*)literal_addr());
}
_reads = (GrowableArray<ModuleEntry*>*)archived_reads;
if (_version != NULL) {
_version = ArchiveBuilder::get_relocated_symbol(_version);
}
if (_location != NULL) {
_location = ArchiveBuilder::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 = restore_growable_array((Array<ModuleEntry*>*)_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) {
assert(a == b || a->name() != b->name(), "no duplicated names");
return a->name()->fast_compare(b->name());
}
void ModuleEntryTable::iterate_symbols(MetaspaceClosure* closure) {
for (int i = 0; i < table_size(); ++i) {
for (ModuleEntry* m = bucket(i); m != NULL; m = m->next()) {
m->iterate_symbols(closure);
}
}
}
Array<ModuleEntry*>* ModuleEntryTable::allocate_archived_entries() {
Array<ModuleEntry*>* archived_modules = MetaspaceShared::new_rw_array<ModuleEntry*>(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<ModuleEntry*>* 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<ModuleEntry*>* 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<ModuleEntry*>* 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<ModuleEntry*>* 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, ModuleEntry* ModuleEntryTable::new_entry(unsigned int hash, Handle module_handle,
bool is_open, Symbol* name, bool is_open, Symbol* name,
Symbol* version, Symbol* location, Symbol* version, Symbol* location,

View File

@ -47,6 +47,8 @@
#define JAVA_BASE_NAME "java.base" #define JAVA_BASE_NAME "java.base"
#define JAVA_BASE_NAME_LEN 9 #define JAVA_BASE_NAME_LEN 9
template <class T> class Array;
class MetaspaceClosure;
class ModuleClosure; class ModuleClosure;
// A ModuleEntry describes a module that has been defined by a call to JVM_DefineModule. // A ModuleEntry describes a module that has been defined by a call to JVM_DefineModule.
@ -63,7 +65,7 @@ class ModuleClosure;
class ModuleEntry : public HashtableEntry<Symbol*, mtModule> { class ModuleEntry : public HashtableEntry<Symbol*, mtModule> {
private: private:
OopHandle _module; // java.lang.Module OopHandle _module; // java.lang.Module
OopHandle _pd; // java.security.ProtectionDomain, cached OopHandle _shared_pd; // java.security.ProtectionDomain, cached
// for shared classes from this module // for shared classes from this module
ClassLoaderData* _loader_data; ClassLoaderData* _loader_data;
GrowableArray<ModuleEntry*>* _reads; // list of modules that are readable by this module GrowableArray<ModuleEntry*>* _reads; // list of modules that are readable by this module
@ -75,13 +77,15 @@ private:
bool _must_walk_reads; // walk module's reads list at GC safepoints to purge out dead modules 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_open; // whether the packages in the module are all unqualifiedly exported
bool _is_patched; // whether the module is patched via --patch-module 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;) JFR_ONLY(DEFINE_TRACE_ID_FIELD;)
enum {MODULE_READS_SIZE = 101}; // Initial size of list of modules that the module can read. enum {MODULE_READS_SIZE = 101}; // Initial size of list of modules that the module can read.
public: public:
void init() { void init() {
_module = OopHandle(); _module = OopHandle();
_pd = OopHandle(); _shared_pd = OopHandle();
_loader_data = NULL; _loader_data = NULL;
_reads = NULL; _reads = NULL;
_version = NULL; _version = NULL;
@ -188,6 +192,18 @@ public:
CDS_ONLY(int shared_path_index() { return _shared_path_index;}) CDS_ONLY(int shared_path_index() { return _shared_path_index;})
JFR_ONLY(DEFINE_TRACE_ID_METHODS;) JFR_ONLY(DEFINE_TRACE_ID_METHODS;)
#if INCLUDE_CDS_JAVA_HEAP
void iterate_symbols(MetaspaceClosure* closure);
ModuleEntry* allocate_archived_entry() const;
void init_as_archived_entry();
void init_archived_oops();
static ModuleEntry* get_archived_entry(ModuleEntry* orig_entry);
static Array<ModuleEntry*>* write_growable_array(GrowableArray<ModuleEntry*>* array);
static GrowableArray<ModuleEntry*>* restore_growable_array(Array<ModuleEntry*>* archived_array);
void load_from_archive(ClassLoaderData* loader_data);
void restore_archive_oops(ClassLoaderData* loader_data);
#endif
}; };
// Iterator interface // Iterator interface
@ -270,6 +286,17 @@ public:
void print(outputStream* st = tty); void print(outputStream* st = tty);
void verify(); void verify();
#if INCLUDE_CDS_JAVA_HEAP
void iterate_symbols(MetaspaceClosure* closure);
Array<ModuleEntry*>* allocate_archived_entries();
void init_archived_entries(Array<ModuleEntry*>* archived_modules);
void init_archived_oops(Array<ModuleEntry*>* archived_modules);
void load_archived_entries(ClassLoaderData* loader_data,
Array<ModuleEntry*>* archived_modules);
void restore_archived_oops(ClassLoaderData* loader_data,
Array<ModuleEntry*>* archived_modules);
#endif
}; };
#endif // SHARE_CLASSFILE_MODULEENTRY_HPP #endif // SHARE_CLASSFILE_MODULEENTRY_HPP

View File

@ -27,6 +27,7 @@
#include "classfile/classFileParser.hpp" #include "classfile/classFileParser.hpp"
#include "classfile/classLoader.hpp" #include "classfile/classLoader.hpp"
#include "classfile/classLoaderData.inline.hpp" #include "classfile/classLoaderData.inline.hpp"
#include "classfile/classLoaderDataShared.hpp"
#include "classfile/javaAssertions.hpp" #include "classfile/javaAssertions.hpp"
#include "classfile/javaClasses.hpp" #include "classfile/javaClasses.hpp"
#include "classfile/javaClasses.inline.hpp" #include "classfile/javaClasses.inline.hpp"
@ -39,7 +40,9 @@
#include "classfile/vmSymbols.hpp" #include "classfile/vmSymbols.hpp"
#include "logging/log.hpp" #include "logging/log.hpp"
#include "logging/logStream.hpp" #include "logging/logStream.hpp"
#include "memory/metaspaceShared.hpp"
#include "memory/resourceArea.hpp" #include "memory/resourceArea.hpp"
#include "prims/jvmtiExport.hpp"
#include "runtime/handles.inline.hpp" #include "runtime/handles.inline.hpp"
#include "runtime/javaCalls.hpp" #include "runtime/javaCalls.hpp"
#include "runtime/jniHandles.inline.hpp" #include "runtime/jniHandles.inline.hpp"
@ -268,6 +271,7 @@ void throw_dup_pkg_exception(const char* module_name, PackageEntry* package, TRA
void Modules::define_module(jobject module, jboolean is_open, jstring version, void Modules::define_module(jobject module, jboolean is_open, jstring version,
jstring location, jobjectArray packages, TRAPS) { jstring location, jobjectArray packages, TRAPS) {
check_cds_restrictions(CHECK);
ResourceMark rm(THREAD); ResourceMark rm(THREAD);
if (module == NULL) { if (module == NULL) {
@ -450,6 +454,46 @@ void Modules::define_module(jobject module, jboolean is_open, jstring version,
} }
} }
#if INCLUDE_CDS_JAVA_HEAP
void Modules::define_archived_modules(jobject platform_loader, jobject system_loader, TRAPS) {
assert(UseSharedSpaces && MetaspaceShared::use_full_module_graph(), "must be");
// We don't want the classes used by the archived full module graph to be redefined by JVMTI.
// Luckily, such classes are loaded in the JVMTI "early" phase, and CDS is disabled if a JVMTI
// agent wants to redefine classes in this phase.
JVMTI_ONLY(assert(JvmtiExport::is_early_phase(), "must be"));
assert(!(JvmtiExport::should_post_class_file_load_hook() && JvmtiExport::has_early_class_hook_env()),
"CDS should be disabled if early class hooks are enabled");
Handle java_base_module(THREAD, ClassLoaderDataShared::restore_archived_oops_for_null_class_loader_data());
// Patch any previously loaded class's module field with java.base's java.lang.Module.
ModuleEntryTable::patch_javabase_entries(java_base_module);
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");
}
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);
}
void Modules::check_cds_restrictions(TRAPS) {
if (DumpSharedSpaces && Universe::is_module_initialized() && MetaspaceShared::use_full_module_graph()) {
THROW_MSG(vmSymbols::java_lang_UnsupportedOperationException(),
"During -Xshare:dump, module system cannot be modified after it's initialized");
}
}
#endif // INCLUDE_CDS_JAVA_HEAP
void Modules::set_bootloader_unnamed_module(jobject module, TRAPS) { void Modules::set_bootloader_unnamed_module(jobject module, TRAPS) {
ResourceMark rm(THREAD); ResourceMark rm(THREAD);
@ -488,6 +532,7 @@ void Modules::set_bootloader_unnamed_module(jobject module, TRAPS) {
} }
void Modules::add_module_exports(jobject from_module, jstring package_name, jobject to_module, TRAPS) { void Modules::add_module_exports(jobject from_module, jstring package_name, jobject to_module, TRAPS) {
check_cds_restrictions(CHECK);
if (package_name == NULL) { if (package_name == NULL) {
THROW_MSG(vmSymbols::java_lang_NullPointerException(), THROW_MSG(vmSymbols::java_lang_NullPointerException(),
@ -555,6 +600,7 @@ void Modules::add_module_exports(jobject from_module, jstring package_name, jobj
void Modules::add_module_exports_qualified(jobject from_module, jstring package, void Modules::add_module_exports_qualified(jobject from_module, jstring package,
jobject to_module, TRAPS) { jobject to_module, TRAPS) {
check_cds_restrictions(CHECK);
if (to_module == NULL) { if (to_module == NULL) {
THROW_MSG(vmSymbols::java_lang_NullPointerException(), THROW_MSG(vmSymbols::java_lang_NullPointerException(),
"to_module is null"); "to_module is null");
@ -563,6 +609,7 @@ void Modules::add_module_exports_qualified(jobject from_module, jstring package,
} }
void Modules::add_reads_module(jobject from_module, jobject to_module, TRAPS) { void Modules::add_reads_module(jobject from_module, jobject to_module, TRAPS) {
check_cds_restrictions(CHECK);
if (from_module == NULL) { if (from_module == NULL) {
THROW_MSG(vmSymbols::java_lang_NullPointerException(), THROW_MSG(vmSymbols::java_lang_NullPointerException(),
"from_module is null"); "from_module is null");
@ -668,6 +715,7 @@ jobject Modules::get_named_module(Handle h_loader, const char* package_name, TRA
// Export package in module to all unnamed modules. // Export package in module to all unnamed modules.
void Modules::add_module_exports_to_all_unnamed(jobject module, jstring package_name, TRAPS) { void Modules::add_module_exports_to_all_unnamed(jobject module, jstring package_name, TRAPS) {
check_cds_restrictions(CHECK);
if (module == NULL) { if (module == NULL) {
THROW_MSG(vmSymbols::java_lang_NullPointerException(), THROW_MSG(vmSymbols::java_lang_NullPointerException(),
"module is null"); "module is null");

View File

@ -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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -32,6 +32,7 @@ class ModuleEntryTable;
class Symbol; class Symbol;
class Modules : AllStatic { class Modules : AllStatic {
static void check_cds_restrictions(TRAPS) NOT_CDS_JAVA_HEAP_RETURN;
public: public:
// define_module defines a module containing the specified packages. It binds the // define_module defines a module containing the specified packages. It binds the
@ -52,6 +53,9 @@ public:
static void define_module(jobject module, jboolean is_open, jstring version, static void define_module(jobject module, jboolean is_open, jstring version,
jstring location, jobjectArray packages, TRAPS); 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 // Provides the java.lang.Module for the unnamed module defined
// to the boot loader. // to the boot loader.
// //

View File

@ -26,7 +26,11 @@
#include "classfile/moduleEntry.hpp" #include "classfile/moduleEntry.hpp"
#include "classfile/packageEntry.hpp" #include "classfile/packageEntry.hpp"
#include "logging/log.hpp" #include "logging/log.hpp"
#include "memory/archiveBuilder.hpp"
#include "memory/archiveUtils.hpp"
#include "memory/metaspaceShared.hpp"
#include "memory/resourceArea.hpp" #include "memory/resourceArea.hpp"
#include "oops/array.hpp"
#include "oops/symbol.hpp" #include "oops/symbol.hpp"
#include "runtime/java.hpp" #include "runtime/java.hpp"
#include "runtime/handles.inline.hpp" #include "runtime/handles.inline.hpp"
@ -34,6 +38,8 @@
#include "utilities/growableArray.hpp" #include "utilities/growableArray.hpp"
#include "utilities/hashtable.inline.hpp" #include "utilities/hashtable.inline.hpp"
#include "utilities/ostream.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 // Returns true if this package specifies m as a qualified export, including through an unnamed export
bool PackageEntry::is_qexported_to(ModuleEntry* m) const { bool PackageEntry::is_qexported_to(ModuleEntry* m) const {
@ -189,6 +195,126 @@ PackageEntryTable::~PackageEntryTable() {
assert(new_entry_free_list() == NULL, "entry present on PackageEntryTable's free list"); 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<const PackageEntry*>,
primitive_equals<const PackageEntry*>,
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::iterate_symbols(MetaspaceClosure* closure) {
closure->push(literal_addr()); // name
}
void PackageEntry::init_as_archived_entry() {
Array<ModuleEntry*>* archived_qualified_exports = ModuleEntry::write_growable_array(_qualified_exports);
set_next(NULL);
set_literal(ArchiveBuilder::get_relocated_symbol(literal()));
set_hash(0x0); // re-init at runtime
_module = ModuleEntry::get_archived_entry(_module);
_qualified_exports = (GrowableArray<ModuleEntry*>*)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::restore_growable_array((Array<ModuleEntry*>*)_qualified_exports);
JFR_ONLY(INIT_ID(this);)
}
static int compare_package_by_name(PackageEntry* a, PackageEntry* b) {
assert(a == b || a->name() != b->name(), "no duplicated names");
return a->name()->fast_compare(b->name());
}
void PackageEntryTable::iterate_symbols(MetaspaceClosure* closure) {
for (int i = 0; i < table_size(); ++i) {
for (PackageEntry* p = bucket(i); p != NULL; p = p->next()) {
p->iterate_symbols(closure);
}
}
}
Array<PackageEntry*>* 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<PackageEntry*>* archived_packages = MetaspaceShared::new_rw_array<PackageEntry*>(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<PackageEntry*>* 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<PackageEntry*>* 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) { PackageEntry* PackageEntryTable::new_entry(unsigned int hash, Symbol* name, ModuleEntry* module) {
assert(Module_lock->owned_by_self(), "should have the Module_lock"); assert(Module_lock->owned_by_self(), "should have the Module_lock");
PackageEntry* entry = (PackageEntry*)Hashtable<Symbol*, mtModule>::allocate_new_entry(hash, name); PackageEntry* entry = (PackageEntry*)Hashtable<Symbol*, mtModule>::allocate_new_entry(hash, name);
@ -274,7 +400,6 @@ void PackageEntryTable::verify_javabase_packages(GrowableArray<Symbol*> *pkg_lis
} }
} }
} }
} }
// iteration of qualified exports // iteration of qualified exports

View File

@ -36,6 +36,8 @@
#include "jfr/support/jfrTraceIdExtension.hpp" #include "jfr/support/jfrTraceIdExtension.hpp"
#endif #endif
template <class T> class Array;
class MetaspaceClosure;
// A PackageEntry basically represents a Java package. It contains: // A PackageEntry basically represents a Java package. It contains:
// - Symbol* containing the package's name. // - Symbol* containing the package's name.
@ -217,6 +219,14 @@ public:
void print(outputStream* st = tty); void print(outputStream* st = tty);
void verify(); void verify();
#if INCLUDE_CDS_JAVA_HEAP
void iterate_symbols(MetaspaceClosure* closure);
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() { static int max_index_for_defined_in_class_path() {
return sizeof(int) * BitsPerByte; return sizeof(int) * BitsPerByte;
} }
@ -295,6 +305,13 @@ public:
void print(outputStream* st = tty); void print(outputStream* st = tty);
void verify(); void verify();
#if INCLUDE_CDS_JAVA_HEAP
void iterate_symbols(MetaspaceClosure* closure);
Array<PackageEntry*>* allocate_archived_entries();
void init_archived_entries(Array<PackageEntry*>* archived_packages);
void load_archived_entries(Array<PackageEntry*>* archived_packages);
#endif
}; };
#endif // SHARE_CLASSFILE_PACKAGEENTRY_HPP #endif // SHARE_CLASSFILE_PACKAGEENTRY_HPP

View File

@ -187,10 +187,13 @@ class TableStatistics;
do_klass(ByteArrayInputStream_klass, java_io_ByteArrayInputStream ) \ do_klass(ByteArrayInputStream_klass, java_io_ByteArrayInputStream ) \
do_klass(URL_klass, java_net_URL ) \ do_klass(URL_klass, java_net_URL ) \
do_klass(Jar_Manifest_klass, java_util_jar_Manifest ) \ 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_klass, jdk_internal_loader_ClassLoaders ) \
do_klass(jdk_internal_loader_ClassLoaders_AppClassLoader_klass, jdk_internal_loader_ClassLoaders_AppClassLoader) \ 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(jdk_internal_loader_ClassLoaders_PlatformClassLoader_klass, jdk_internal_loader_ClassLoaders_PlatformClassLoader) \
do_klass(CodeSource_klass, java_security_CodeSource ) \ 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 ) \ do_klass(StackTraceElement_klass, java_lang_StackTraceElement ) \
\ \

View File

@ -130,6 +130,7 @@
template(java_lang_Record, "java/lang/Record") \ template(java_lang_Record, "java/lang/Record") \
\ \
template(jdk_internal_loader_NativeLibraries, "jdk/internal/loader/NativeLibraries") \ 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_AppClassLoader, "jdk/internal/loader/ClassLoaders$AppClassLoader") \
template(jdk_internal_loader_ClassLoaders_PlatformClassLoader, "jdk/internal/loader/ClassLoaders$PlatformClassLoader") \ template(jdk_internal_loader_ClassLoaders_PlatformClassLoader, "jdk/internal/loader/ClassLoaders$PlatformClassLoader") \
\ \
@ -664,6 +665,8 @@
\ \
/* cds */ \ /* cds */ \
template(jdk_internal_loader_ClassLoaders, "jdk/internal/loader/ClassLoaders") \ 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_name, "toFileURL") \
template(toFileURL_signature, "(Ljava/lang/String;)Ljava/net/URL;") \ template(toFileURL_signature, "(Ljava/lang/String;)Ljava/net/URL;") \
template(url_void_signature, "(Ljava/net/URL;)V") \ template(url_void_signature, "(Ljava/net/URL;)V") \

View File

@ -490,6 +490,14 @@ JVM_AddModuleExportsToAll(JNIEnv *env, jobject from_module, jstring package);
JNIEXPORT void JNICALL JNIEXPORT void JNICALL
JVM_AddReadsModule(JNIEnv *env, jobject from_module, jobject source_module); 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 * Reflection support functions
*/ */

View File

@ -23,6 +23,7 @@
*/ */
#include "precompiled.hpp" #include "precompiled.hpp"
#include "classfile/classLoaderDataShared.hpp"
#include "classfile/systemDictionaryShared.hpp" #include "classfile/systemDictionaryShared.hpp"
#include "logging/log.hpp" #include "logging/log.hpp"
#include "logging/logMessage.hpp" #include "logging/logMessage.hpp"
@ -218,6 +219,11 @@ void ArchiveBuilder::gather_klasses_and_symbols() {
log_info(cds)("Gathering classes and symbols ... "); log_info(cds)("Gathering classes and symbols ... ");
GatherKlassesAndSymbols doit(this); GatherKlassesAndSymbols doit(this);
iterate_roots(&doit, /*is_relocating_pointers=*/false); iterate_roots(&doit, /*is_relocating_pointers=*/false);
#if INCLUDE_CDS_JAVA_HEAP
if (DumpSharedSpaces && MetaspaceShared::use_full_module_graph()) {
ClassLoaderDataShared::iterate_symbols(&doit);
}
#endif
doit.finish(); doit.finish();
log_info(cds)("Number of classes %d", _num_instance_klasses + _num_obj_array_klasses + _num_type_array_klasses); log_info(cds)("Number of classes %d", _num_instance_klasses + _num_obj_array_klasses + _num_type_array_klasses);

View File

@ -39,6 +39,7 @@ public:
f(SymbolBucket) \ f(SymbolBucket) \
f(StringHashentry) \ f(StringHashentry) \
f(StringBucket) \ f(StringBucket) \
f(ModulesNatives) \
f(Other) f(Other)
enum Type { enum Type {
@ -74,6 +75,11 @@ public:
_bytes [which][type] += byte_size; _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) { void record_other_type(int byte_size, bool read_only) {
int which = (read_only) ? RO : RW; int which = (read_only) ? RO : RW;
_bytes [which][OtherType] += byte_size; _bytes [which][OtherType] += byte_size;

View File

@ -218,6 +218,7 @@ void FileMapHeader::populate(FileMapInfo* mapinfo, size_t alignment) {
_max_heap_size = MaxHeapSize; _max_heap_size = MaxHeapSize;
_narrow_klass_shift = CompressedKlassPointers::shift(); _narrow_klass_shift = CompressedKlassPointers::shift();
_use_optimized_module_handling = MetaspaceShared::use_optimized_module_handling(); _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 // The following fields are for sanity checks for whether this archive
// will function correctly with this JVM and the bootclasspath it's // will function correctly with this JVM and the bootclasspath it's
@ -2179,7 +2180,12 @@ bool FileMapHeader::validate() {
if (!_use_optimized_module_handling) { if (!_use_optimized_module_handling) {
MetaspaceShared::disable_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; return true;

View File

@ -233,6 +233,7 @@ class FileMapHeader: private CDSFileMapHeaderBase {
bool _allow_archiving_with_java_agent; // setting of the AllowArchivingWithJavaAgent option 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 bool _use_optimized_module_handling;// No module-relation VM options were specified, so we can skip
// some expensive operations. // 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 size_t _ptrmap_size_in_bits; // Size of pointer relocation bitmap
char* from_mapped_offset(size_t offset) const { char* from_mapped_offset(size_t offset) const {

View File

@ -23,9 +23,13 @@
*/ */
#include "precompiled.hpp" #include "precompiled.hpp"
#include "classfile/classLoaderData.hpp"
#include "classfile/classLoaderDataShared.hpp"
#include "classfile/javaClasses.inline.hpp" #include "classfile/javaClasses.inline.hpp"
#include "classfile/moduleEntry.hpp"
#include "classfile/stringTable.hpp" #include "classfile/stringTable.hpp"
#include "classfile/symbolTable.hpp" #include "classfile/symbolTable.hpp"
#include "classfile/systemDictionary.hpp"
#include "classfile/systemDictionaryShared.hpp" #include "classfile/systemDictionaryShared.hpp"
#include "classfile/vmSymbols.hpp" #include "classfile/vmSymbols.hpp"
#include "gc/shared/gcLocker.hpp" #include "gc/shared/gcLocker.hpp"
@ -45,6 +49,7 @@
#include "oops/fieldStreams.inline.hpp" #include "oops/fieldStreams.inline.hpp"
#include "oops/oop.inline.hpp" #include "oops/oop.inline.hpp"
#include "runtime/fieldDescriptor.inline.hpp" #include "runtime/fieldDescriptor.inline.hpp"
#include "runtime/javaCalls.hpp"
#include "runtime/safepointVerifiers.hpp" #include "runtime/safepointVerifiers.hpp"
#include "utilities/bitMap.inline.hpp" #include "utilities/bitMap.inline.hpp"
#if INCLUDE_G1GC #if INCLUDE_G1GC
@ -84,10 +89,19 @@ static ArchivableStaticFieldInfo open_archive_subgraph_entry_fields[] = {
{"jdk/internal/math/FDBigInteger", "archivedCaches"}, {"jdk/internal/math/FDBigInteger", "archivedCaches"},
}; };
// Entry fields for subgraphs archived in the open archive heap region (full module graph).
static ArchivableStaticFieldInfo fmg_open_archive_subgraph_entry_fields[] = {
{"jdk/internal/loader/ArchivedClassLoaders", "archivedClassLoaders"},
{"jdk/internal/module/ArchivedBootLayer", "archivedBootLayer"},
{"java/lang/Module$ArchivedData", "archivedData"},
};
const static int num_closed_archive_subgraph_entry_fields = const static int num_closed_archive_subgraph_entry_fields =
sizeof(closed_archive_subgraph_entry_fields) / sizeof(ArchivableStaticFieldInfo); sizeof(closed_archive_subgraph_entry_fields) / sizeof(ArchivableStaticFieldInfo);
const static int num_open_archive_subgraph_entry_fields = const static int num_open_archive_subgraph_entry_fields =
sizeof(open_archive_subgraph_entry_fields) / sizeof(ArchivableStaticFieldInfo); sizeof(open_archive_subgraph_entry_fields) / sizeof(ArchivableStaticFieldInfo);
const static int num_fmg_open_archive_subgraph_entry_fields =
sizeof(fmg_open_archive_subgraph_entry_fields) / sizeof(ArchivableStaticFieldInfo);
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
// //
@ -108,6 +122,36 @@ unsigned HeapShared::oop_hash(oop const& p) {
return hash; 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; HeapShared::ArchivedObjectCache* HeapShared::_archived_object_cache = NULL;
oop HeapShared::find_archived_heap_object(oop obj) { oop HeapShared::find_archived_heap_object(oop obj) {
assert(DumpSharedSpaces, "dump-time only"); assert(DumpSharedSpaces, "dump-time only");
@ -238,6 +282,10 @@ void HeapShared::archive_java_heap_objects(GrowableArray<MemRegion> *closed,
log_info(cds)("Dumping objects to open archive heap region ..."); log_info(cds)("Dumping objects to open archive heap region ...");
copy_open_archive_heap_objects(open); copy_open_archive_heap_objects(open);
if (MetaspaceShared::use_full_module_graph()) {
ClassLoaderDataShared::init_archived_oops();
}
destroy_archived_object_cache(); destroy_archived_object_cache();
} }
@ -256,7 +304,9 @@ void HeapShared::copy_closed_archive_heap_objects(
archive_object_subgraphs(closed_archive_subgraph_entry_fields, archive_object_subgraphs(closed_archive_subgraph_entry_fields,
num_closed_archive_subgraph_entry_fields, num_closed_archive_subgraph_entry_fields,
true /* is_closed_archive */, THREAD); true /* is_closed_archive */,
false /* is_full_module_graph */,
THREAD);
G1CollectedHeap::heap()->end_archive_alloc_range(closed_archive, G1CollectedHeap::heap()->end_archive_alloc_range(closed_archive,
os::vm_allocation_granularity()); os::vm_allocation_granularity());
@ -276,7 +326,15 @@ void HeapShared::copy_open_archive_heap_objects(
archive_object_subgraphs(open_archive_subgraph_entry_fields, archive_object_subgraphs(open_archive_subgraph_entry_fields,
num_open_archive_subgraph_entry_fields, num_open_archive_subgraph_entry_fields,
false /* is_closed_archive */, false /* is_closed_archive */,
false /* is_full_module_graph */,
THREAD); THREAD);
if (MetaspaceShared::use_full_module_graph()) {
archive_object_subgraphs(fmg_open_archive_subgraph_entry_fields,
num_fmg_open_archive_subgraph_entry_fields,
false /* is_closed_archive */,
true /* is_full_module_graph */,
THREAD);
}
G1CollectedHeap::heap()->end_archive_alloc_range(open_archive, G1CollectedHeap::heap()->end_archive_alloc_range(open_archive,
os::vm_allocation_granularity()); os::vm_allocation_granularity());
@ -296,15 +354,22 @@ HeapShared::RunTimeKlassSubGraphInfoTable HeapShared::_run_time_subgraph_info_
// Get the subgraph_info for Klass k. A new subgraph_info is created if // Get the subgraph_info for Klass k. A new subgraph_info is created if
// there is no existing one for k. The subgraph_info records the relocated // there is no existing one for k. The subgraph_info records the relocated
// Klass* of the original k. // Klass* of the original k.
KlassSubGraphInfo* HeapShared::init_subgraph_info(Klass* k, bool is_full_module_graph) {
assert(DumpSharedSpaces, "dump time only");
bool created;
Klass* relocated_k = MetaspaceShared::get_relocated_klass(k);
KlassSubGraphInfo* info =
_dump_time_subgraph_info_table->put_if_absent(relocated_k, KlassSubGraphInfo(relocated_k, is_full_module_graph),
&created);
assert(created, "must not initialize twice");
return info;
}
KlassSubGraphInfo* HeapShared::get_subgraph_info(Klass* k) { KlassSubGraphInfo* HeapShared::get_subgraph_info(Klass* k) {
assert(DumpSharedSpaces, "dump time only"); assert(DumpSharedSpaces, "dump time only");
Klass* relocated_k = MetaspaceShared::get_relocated_klass(k); Klass* relocated_k = MetaspaceShared::get_relocated_klass(k);
KlassSubGraphInfo* info = _dump_time_subgraph_info_table->get(relocated_k); KlassSubGraphInfo* info = _dump_time_subgraph_info_table->get(relocated_k);
if (info == NULL) { assert(info != NULL, "must have been initialized");
_dump_time_subgraph_info_table->put(relocated_k, KlassSubGraphInfo(relocated_k));
info = _dump_time_subgraph_info_table->get(relocated_k);
++ _dump_time_subgraph_info_table->_count;
}
return info; return info;
} }
@ -384,6 +449,7 @@ void ArchivedKlassSubGraphInfoRecord::init(KlassSubGraphInfo* info) {
_k = info->klass(); _k = info->klass();
_entry_field_records = NULL; _entry_field_records = NULL;
_subgraph_object_klasses = NULL; _subgraph_object_klasses = NULL;
_is_full_module_graph = info->is_full_module_graph();
// populate the entry fields // populate the entry fields
GrowableArray<juint>* entry_fields = info->subgraph_entry_fields(); GrowableArray<juint>* entry_fields = info->subgraph_entry_fields();
@ -464,7 +530,7 @@ void HeapShared::serialize_subgraph_info_table_header(SerializeClosure* soc) {
_run_time_subgraph_info_table.serialize_header(soc); _run_time_subgraph_info_table.serialize_header(soc);
} }
void HeapShared::initialize_from_archived_subgraph(Klass* k) { void HeapShared::initialize_from_archived_subgraph(Klass* k, TRAPS) {
if (!open_archive_heap_region_mapped()) { if (!open_archive_heap_region_mapped()) {
return; // nothing to do return; // nothing to do
} }
@ -476,7 +542,9 @@ void HeapShared::initialize_from_archived_subgraph(Klass* k) {
// Initialize from archived data. Currently this is done only // Initialize from archived data. Currently this is done only
// during VM initialization time. No lock is needed. // during VM initialization time. No lock is needed.
if (record != NULL) { if (record != NULL) {
Thread* THREAD = Thread::current(); if (record->is_full_module_graph() && !MetaspaceShared::use_full_module_graph()) {
return;
}
int i; int i;
// Load/link/initialize the klasses of the objects in the subgraph. // Load/link/initialize the klasses of the objects in the subgraph.
@ -628,6 +696,25 @@ void HeapShared::check_closed_archive_heap_region_object(InstanceKlass* k,
} }
} }
void HeapShared::check_module_oop(oop orig_module_obj) {
assert(DumpSharedSpaces, "must be");
assert(java_lang_Module::is_instance(orig_module_obj), "must be");
ModuleEntry* orig_module_ent = java_lang_Module::module_entry_raw(orig_module_obj);
if (orig_module_ent == NULL) {
// These special Module objects are created in Java code. They are not
// defined via Modules::define_module(), so they don't have a ModuleEntry:
// java.lang.Module::ALL_UNNAMED_MODULE
// java.lang.Module::EVERYONE_MODULE
// jdk.internal.loader.ClassLoaders$BootClassLoader::unnamedModule
assert(java_lang_Module::name(orig_module_obj) == NULL, "must be unnamed");
log_info(cds, heap)("Module oop with No ModuleEntry* @[" PTR_FORMAT "]", p2i(orig_module_obj));
} else {
ClassLoaderData* loader_data = orig_module_ent->loader_data();
assert(loader_data->is_builtin_class_loader_data(), "must be");
}
}
// (1) If orig_obj has not been archived yet, archive it. // (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), // (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. // trace all objects that are reachable from it, and make sure these objects are archived.
@ -695,6 +782,18 @@ oop HeapShared::archive_reachable_objects_from(int level,
vm_exit(1); 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"); assert(archived_obj != NULL, "must be");
@ -881,8 +980,9 @@ void HeapShared::set_has_been_seen_during_subgraph_recording(oop obj) {
++ _num_new_walked_objs; ++ _num_new_walked_objs;
} }
void HeapShared::start_recording_subgraph(InstanceKlass *k, const char* class_name) { void HeapShared::start_recording_subgraph(InstanceKlass *k, const char* class_name, bool is_full_module_graph) {
log_info(cds, heap)("Start recording subgraph(s) for archived fields in %s", class_name); log_info(cds, heap)("Start recording subgraph(s) for archived fields in %s", class_name);
init_subgraph_info(k, is_full_module_graph);
init_seen_objects_table(); init_seen_objects_table();
_num_new_walked_objs = 0; _num_new_walked_objs = 0;
_num_new_archived_objs = 0; _num_new_archived_objs = 0;
@ -959,6 +1059,11 @@ void HeapShared::init_subgraph_entry_fields(Thread* THREAD) {
init_subgraph_entry_fields(open_archive_subgraph_entry_fields, init_subgraph_entry_fields(open_archive_subgraph_entry_fields,
num_open_archive_subgraph_entry_fields, num_open_archive_subgraph_entry_fields,
THREAD); THREAD);
if (MetaspaceShared::use_full_module_graph()) {
init_subgraph_entry_fields(fmg_open_archive_subgraph_entry_fields,
num_fmg_open_archive_subgraph_entry_fields,
THREAD);
}
} }
void HeapShared::init_for_dumping(Thread* THREAD) { void HeapShared::init_for_dumping(Thread* THREAD) {
@ -968,6 +1073,7 @@ void HeapShared::init_for_dumping(Thread* THREAD) {
void HeapShared::archive_object_subgraphs(ArchivableStaticFieldInfo fields[], void HeapShared::archive_object_subgraphs(ArchivableStaticFieldInfo fields[],
int num, bool is_closed_archive, int num, bool is_closed_archive,
bool is_full_module_graph,
Thread* THREAD) { Thread* THREAD) {
_num_total_subgraph_recordings = 0; _num_total_subgraph_recordings = 0;
_num_total_walked_objs = 0; _num_total_walked_objs = 0;
@ -985,7 +1091,7 @@ void HeapShared::archive_object_subgraphs(ArchivableStaticFieldInfo fields[],
for (i = 0; i < num; ) { for (i = 0; i < num; ) {
ArchivableStaticFieldInfo* info = &fields[i]; ArchivableStaticFieldInfo* info = &fields[i];
const char* klass_name = info->klass_name; const char* klass_name = info->klass_name;
start_recording_subgraph(info->klass, klass_name); start_recording_subgraph(info->klass, klass_name, is_full_module_graph);
// If you have specified consecutive fields of the same klass in // If you have specified consecutive fields of the same klass in
// fields[], these will be archived in the same // fields[], these will be archived in the same
@ -996,6 +1102,7 @@ void HeapShared::archive_object_subgraphs(ArchivableStaticFieldInfo fields[],
if (f->klass_name != klass_name) { if (f->klass_name != klass_name) {
break; break;
} }
archive_reachable_objects_from_static_field(f->klass, f->klass_name, archive_reachable_objects_from_static_field(f->klass, f->klass_name,
f->offset, f->field_name, f->offset, f->field_name,
is_closed_archive, CHECK); is_closed_archive, CHECK);

View File

@ -66,10 +66,12 @@ class KlassSubGraphInfo: public CHeapObj<mtClass> {
// is_closed_archive flag. // is_closed_archive flag.
GrowableArray<juint>* _subgraph_entry_fields; GrowableArray<juint>* _subgraph_entry_fields;
bool _is_full_module_graph;
public: public:
KlassSubGraphInfo(Klass* k) : KlassSubGraphInfo(Klass* k, bool is_full_module_graph) :
_k(k), _subgraph_object_klasses(NULL), _k(k), _subgraph_object_klasses(NULL),
_subgraph_entry_fields(NULL) {} _subgraph_entry_fields(NULL),
_is_full_module_graph(is_full_module_graph) {}
~KlassSubGraphInfo() { ~KlassSubGraphInfo() {
if (_subgraph_object_klasses != NULL) { if (_subgraph_object_klasses != NULL) {
delete _subgraph_object_klasses; delete _subgraph_object_klasses;
@ -93,6 +95,7 @@ class KlassSubGraphInfo: public CHeapObj<mtClass> {
return _subgraph_object_klasses == NULL ? 0 : return _subgraph_object_klasses == NULL ? 0 :
_subgraph_object_klasses->length(); _subgraph_object_klasses->length();
} }
bool is_full_module_graph() const { return _is_full_module_graph; }
}; };
// An archived record of object sub-graphs reachable from static // An archived record of object sub-graphs reachable from static
@ -101,6 +104,7 @@ class KlassSubGraphInfo: public CHeapObj<mtClass> {
class ArchivedKlassSubGraphInfoRecord { class ArchivedKlassSubGraphInfoRecord {
private: private:
Klass* _k; Klass* _k;
bool _is_full_module_graph;
// contains pairs of field offset and value for each subgraph entry field // contains pairs of field offset and value for each subgraph entry field
Array<juint>* _entry_field_records; Array<juint>* _entry_field_records;
@ -115,6 +119,7 @@ class ArchivedKlassSubGraphInfoRecord {
Klass* klass() const { return _k; } Klass* klass() const { return _k; }
Array<juint>* entry_field_records() const { return _entry_field_records; } Array<juint>* entry_field_records() const { return _entry_field_records; }
Array<Klass*>* subgraph_object_klasses() const { return _subgraph_object_klasses; } Array<Klass*>* subgraph_object_klasses() const { return _subgraph_object_klasses; }
bool is_full_module_graph() const { return _is_full_module_graph; }
}; };
#endif // INCLUDE_CDS_JAVA_HEAP #endif // INCLUDE_CDS_JAVA_HEAP
@ -186,6 +191,7 @@ private:
static void archive_object_subgraphs(ArchivableStaticFieldInfo fields[], static void archive_object_subgraphs(ArchivableStaticFieldInfo fields[],
int num, int num,
bool is_closed_archive, bool is_closed_archive,
bool is_full_module_graph,
Thread* THREAD); Thread* THREAD);
// Archive object sub-graph starting from the given static field // Archive object sub-graph starting from the given static field
@ -200,6 +206,7 @@ private:
static void verify_reachable_objects_from(oop obj, bool is_archived) PRODUCT_RETURN; static void verify_reachable_objects_from(oop obj, bool is_archived) PRODUCT_RETURN;
static void verify_subgraph_from(oop orig_obj) PRODUCT_RETURN; static void verify_subgraph_from(oop orig_obj) PRODUCT_RETURN;
static KlassSubGraphInfo* init_subgraph_info(Klass *k, bool is_full_module_graph);
static KlassSubGraphInfo* get_subgraph_info(Klass *k); static KlassSubGraphInfo* get_subgraph_info(Klass *k);
static void init_subgraph_entry_fields(ArchivableStaticFieldInfo fields[], static void init_subgraph_entry_fields(ArchivableStaticFieldInfo fields[],
@ -239,13 +246,17 @@ private:
static int _num_total_recorded_klasses; static int _num_total_recorded_klasses;
static int _num_total_verifications; static int _num_total_verifications;
static void start_recording_subgraph(InstanceKlass *k, const char* klass_name); static void start_recording_subgraph(InstanceKlass *k, const char* klass_name,
bool is_full_module_graph);
static void done_recording_subgraph(InstanceKlass *k, const char* klass_name); static void done_recording_subgraph(InstanceKlass *k, const char* klass_name);
static bool has_been_seen_during_subgraph_recording(oop obj); static bool has_been_seen_during_subgraph_recording(oop obj);
static void set_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: public:
static void reset_archived_object_states(TRAPS);
static void create_archived_object_cache() { static void create_archived_object_cache() {
_archived_object_cache = _archived_object_cache =
new (ResourceObj::C_HEAP, mtClass)ArchivedObjectCache(); new (ResourceObj::C_HEAP, mtClass)ArchivedObjectCache();
@ -321,7 +332,7 @@ private:
inline static bool is_archived_object(oop p) NOT_CDS_JAVA_HEAP_RETURN_(false); inline static bool is_archived_object(oop p) NOT_CDS_JAVA_HEAP_RETURN_(false);
static void initialize_from_archived_subgraph(Klass* k) NOT_CDS_JAVA_HEAP_RETURN; static void initialize_from_archived_subgraph(Klass* k, TRAPS) NOT_CDS_JAVA_HEAP_RETURN;
// NarrowOops stored in the CDS archive may use a different encoding scheme // NarrowOops stored in the CDS archive may use a different encoding scheme
// than CompressedOops::{base,shift} -- see FileMapInfo::map_heap_regions_impl. // than CompressedOops::{base,shift} -- see FileMapInfo::map_heap_regions_impl.

View File

@ -24,6 +24,7 @@
#include "precompiled.hpp" #include "precompiled.hpp"
#include "classfile/classLoaderDataGraph.hpp" #include "classfile/classLoaderDataGraph.hpp"
#include "classfile/classLoaderDataShared.hpp"
#include "classfile/classListParser.hpp" #include "classfile/classListParser.hpp"
#include "classfile/classLoaderExt.hpp" #include "classfile/classLoaderExt.hpp"
#include "classfile/loaderConstraints.hpp" #include "classfile/loaderConstraints.hpp"
@ -40,6 +41,7 @@
#include "logging/logMessage.hpp" #include "logging/logMessage.hpp"
#include "memory/archiveBuilder.hpp" #include "memory/archiveBuilder.hpp"
#include "memory/archiveUtils.inline.hpp" #include "memory/archiveUtils.inline.hpp"
#include "memory/dumpAllocStats.hpp"
#include "memory/dynamicArchive.hpp" #include "memory/dynamicArchive.hpp"
#include "memory/filemap.hpp" #include "memory/filemap.hpp"
#include "memory/heapShared.inline.hpp" #include "memory/heapShared.inline.hpp"
@ -87,6 +89,7 @@ void* MetaspaceShared::_shared_metaspace_static_top = NULL;
intx MetaspaceShared::_relocation_delta; intx MetaspaceShared::_relocation_delta;
char* MetaspaceShared::_requested_base_address; char* MetaspaceShared::_requested_base_address;
bool MetaspaceShared::_use_optimized_module_handling = true; bool MetaspaceShared::_use_optimized_module_handling = true;
bool MetaspaceShared::_use_full_module_graph = true;
// The CDS archive is divided into the following regions: // The CDS archive is divided into the following regions:
// mc - misc code (the method entry trampolines, c++ vtables) // mc - misc code (the method entry trampolines, c++ vtables)
@ -151,6 +154,10 @@ char* MetaspaceShared::read_only_space_alloc(size_t num_bytes) {
return _ro_region.allocate(num_bytes); 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(); } size_t MetaspaceShared::reserved_space_alignment() { return os::vm_allocation_granularity(); }
static bool shared_base_valid(char* shared_base) { static bool shared_base_valid(char* shared_base) {
@ -500,6 +507,8 @@ void MetaspaceShared::serialize(SerializeClosure* soc) {
serialize_cloned_cpp_vtptrs(soc); serialize_cloned_cpp_vtptrs(soc);
soc->do_tag(--tag); soc->do_tag(--tag);
CDS_JAVA_HEAP_ONLY(ClassLoaderDataShared::serialize(soc));
soc->do_tag(666); soc->do_tag(666);
} }
@ -1070,10 +1079,29 @@ void VM_PopulateDumpSharedSpace::doit() {
char* cloned_vtables = _mc_region.top(); char* cloned_vtables = _mc_region.top();
MetaspaceShared::allocate_cpp_vtable_clones(); MetaspaceShared::allocate_cpp_vtable_clones();
{
_mc_region.pack(&_rw_region); _mc_region.pack(&_rw_region);
builder.dump_rw_region(); builder.dump_rw_region();
#if INCLUDE_CDS_JAVA_HEAP
if (MetaspaceShared::use_full_module_graph()) {
// Archive the ModuleEntry's and PackageEntry's of the 3 built-in loaders
char* start = _rw_region.top();
ClassLoaderDataShared::allocate_archived_tables();
ArchiveBuilder::alloc_stats()->record_modules(_rw_region.top() - start, /*read_only*/false);
}
#endif
}
{
_rw_region.pack(&_ro_region); _rw_region.pack(&_ro_region);
builder.dump_ro_region(); builder.dump_ro_region();
#if INCLUDE_CDS_JAVA_HEAP
if (MetaspaceShared::use_full_module_graph()) {
char* start = _ro_region.top();
ClassLoaderDataShared::init_archived_tables();
ArchiveBuilder::alloc_stats()->record_modules(_ro_region.top() - start, /*read_only*/true);
}
#endif
}
builder.relocate_pointers(); builder.relocate_pointers();
dump_shared_symbol_table(builder.symbols()); dump_shared_symbol_table(builder.symbols());
@ -1366,6 +1394,12 @@ void MetaspaceShared::preload_and_dump(TRAPS) {
link_and_cleanup_shared_classes(CATCH); link_and_cleanup_shared_classes(CATCH);
log_info(cds)("Rewriting and linking classes: done"); 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; VM_PopulateDumpSharedSpace op;
MutexLocker ml(THREAD, HeapShared::is_heap_object_archiving_allowed() ? MutexLocker ml(THREAD, HeapShared::is_heap_object_archiving_allowed() ?
Heap_lock : NULL); // needed by HeapShared::run_gc() Heap_lock : NULL); // needed by HeapShared::run_gc()
@ -1749,7 +1783,8 @@ MapArchiveResult MetaspaceShared::map_archives(FileMapInfo* static_mapinfo, File
static_mapinfo->map_heap_regions(); 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 { } else {
unmap_archive(static_mapinfo); unmap_archive(static_mapinfo);
unmap_archive(dynamic_mapinfo); unmap_archive(dynamic_mapinfo);
@ -2074,6 +2109,17 @@ intx MetaspaceShared::final_delta() {
- intx(SharedBaseAddress); // .. but the base archive is mapped at here at dump time - intx(SharedBaseAddress); // .. but the base archive is mapped at here at dump time
} }
bool MetaspaceShared::use_full_module_graph() {
bool result = _use_optimized_module_handling && _use_full_module_graph &&
(UseSharedSpaces || DumpSharedSpaces) && HeapShared::is_heap_object_archiving_allowed();
if (result && UseSharedSpaces) {
// Classes used by the archived full module graph are loaded in JVMTI early phase.
assert(!(JvmtiExport::should_post_class_file_load_hook() && JvmtiExport::has_early_class_hook_env()),
"CDS should be disabled if early class hooks are enabled");
}
return result;
}
void MetaspaceShared::print_on(outputStream* st) { void MetaspaceShared::print_on(outputStream* st) {
if (UseSharedSpaces || DumpSharedSpaces) { if (UseSharedSpaces || DumpSharedSpaces) {
st->print("CDS archive(s) mapped at: "); st->print("CDS archive(s) mapped at: ");

View File

@ -78,6 +78,7 @@ class MetaspaceShared : AllStatic {
static intx _relocation_delta; static intx _relocation_delta;
static char* _requested_base_address; static char* _requested_base_address;
static bool _use_optimized_module_handling; static bool _use_optimized_module_handling;
static bool _use_full_module_graph;
public: public:
enum { enum {
// core archive spaces // core archive spaces
@ -215,17 +216,22 @@ class MetaspaceShared : AllStatic {
// Allocate a block of memory from the "mc" or "ro" regions. // Allocate a block of memory from the "mc" or "ro" regions.
static char* misc_code_space_alloc(size_t num_bytes); static char* misc_code_space_alloc(size_t num_bytes);
static char* read_only_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 <typename T> template <typename T>
static Array<T>* new_ro_array(int length) { static Array<T>* new_ro_array(int length) {
#if INCLUDE_CDS
size_t byte_size = Array<T>::byte_sizeof(length, sizeof(T)); size_t byte_size = Array<T>::byte_sizeof(length, sizeof(T));
Array<T>* array = (Array<T>*)read_only_space_alloc(byte_size); Array<T>* array = (Array<T>*)read_only_space_alloc(byte_size);
array->initialize(length); array->initialize(length);
return array; return array;
#else }
return NULL;
#endif template <typename T>
static Array<T>* new_rw_array(int length) {
size_t byte_size = Array<T>::byte_sizeof(length, sizeof(T));
Array<T>* array = (Array<T>*)read_write_space_alloc(byte_size);
array->initialize(length);
return array;
} }
template <typename T> template <typename T>
@ -270,9 +276,13 @@ class MetaspaceShared : AllStatic {
GrowableArray<ArchiveHeapOopmapInfo>* open_oopmaps); GrowableArray<ArchiveHeapOopmapInfo>* open_oopmaps);
// Can we skip some expensive operations related to modules? // 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; } 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: private:
#if INCLUDE_CDS #if INCLUDE_CDS
static void write_region(FileMapInfo* mapinfo, int region_idx, DumpRegion* dump_region, static void write_region(FileMapInfo* mapinfo, int region_idx, DumpRegion* dump_region,

View File

@ -47,6 +47,7 @@
#include "logging/logMessage.hpp" #include "logging/logMessage.hpp"
#include "logging/logStream.hpp" #include "logging/logStream.hpp"
#include "memory/allocation.inline.hpp" #include "memory/allocation.inline.hpp"
#include "memory/archiveUtils.hpp"
#include "memory/iterator.inline.hpp" #include "memory/iterator.inline.hpp"
#include "memory/metadataFactory.hpp" #include "memory/metadataFactory.hpp"
#include "memory/metaspaceClosure.hpp" #include "memory/metaspaceClosure.hpp"

View File

@ -1329,6 +1329,7 @@ public:
virtual void remove_unshareable_info(); virtual void remove_unshareable_info();
virtual void remove_java_mirror(); virtual void remove_java_mirror();
void restore_unshareable_info(ClassLoaderData* loader_data, Handle protection_domain, PackageEntry* pkg_entry, TRAPS); void restore_unshareable_info(ClassLoaderData* loader_data, Handle protection_domain, PackageEntry* pkg_entry, TRAPS);
void init_shared_package_entry();
// jvm support // jvm support
jint compute_modifier_flags(TRAPS) const; jint compute_modifier_flags(TRAPS) const;

View File

@ -1227,6 +1227,11 @@ JVM_ENTRY (void, JVM_AddReadsModule(JNIEnv *env, jobject from_module, jobject so
Modules::add_reads_module(from_module, source_module, CHECK); Modules::add_reads_module(from_module, source_module, CHECK);
JVM_END 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 ////////////////////////////////////////////////////////////////////////////// // Reflection support //////////////////////////////////////////////////////////////////////////////
JVM_ENTRY(jstring, JVM_InitClassName(JNIEnv *env, jclass cls)) JVM_ENTRY(jstring, JVM_InitClassName(JNIEnv *env, jclass cls))
@ -3722,7 +3727,7 @@ JVM_ENTRY(void, JVM_InitializeFromArchive(JNIEnv* env, jclass cls))
JVMWrapper("JVM_InitializeFromArchive"); JVMWrapper("JVM_InitializeFromArchive");
Klass* k = java_lang_Class::as_Klass(JNIHandles::resolve(cls)); Klass* k = java_lang_Class::as_Klass(JNIHandles::resolve(cls));
assert(k->is_klass(), "just checking"); assert(k->is_klass(), "just checking");
HeapShared::initialize_from_archived_subgraph(k); HeapShared::initialize_from_archived_subgraph(k, THREAD);
JVM_END JVM_END
JVM_ENTRY(void, JVM_RegisterLambdaProxyClassForArchiving(JNIEnv* env, JVM_ENTRY(void, JVM_RegisterLambdaProxyClassForArchiving(JNIEnv* env,

View File

@ -1459,7 +1459,14 @@ bool Arguments::add_property(const char* prop, PropertyWriteable writeable, Prop
if (is_internal_module_property(key) || if (is_internal_module_property(key) ||
strcmp(key, "jdk.module.main") == 0) { strcmp(key, "jdk.module.main") == 0) {
MetaspaceShared::disable_optimized_module_handling(); 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 #endif
@ -2508,7 +2515,7 @@ jint Arguments::parse_each_vm_init_arg(const JavaVMInitArgs* args, bool* patch_m
Arguments::append_sysclasspath(tail); Arguments::append_sysclasspath(tail);
#if INCLUDE_CDS #if INCLUDE_CDS
MetaspaceShared::disable_optimized_module_handling(); 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 because bootclasspath was appended");
#endif #endif
// -bootclasspath/p: // -bootclasspath/p:
} else if (match_option(option, "-Xbootclasspath/p:", &tail)) { } else if (match_option(option, "-Xbootclasspath/p:", &tail)) {

View File

@ -2714,6 +2714,17 @@ public abstract class ClassLoader {
offset = unsafe.objectFieldOffset(k, name); offset = unsafe.objectFieldOffset(k, name);
return unsafe.compareAndSetReference(this, offset, null, obj); 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;
}
} }
/* /*

View File

@ -55,6 +55,7 @@ import java.util.stream.Stream;
import jdk.internal.loader.BuiltinClassLoader; import jdk.internal.loader.BuiltinClassLoader;
import jdk.internal.loader.BootLoader; import jdk.internal.loader.BootLoader;
import jdk.internal.loader.ClassLoaders; import jdk.internal.loader.ClassLoaders;
import jdk.internal.misc.VM;
import jdk.internal.module.IllegalAccessLogger; import jdk.internal.module.IllegalAccessLogger;
import jdk.internal.module.ModuleLoaderMap; import jdk.internal.module.ModuleLoaderMap;
import jdk.internal.module.ServicesCatalog; import jdk.internal.module.ServicesCatalog;
@ -246,12 +247,55 @@ public final class Module implements AnnotatedElement {
// -- // --
// special Module to mean "all unnamed modules" // special Module to mean "all unnamed modules"
private static final Module ALL_UNNAMED_MODULE = new Module(null); private static final Module ALL_UNNAMED_MODULE;
private static final Set<Module> ALL_UNNAMED_MODULE_SET = Set.of(ALL_UNNAMED_MODULE); private static final Set<Module> ALL_UNNAMED_MODULE_SET;
// special Module to mean "everyone" // special Module to mean "everyone"
private static final Module EVERYONE_MODULE = new Module(null); private static final Module EVERYONE_MODULE;
private static final Set<Module> EVERYONE_SET = Set.of(EVERYONE_MODULE); private static final Set<Module> EVERYONE_SET;
private static class ArchivedData {
private static ArchivedData archivedData;
private final Module allUnnamedModule;
private final Set<Module> allUnnamedModules;
private final Module everyoneModule;
private final Set<Module> 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 * The holder of data structures to support readability, exports, and

View File

@ -2248,6 +2248,9 @@ public final class System {
public ServicesCatalog getServicesCatalog(ModuleLayer layer) { public ServicesCatalog getServicesCatalog(ModuleLayer layer) {
return layer.getServicesCatalog(); return layer.getServicesCatalog();
} }
public void bindToLoader(ModuleLayer layer, ClassLoader loader) {
layer.bindToLoader(loader);
}
public Stream<ModuleLayer> layers(ModuleLayer layer) { public Stream<ModuleLayer> layers(ModuleLayer layer) {
return layer.layers(); return layer.layers();
} }

View File

@ -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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -269,4 +269,11 @@ public class SecureClassLoader extends ClassLoader {
return cs.matchCerts(csk.cs, true); return cs.matchCerts(csk.cs, true);
} }
} }
/**
* Called by the VM, during -Xshare:dump
*/
private void resetArchivedStates() {
pdcache.clear();
}
} }

View File

@ -256,6 +256,12 @@ public interface JavaLangAccess {
*/ */
ServicesCatalog getServicesCatalog(ModuleLayer layer); 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 * Returns an ordered stream of layers. The first element is the
* given layer, the remaining elements are its parents, in DFS order. * given layer, the remaining elements are its parents, in DFS order.

View File

@ -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<String, ?> 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<String, ?> packageToModule() {
return packageToModule;
}
static void archive() {
archivedClassLoaders = new ArchivedClassLoaders();
}
static ArchivedClassLoaders get() {
return archivedClassLoaders;
}
static {
VM.initializeFromArchive(ArchivedClassLoaders.class);
}
}

View File

@ -65,7 +65,15 @@ public class BootLoader {
} }
// ServiceCatalog for the boot class loader // 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 // ClassLoaderValue map for the boot class loader
private static final ConcurrentHashMap<?, ?> CLASS_LOADER_VALUE_MAP private static final ConcurrentHashMap<?, ?> CLASS_LOADER_VALUE_MAP

View File

@ -104,8 +104,7 @@ public class BuiltinClassLoader
private final BuiltinClassLoader parent; private final BuiltinClassLoader parent;
// the URL class path, or null if there is no class path // 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. * A module defined/loaded by a built-in class loader.
@ -156,10 +155,26 @@ public class BuiltinClassLoader
} }
} }
// maps package name to loaded module for modules in the boot layer // maps package name to loaded module for modules in the boot layer
private static final Map<String, LoadedModule> packageToModule private static final Map<String, LoadedModule> packageToModule;
= new ConcurrentHashMap<>(1024); static {
ArchivedClassLoaders archivedClassLoaders = ArchivedClassLoaders.get();
if (archivedClassLoaders != null) {
@SuppressWarnings("unchecked")
Map<String, LoadedModule> map
= (Map<String, LoadedModule>) archivedClassLoaders.packageToModule();
packageToModule = map;
} else {
packageToModule = new ConcurrentHashMap<>(1024);
}
}
/**
* Invoked by ArchivedClassLoaders to archive the package-to-module map.
*/
static Map<String, ?> packageToModule() {
return packageToModule;
}
// maps a module name to a module reference // maps a module name to a module reference
private final Map<String, ModuleReference> nameToModule; private final Map<String, ModuleReference> nameToModule;
@ -185,6 +200,21 @@ public class BuiltinClassLoader
this.moduleToReader = new ConcurrentHashMap<>(); this.moduleToReader = new ConcurrentHashMap<>();
} }
/**
* 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 * Returns {@code true} if there is a class path associated with this
* class loader. * class loader.
@ -1042,4 +1072,9 @@ public class BuiltinClassLoader
private static URL checkURL(URL url) { private static URL checkURL(URL url) {
return URLClassPath.checkURL(url); return URLClassPath.checkURL(url);
} }
// Called from VM only, during -Xshare:dump
private void resetArchivedStates() {
ucp = null;
}
} }

View File

@ -36,6 +36,7 @@ import java.util.jar.Manifest;
import jdk.internal.access.JavaLangAccess; import jdk.internal.access.JavaLangAccess;
import jdk.internal.access.SharedSecrets; import jdk.internal.access.SharedSecrets;
import jdk.internal.misc.VM; import jdk.internal.misc.VM;
import jdk.internal.module.ServicesCatalog;
/** /**
* Creates and provides access to the built-in platform and application class * Creates and provides access to the built-in platform and application class
@ -56,14 +57,22 @@ public class ClassLoaders {
// Creates the built-in class loaders. // Creates the built-in class loaders.
static { static {
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 // -Xbootclasspath/a or -javaagent with Boot-Class-Path attribute
String append = VM.getSavedProperty("jdk.boot.class.path.append"); String append = VM.getSavedProperty("jdk.boot.class.path.append");
BOOT_LOADER = URLClassPath ucp = (append != null && !append.isEmpty())
new BootClassLoader((append != null && !append.isEmpty())
? new URLClassPath(append, true) ? new URLClassPath(append, true)
: null); : null;
BOOT_LOADER = new BootClassLoader(ucp);
PLATFORM_LOADER = new PlatformClassLoader(BOOT_LOADER); PLATFORM_LOADER = new PlatformClassLoader(BOOT_LOADER);
}
// A class path is required when no initial module is specified. // A class path is required when no initial module is specified.
// In this case the class path defaults to "", meaning the current // In this case the class path defaults to "", meaning the current
// working directory. When an initial module is specified, on the // working directory. When an initial module is specified, on the
@ -75,7 +84,15 @@ public class ClassLoaders {
cp = (initialModuleName == null) ? "" : null; cp = (initialModuleName == null) ? "" : null;
} }
URLClassPath ucp = new URLClassPath(cp, false); URLClassPath ucp = new URLClassPath(cp, false);
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); APP_LOADER = new AppClassLoader(PLATFORM_LOADER, ucp);
ArchivedClassLoaders.archive();
}
} }
/** /**
@ -144,11 +161,8 @@ public class ClassLoaders {
throw new InternalError(); throw new InternalError();
} }
final URLClassPath ucp; AppClassLoader(BuiltinClassLoader parent, URLClassPath ucp) {
AppClassLoader(PlatformClassLoader parent, URLClassPath ucp) {
super("app", parent, ucp); super("app", parent, ucp);
this.ucp = ucp;
} }
@Override @Override
@ -181,7 +195,7 @@ public class ClassLoaders {
* @see java.lang.instrument.Instrumentation#appendToSystemClassLoaderSearch * @see java.lang.instrument.Instrumentation#appendToSystemClassLoaderSearch
*/ */
void appendToClassPathForInstrumentation(String path) { void appendToClassPathForInstrumentation(String path) {
ucp.addFile(path); appendClassPath(path);
} }
/** /**
@ -190,6 +204,13 @@ public class ClassLoaders {
protected Package defineOrCheckPackage(String pn, Manifest man, URL url) { protected Package defineOrCheckPackage(String pn, Manifest man, URL url) {
return super.defineOrCheckPackage(pn, man, url); return super.defineOrCheckPackage(pn, man, url);
} }
/**
* Called by the VM, during -Xshare:dump
*/
private void resetArchivedStates() {
setClassPath(null);
}
} }
/** /**

View File

@ -468,6 +468,8 @@ public class VM {
*/ */
public static native void initializeFromArchive(Class<?> c); public static native void initializeFromArchive(Class<?> c);
public static native void defineArchivedModules(ClassLoader platformLoader, ClassLoader systemLoader);
public static native long getRandomSeedForCDSDump(); public static native long getRandomSeedForCDSDump();
/** /**

View File

@ -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);
}
}

View File

@ -22,21 +22,20 @@
* or visit www.oracle.com if you need additional information or have any * or visit www.oracle.com if you need additional information or have any
* questions. * questions.
*/ */
package jdk.internal.module; package jdk.internal.module;
import java.lang.module.Configuration;
import java.lang.module.ModuleFinder;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.function.Function; import java.util.function.Function;
import java.lang.module.Configuration;
import java.lang.module.ModuleFinder;
import jdk.internal.misc.VM; 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 static ArchivedModuleGraph archivedModuleGraph;
private final boolean hasSplitPackages; private final boolean hasSplitPackages;
@ -47,7 +46,7 @@ final class ArchivedModuleGraph {
private final Map<String, Set<String>> concealedPackagesToOpen; private final Map<String, Set<String>> concealedPackagesToOpen;
private final Map<String, Set<String>> exportedPackagesToOpen; private final Map<String, Set<String>> exportedPackagesToOpen;
public ArchivedModuleGraph(boolean hasSplitPackages, private ArchivedModuleGraph(boolean hasSplitPackages,
boolean hasIncubatorModules, boolean hasIncubatorModules,
ModuleFinder finder, ModuleFinder finder,
Configuration configuration, Configuration configuration,
@ -107,8 +106,20 @@ final class ArchivedModuleGraph {
/** /**
* Archive the module graph for the given initial module. * Archive the module graph for the given initial module.
*/ */
static void archive(ArchivedModuleGraph graph) { static void archive(boolean hasSplitPackages,
archivedModuleGraph = graph; boolean hasIncubatorModules,
ModuleFinder finder,
Configuration configuration,
Function<String, ClassLoader> classLoaderFunction,
Map<String, Set<String>> concealedPackagesToOpen,
Map<String, Set<String>> exportedPackagesToOpen) {
archivedModuleGraph = new ArchivedModuleGraph(hasSplitPackages,
hasIncubatorModules,
finder,
configuration,
classLoaderFunction,
concealedPackagesToOpen,
exportedPackagesToOpen);
} }
static { static {

View File

@ -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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -57,7 +57,7 @@ public final class IllegalAccessLogger {
/** /**
* Logger modes * Logger modes
*/ */
public static enum Mode { public enum Mode {
/** /**
* Prints a warning when an illegal access succeeds and then * Prints a warning when an illegal access succeeds and then
* discards the logger so that there is no further output. * discards the logger so that there is no further output.
@ -118,7 +118,7 @@ public final class IllegalAccessLogger {
} }
/** /**
* 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() { public void complete() {
Map<Module, Set<String>> map1 = unmodifiableMap(moduleToConcealedPackages); Map<Module, Set<String>> map1 = unmodifiableMap(moduleToConcealedPackages);

View File

@ -42,18 +42,19 @@ import java.util.Iterator;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects; import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import jdk.internal.loader.BootLoader;
import jdk.internal.loader.BuiltinClassLoader;
import jdk.internal.access.JavaLangAccess; import jdk.internal.access.JavaLangAccess;
import jdk.internal.access.JavaLangModuleAccess; import jdk.internal.access.JavaLangModuleAccess;
import jdk.internal.access.SharedSecrets; 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; import jdk.internal.perf.PerfCounter;
/** /**
@ -86,8 +87,8 @@ public final class ModuleBootstrap {
private static final String ALL_MODULE_PATH = "ALL-MODULE-PATH"; private static final String ALL_MODULE_PATH = "ALL-MODULE-PATH";
// access to java.lang/module // access to java.lang/module
private static final JavaLangModuleAccess JLMA private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess();
= SharedSecrets.getJavaLangModuleAccess(); private static final JavaLangModuleAccess JLMA = SharedSecrets.getJavaLangModuleAccess();
// The ModulePatcher for the initial configuration // The ModulePatcher for the initial configuration
private static final ModulePatcher patcher = initModulePatcher(); private static final ModulePatcher patcher = initModulePatcher();
@ -134,14 +135,58 @@ public final class ModuleBootstrap {
} }
/** /**
* 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) * @see java.lang.System#initPhase2(boolean, boolean)
*/ */
public static ModuleLayer boot() throws Exception { public static ModuleLayer boot() {
Counters.start(); Counters.start();
ModuleLayer bootLayer;
ArchivedBootLayer archivedBootLayer = ArchivedBootLayer.get();
if (archivedBootLayer != null) {
assert canUseArchivedBootLayer();
bootLayer = archivedBootLayer.bootLayer();
BootLoader.getUnnamedModule(); // trigger <clinit> 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 // Step 0: Command line options
ModuleFinder upgradeModulePath = finderFor("jdk.module.upgrade.path"); ModuleFinder upgradeModulePath = finderFor("jdk.module.upgrade.path");
@ -428,6 +473,7 @@ public final class ModuleBootstrap {
concealedPackagesToOpen = systemModules.concealedPackagesToOpen(); concealedPackagesToOpen = systemModules.concealedPackagesToOpen();
exportedPackagesToOpen = systemModules.exportedPackagesToOpen(); exportedPackagesToOpen = systemModules.exportedPackagesToOpen();
} }
IllegalAccessLogger.Builder builder =
addIllegalAccess(upgradeModulePath, addIllegalAccess(upgradeModulePath,
concealedPackagesToOpen, concealedPackagesToOpen,
exportedPackagesToOpen, exportedPackagesToOpen,
@ -442,21 +488,21 @@ public final class ModuleBootstrap {
limitedFinder = new SafeModuleFinder(finder); limitedFinder = new SafeModuleFinder(finder);
} }
// Module graph can be archived at CDS dump time. Only allow the // Archive module graph and boot layer can be archived at CDS dump time.
// unnamed module case for now. // Only allow the unnamed module case for now.
if (canArchive && (mainModule == null)) { if (canArchive && (mainModule == null)) {
ArchivedModuleGraph.archive( ArchivedModuleGraph.archive(hasSplitPackages,
new ArchivedModuleGraph(hasSplitPackages,
hasIncubatorModules, hasIncubatorModules,
systemModuleFinder, systemModuleFinder,
cf, cf,
clf, clf,
concealedPackagesToOpen, concealedPackagesToOpen,
exportedPackagesToOpen)); exportedPackagesToOpen);
}
// total time to initialize if (!hasSplitPackages && !hasIncubatorModules) {
Counters.publish("jdk.module.boot.totalTime"); ArchivedBootLayer.archive(bootLayer, builder);
}
}
return bootLayer; return bootLayer;
} }
@ -751,7 +797,8 @@ public final class ModuleBootstrap {
* Process the --illegal-access option (and its default) to open packages * Process the --illegal-access option (and its default) to open packages
* of system modules in the boot layer to code in unnamed modules. * of system modules in the boot layer to code in unnamed modules.
*/ */
private static void addIllegalAccess(ModuleFinder upgradeModulePath, private static IllegalAccessLogger.Builder
addIllegalAccess(ModuleFinder upgradeModulePath,
Map<String, Set<String>> concealedPackagesToOpen, Map<String, Set<String>> concealedPackagesToOpen,
Map<String, Set<String>> exportedPackagesToOpen, Map<String, Set<String>> exportedPackagesToOpen,
ModuleLayer bootLayer, ModuleLayer bootLayer,
@ -761,7 +808,7 @@ public final class ModuleBootstrap {
if (value != null) { if (value != null) {
switch (value) { switch (value) {
case "deny": case "deny":
return; return null;
case "permit": case "permit":
break; break;
case "warn": case "warn":
@ -773,7 +820,7 @@ public final class ModuleBootstrap {
default: default:
fail("Value specified to --illegal-access not recognized:" fail("Value specified to --illegal-access not recognized:"
+ " '" + value + "'"); + " '" + value + "'");
return; return null;
} }
} }
IllegalAccessLogger.Builder builder IllegalAccessLogger.Builder builder
@ -836,11 +883,11 @@ public final class ModuleBootstrap {
builder.logAccessToExportedPackages(m, exportedPackages); builder.logAccessToExportedPackages(m, exportedPackages);
// open the packages to unnamed modules // open the packages to unnamed modules
JavaLangAccess jla = SharedSecrets.getJavaLangAccess(); JLA.addOpensToAllUnnamed(m, concealedPackages, exportedPackages);
jla.addOpensToAllUnnamed(m, concealedPackages, exportedPackages);
} }
builder.complete(); builder.complete();
return builder;
} }
/** /**
@ -905,11 +952,19 @@ public final class ModuleBootstrap {
return decode(prefix, ",", true); 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 * Gets and remove the named system property
*/ */
private static String getAndRemoveProperty(String key) { private static String getAndRemoveProperty(String key) {
return (String)System.getProperties().remove(key); return (String) System.getProperties().remove(key);
} }
/** /**

View File

@ -171,6 +171,16 @@ public final class ServicesCatalog {
return catalog; 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 // the ServicesCatalog registered to a class loader
private static final ClassLoaderValue<ServicesCatalog> CLV = new ClassLoaderValue<>(); private static final ClassLoaderValue<ServicesCatalog> CLV = new ClassLoaderValue<>();
} }

View File

@ -62,6 +62,13 @@ Java_jdk_internal_misc_VM_initializeFromArchive(JNIEnv *env, jclass ignore,
JVM_InitializeFromArchive(env, c); 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 JNIEXPORT jlong JNICALL
Java_jdk_internal_misc_VM_getRandomSeedForCDSDump(JNIEnv *env, jclass ignore) { Java_jdk_internal_misc_VM_getRandomSeedForCDSDump(JNIEnv *env, jclass ignore) {
return JVM_GetRandomSeedForCDSDump(); return JVM_GetRandomSeedForCDSDump();

View File

@ -28,12 +28,15 @@
* @library /test/lib * @library /test/lib
* @modules java.base/jdk.internal.misc * @modules java.base/jdk.internal.misc
* java.management * java.management
* @compile ../modules/CompilerUtils.java
* @run driver CheckForProperDetailStackTrace * @run driver CheckForProperDetailStackTrace
*/ */
import jdk.test.lib.Platform; import jdk.test.lib.Platform;
import jdk.test.lib.process.ProcessTools; import jdk.test.lib.process.ProcessTools;
import jdk.test.lib.process.OutputAnalyzer; 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.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -49,6 +52,11 @@ import java.util.regex.Pattern;
* through this test and update it accordingly. * through this test and update it accordingly.
*/ */
public class CheckForProperDetailStackTrace { 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 .* /* 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. to make sure it matches even if the symbol is not unmangled.
@ -83,11 +91,24 @@ public class CheckForProperDetailStackTrace {
private static String expectedSymbol = "locked_create_entry"; private static String expectedSymbol = "locked_create_entry";
public static void main(String args[]) throws Exception { 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( ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(
"-XX:+UnlockDiagnosticVMOptions", "-XX:+UnlockDiagnosticVMOptions",
"-XX:NativeMemoryTracking=detail", "-XX:NativeMemoryTracking=detail",
"-XX:+PrintNMTStatistics", "-XX:+PrintNMTStatistics",
"-version"); "-p", MODS_DIR.toString(),
"-m", "jdk.test/test.Main");
OutputAnalyzer output = new OutputAnalyzer(pb.start()); OutputAnalyzer output = new OutputAnalyzer(pb.start());
output.shouldHaveExitValue(0); output.shouldHaveExitValue(0);

View File

@ -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 {
}

View File

@ -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");
}
}

View File

@ -0,0 +1,90 @@
/*
* 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 the archived mirrors of the primitive classes have the proper module (java.base)
* @requires vm.cds
* @library /test/lib
* @run driver PrimitiveClassMirrors
*/
import jdk.test.lib.cds.CDSTestUtils;
import jdk.test.lib.cds.CDSOptions;
import jdk.test.lib.process.OutputAnalyzer;
public class PrimitiveClassMirrors {
public static void main(String[] args) throws Exception {
CDSOptions opts = new CDSOptions();
CDSTestUtils.createArchiveAndCheck(opts);
opts.setUseVersion(false);
opts.addSuffix("-Xlog:cds=warning", "PrimitiveClassMirrors$TestApp");
OutputAnalyzer out = CDSTestUtils.runWithArchive(opts);
out.shouldHaveExitValue(0);
// The test should have same results if CDS is turned off
opts.setXShareMode("off");
OutputAnalyzer out2 = CDSTestUtils.runWithArchive(opts);
out2.shouldHaveExitValue(0);
}
static class TestApp {
public static void main(String args[]) throws Exception {
Class classes[] = {
int.class,
float.class,
double.class,
byte.class,
boolean.class,
char.class,
long.class,
short.class,
void.class,
int[].class,
float[].class,
double[].class,
byte[].class,
boolean[].class,
char[].class,
long[].class,
short[].class,
};
for (Class c : classes) {
test(c);
}
}
}
static void test(Class c) throws Exception {
Module m = c.getModule();
boolean unexpected = (m == null || !("java.base".equals(m.getName())));
System.out.println("Module for " + c + " = " + m + (unexpected ? " *** Error" : ""));
if (unexpected) {
throw new RuntimeException("Unexpected: " + m);
}
}
}

View File

@ -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, "<EOF>", arr2[i]);
}
if (i >= arr2.length) {
mismatch(i, arr1[i], "<EOF>");
}
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("============================================================");
print_loader("default", ServiceLoader.load(c));
print_loader("null loader", ServiceLoader.load(c, null));
print_loader("platform loader", ServiceLoader.load(c, ServiceLoaderApp.class.getClassLoader().getParent()));
print_loader("system loader", ServiceLoader.load(c, ServiceLoaderApp.class.getClassLoader()));
}
static void print_loader(String testCase, ServiceLoader loader) throws Exception {
System.out.println("[TEST CASE] " + testCase);
System.out.println("[svcloader] " + asString(loader));
Iterator it = loader.iterator();
ArrayList<String> 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;
}
}

View File

@ -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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -72,6 +72,7 @@ public class SpecifySysLoaderProp {
TestCommon.run( TestCommon.run(
"-verbose:class", "-verbose:class",
"-cp", appJar, "-cp", appJar,
"-Xlog:cds",
"-Djava.system.class.loader=TestClassLoader", "-Djava.system.class.loader=TestClassLoader",
"ReportMyLoader") "ReportMyLoader")
.assertNormalExit("ReportMyLoader's loader = jdk.internal.loader.ClassLoaders$AppClassLoader@", //<-this is still printed because TestClassLoader simply delegates to Launcher$AppLoader, but ... .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 @@ public class SpecifySysLoaderProp {
.assertNormalExit(output -> { .assertNormalExit(output -> {
output.shouldMatch(".class,load. TestClassLoader source: file:"); output.shouldMatch(".class,load. TestClassLoader source: file:");
output.shouldMatch(".class,load. ReportMyLoader source: file:.*" + jarFileName); 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 // (3) Try to change the java.system.class.loader programmatically after

View File

@ -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<String> 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");
}
}
}
}

View File

@ -0,0 +1,71 @@
/*
* 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 8249276
* @summary Make sure that archived module graph is not loaded if critical classes have been redefined.
* @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds
* @requires vm.cds
* @requires vm.flavor != "minimal"
* @modules java.instrument
* @run driver RedefineClassesInModuleGraph
*/
import jdk.test.lib.process.OutputAnalyzer;
public class RedefineClassesInModuleGraph {
public static String appClasses[] = {
RedefineClassesInModuleGraphApp.class.getName(),
};
public static String agentClasses[] = {
RedefineClassesInModuleGraphAgent.class.getName(),
RedefineClassesInModuleGraphTransformer.class.getName(),
};
private static final String MANIFEST =
"Manifest-Version: 1.0\n" +
"Premain-Class: RedefineClassesInModuleGraphAgent\n" +
"Can-Retransform-Classes: true\n" +
"Can-Redefine-Classes: true\n";
public static void main(String[] args) throws Throwable {
String agentJar =
ClassFileInstaller.writeJar("RedefineClassesInModuleGraphAgent.jar",
ClassFileInstaller.Manifest.fromString(MANIFEST),
agentClasses);
String appJar =
ClassFileInstaller.writeJar("RedefineClassesInModuleGraphApp.jar", appClasses);
TestCommon.testDump(appJar, agentClasses);
TestCommon.run(
"-cp", appJar,
"-javaagent:" + agentJar,
RedefineClassesInModuleGraphApp.class.getName())
.assertNormalExit();
}
}

View File

@ -0,0 +1,38 @@
/*
* 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.
*
*/
import java.lang.instrument.Instrumentation;
public class RedefineClassesInModuleGraphAgent {
private static Instrumentation savedInstrumentation;
public static void premain(String agentArguments, Instrumentation instrumentation) {
instrumentation.addTransformer(new RedefineClassesInModuleGraphTransformer(), /*canRetransform=*/true);
savedInstrumentation = instrumentation;
}
public static Instrumentation getInstrumentation() {
return savedInstrumentation;
}
}

View File

@ -0,0 +1,32 @@
/*
* 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.
*
*/
public class RedefineClassesInModuleGraphApp {
public static void main(String args[]) {
Module m = Object.class.getModule();
ModuleLayer ml = m.getLayer();
System.out.println(m);
System.out.println(ml);
}
}

View File

@ -0,0 +1,41 @@
/*
* 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.
*
*/
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;
public class RedefineClassesInModuleGraphTransformer implements ClassFileTransformer {
public byte[] transform(ClassLoader loader, String name, Class<?> classBeingRedefined,
ProtectionDomain pd, byte[] buffer) throws IllegalClassFormatException {
System.out.println("transforming " + name);
if (name.equals("java/lang/Module") ||
name.equals("java/lang/ModuleLayer") ||
name.equals("java/lang/module/ResolvedModule")) {
throw new RuntimeException("Classes used by the module graph should never be transformed by Java agent");
}
return null;
}
}

View File

@ -24,7 +24,7 @@
/** /**
* @test * @test
* @requires vm.cds & !vm.jvmci * @requires vm.cds & !vm.graal.enabled
* @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds
* @run driver OptimizeModuleHandlingTest * @run driver OptimizeModuleHandlingTest
* @summary test module path changes for optimization of * @summary test module path changes for optimization of
@ -63,8 +63,8 @@ public class OptimizeModuleHandlingTest {
private static String CLASS_FOUND_MESSAGE = "com.foos.Test found"; 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 CLASS_NOT_FOUND_MESSAGE = "java.lang.ClassNotFoundException: com.foos.Test";
private static String OPTIMIZE_ENABLED = "Using optimized module handling enabled"; private static String OPTIMIZE_ENABLED = "optimized module handling: enabled";
private static String OPTIMIZE_DISABLED = "Using optimized module handling disabled"; 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_JAR = "class,load.*com.bars.Main.*[.]jar";
private static String MAIN_FROM_CDS = "class,load.*com.bars.Main.*shared objects file"; 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"; private static String TEST_FROM_JAR = "class,load.*com.foos.Test.*[.]jar";

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -53,7 +53,8 @@ public class ReplaceCriticalClasses {
.setXShareMode("dump") .setXShareMode("dump")
.setArchiveName(ReplaceCriticalClasses.class.getName() + ".jsa") .setArchiveName(ReplaceCriticalClasses.class.getName() + ".jsa")
.setUseVersion(false) .setUseVersion(false)
.addSuffix("-showversion"); .addSuffix("-showversion")
.addSuffix("-Xlog:cds");
CDSTestUtils.run(opts).assertNormalExit(""); CDSTestUtils.run(opts).assertNormalExit("");
launchChildProcesses(getTests()); launchChildProcesses(getTests());
@ -80,6 +81,8 @@ public class ReplaceCriticalClasses {
"-early -notshared java/lang/String", "-early -notshared java/lang/String",
"-early -notshared java/lang/Cloneable", "-early -notshared java/lang/Cloneable",
"-early -notshared java/io/Serializable", "-early -notshared java/io/Serializable",
"-early -notshared java/lang/Module",
"-early -notshared java/lang/ModuleLayer",
// CDS should not be disabled -- these critical classes cannot be replaced because // CDS should not be disabled -- these critical classes cannot be replaced because
// JvmtiExport::early_class_hook_env() is false. // JvmtiExport::early_class_hook_env() is false.
@ -87,13 +90,8 @@ public class ReplaceCriticalClasses {
"java/lang/String", "java/lang/String",
"java/lang/Cloneable", "java/lang/Cloneable",
"java/io/Serializable", "java/io/Serializable",
"java/lang/Module",
/* Try to replace classes that are used by the archived subgraph graphs. "java/lang/ModuleLayer",
The following test cases are in ReplaceCriticalClassesForSubgraphs.java.
"-early -notshared -subgraph java/lang/module/ResolvedModule jdk.internal.module.ArchivedModuleGraph",
"-early -notshared -subgraph java/lang/Long java.lang.Long$LongCache",
"-subgraph java/lang/Long java.lang.Long$LongCache",
*/
// Replace classes that are loaded after JVMTI_PHASE_PRIMORDIAL. It's OK to replace // Replace classes that are loaded after JVMTI_PHASE_PRIMORDIAL. It's OK to replace
// such // such
@ -116,12 +114,13 @@ public class ReplaceCriticalClasses {
static void launchChild(String args[]) throws Throwable { static void launchChild(String args[]) throws Throwable {
if (args.length < 1) { if (args.length < 1) {
throw new RuntimeException("Invalid test case. Should be <-early> <-subgraph> <-notshared> klassName subgraphKlass"); throw new RuntimeException("Invalid test case. Should be <-early> <-subgraph> <-notshared> <-nowhitebox> klassName subgraphKlass");
} }
String klassName = null; String klassName = null;
String subgraphKlass = null; String subgraphKlass = null;
String early = ""; String early = "";
boolean subgraph = false; boolean subgraph = false;
boolean whitebox = true;
String shared = "-shared"; String shared = "-shared";
for (int i=0; i<args.length-1; i++) { for (int i=0; i<args.length-1; i++) {
@ -130,6 +129,8 @@ public class ReplaceCriticalClasses {
early = "-early,"; early = "-early,";
} else if (opt.equals("-subgraph")) { } else if (opt.equals("-subgraph")) {
subgraph = true; subgraph = true;
} else if (opt.equals("-nowhitebox")) {
whitebox = false;
} else if (opt.equals("-notshared")) { } else if (opt.equals("-notshared")) {
shared = opt; shared = opt;
} else { } else {
@ -166,10 +167,11 @@ public class ReplaceCriticalClasses {
.addSuffix("-showversion", .addSuffix("-showversion",
"-Xlog:cds", "-Xlog:cds",
"-XX:+UnlockDiagnosticVMOptions", "-XX:+UnlockDiagnosticVMOptions",
agent, agent);
"-XX:+WhiteBoxAPI", if (whitebox) {
opts.addSuffix("-XX:+WhiteBoxAPI",
"-Xbootclasspath/a:" + ClassFileInstaller.getJarPath("whitebox.jar")); "-Xbootclasspath/a:" + ClassFileInstaller.getJarPath("whitebox.jar"));
}
if (subgraph) { if (subgraph) {
opts.addSuffix("-Xlog:cds,cds+heap"); opts.addSuffix("-Xlog:cds,cds+heap");
} }
@ -191,6 +193,8 @@ public class ReplaceCriticalClasses {
if (expectShared) { if (expectShared) {
if (!out.getOutput().contains("UseSharedSpaces: Unable to map at required address in java heap")) { if (!out.getOutput().contains("UseSharedSpaces: Unable to map at required address in java heap")) {
out.shouldContain(subgraphInit); out.shouldContain(subgraphInit);
// If the subgraph is successfully initialized, the specified shared class must not be rewritten.
out.shouldNotContain("Rewriting done.");
} }
} else { } else {
out.shouldNotContain(subgraphInit); out.shouldNotContain(subgraphInit);
@ -200,6 +204,7 @@ public class ReplaceCriticalClasses {
} }
static void testInChild(boolean shouldBeShared, Class klass) { static void testInChild(boolean shouldBeShared, Class klass) {
try {
WhiteBox wb = WhiteBox.getWhiteBox(); WhiteBox wb = WhiteBox.getWhiteBox();
if (shouldBeShared && !wb.isSharedClass(klass)) { if (shouldBeShared && !wb.isSharedClass(klass)) {
@ -208,7 +213,10 @@ public class ReplaceCriticalClasses {
if (!shouldBeShared && wb.isSharedClass(klass)) { if (!shouldBeShared && wb.isSharedClass(klass)) {
throw new RuntimeException(klass + " should not be shared but actually is."); throw new RuntimeException(klass + " should not be shared but actually is.");
} }
System.out.println("wb.isSharedClass(klass): " + wb.isSharedClass(klass) + " == " + shouldBeShared); System.out.println("wb.isSharedClass(" + klass + "): " + wb.isSharedClass(klass) + " == " + shouldBeShared);
} catch (UnsatisfiedLinkError e) {
System.out.println("WhiteBox is disabled -- because test has -nowhitebox");
}
String strings[] = { String strings[] = {
// interned strings from j.l.Object // interned strings from j.l.Object

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -40,10 +40,21 @@ public class ReplaceCriticalClassesForSubgraphs extends ReplaceCriticalClasses {
public String[] getTests() { public String[] getTests() {
String tests[] = { String tests[] = {
// Try to replace classes that are used by the archived subgraph graphs. // Try to replace classes that are used by the archived subgraph graphs. (CDS should be disabled)
"-early -notshared -subgraph java/lang/module/ResolvedModule jdk.internal.module.ArchivedModuleGraph", "-early -notshared -subgraph java/lang/module/ResolvedModule jdk.internal.module.ArchivedModuleGraph",
"-early -notshared -subgraph java/lang/Long java.lang.Long$LongCache", "-early -notshared -subgraph java/lang/Long java.lang.Long$LongCache",
// CDS should not be disabled -- these critical classes cannot be replaced because
// JvmtiExport::early_class_hook_env() is false.
"-subgraph java/lang/module/ResolvedModule jdk.internal.module.ArchivedModuleGraph",
"-subgraph java/lang/Long java.lang.Long$LongCache", "-subgraph java/lang/Long java.lang.Long$LongCache",
// Tests for archived full module graph. We cannot use whitebox, which requires appending to bootclasspath.
// VM will disable full module graph if bootclasspath is appended.
"-nowhitebox -early -notshared -subgraph java/lang/Module jdk.internal.module.ArchivedBootLayer",
"-nowhitebox -early -notshared -subgraph java/lang/ModuleLayer jdk.internal.module.ArchivedBootLayer",
"-nowhitebox -subgraph java/lang/Module jdk.internal.module.ArchivedBootLayer",
"-nowhitebox -subgraph java/lang/ModuleLayer jdk.internal.module.ArchivedBootLayer",
}; };
return tests; return tests;
} }

View File

@ -34,13 +34,18 @@ import jdk.test.lib.process.OutputAnalyzer;
import jdk.test.lib.process.ProcessTools; import jdk.test.lib.process.ProcessTools;
public class ModulesTest { 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 { public static void main(String[] args) throws Exception {
testModuleTrace("-Xlog:module=trace", "-version"); testModuleTrace("-Xlog:module=trace", XSHARE_OFF, "-version");
testModuleLoad("-Xlog:module+load", "-version"); testModuleLoad("-Xlog:module+load", XSHARE_OFF, "-version");
testModuleUnload("-Xlog:module+unload", "-version"); testModuleUnload("-Xlog:module+unload", XSHARE_OFF, "-version");
// same as -Xlog:module+load -Xlog:module+unload // 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 { static void testModuleTrace(String... args) throws Exception {