# HG changeset patch # User ccheung # Date 1503959644 25200 # Node ID a993ec29ec75a1870a70e6b564d408540a5edec8 # Parent e704f55561c3af127a2e715eacea5b8ac89c73f0 8186842: Use Java class loaders for creating the CDS archive Reviewed-by: coleenp, jiangli, iklam, mseledtsov Contributed-by: calvin.cheung@oracle.com, ioi.lam@oracle.com diff -r e704f55561c3 -r a993ec29ec75 hotspot/src/cpu/sparc/vm/templateTable_sparc.cpp --- a/hotspot/src/cpu/sparc/vm/templateTable_sparc.cpp Wed Aug 30 19:18:22 2017 -0400 +++ b/hotspot/src/cpu/sparc/vm/templateTable_sparc.cpp Mon Aug 28 15:34:04 2017 -0700 @@ -2928,7 +2928,7 @@ __ br(Assembler::zero, false, Assembler::pt, notFinal); __ delayed()->and3(Rret, 0xFF, G4_scratch); // gets number of parameters - if (RewriteBytecodes && !UseSharedSpaces) { + if (RewriteBytecodes && !UseSharedSpaces && !DumpSharedSpaces) { patch_bytecode(Bytecodes::_fast_invokevfinal, Rscratch, Rtemp); } diff -r e704f55561c3 -r a993ec29ec75 hotspot/src/share/vm/classfile/classLoader.cpp --- a/hotspot/src/share/vm/classfile/classLoader.cpp Wed Aug 30 19:18:22 2017 -0400 +++ b/hotspot/src/share/vm/classfile/classLoader.cpp Mon Aug 28 15:34:04 2017 -0700 @@ -147,6 +147,7 @@ ClassPathEntry* ClassLoader::_first_append_entry = NULL; ClassPathEntry* ClassLoader::_last_append_entry = NULL; int ClassLoader::_num_entries = 0; +int ClassLoader::_num_boot_entries = -1; #if INCLUDE_CDS GrowableArray* ClassLoader::_boot_modules_array = NULL; GrowableArray* ClassLoader::_platform_modules_array = NULL; @@ -242,7 +243,7 @@ // Given a fully qualified class name, find its defining package in the class loader's // package entry table. -static PackageEntry* get_package_entry(const char* class_name, ClassLoaderData* loader_data, TRAPS) { +PackageEntry* ClassLoader::get_package_entry(const char* class_name, ClassLoaderData* loader_data, TRAPS) { ResourceMark rm(THREAD); const char *pkg_name = ClassLoader::package_from_name(class_name); if (pkg_name == NULL) { @@ -509,7 +510,7 @@ #endif } else { - PackageEntry* package_entry = get_package_entry(name, ClassLoaderData::the_null_class_loader_data(), CHECK_NULL); + PackageEntry* package_entry = ClassLoader::get_package_entry(name, ClassLoaderData::the_null_class_loader_data(), CHECK_NULL); if (package_entry != NULL) { ResourceMark rm; // Get the module name @@ -540,6 +541,13 @@ return NULL; } +JImageLocationRef ClassLoader::jimage_find_resource(JImageFile* jf, + const char* module_name, + const char* file_name, + jlong &size) { + return ((*JImageFindResource)(jf, module_name, get_jimage_version_string(), file_name, &size)); +} + #ifndef PRODUCT bool ctw_visitor(JImageFile* jimage, const char* module_name, const char* version, const char* package, @@ -1459,9 +1467,6 @@ // This would include: // [--patch-module==()*]; [jimage | exploded module build] // - // DumpSharedSpaces and search_append_only are mutually exclusive and cannot - // be true at the same time. - assert(!(DumpSharedSpaces && search_append_only), "DumpSharedSpaces and search_append_only are both true"); // Load Attempt #1: --patch-module // Determine the class' defining module. If it appears in the _patch_mod_entries, @@ -1507,6 +1512,11 @@ e = _first_append_entry; while (e != NULL) { + if (DumpSharedSpaces && classpath_index >= _num_boot_entries) { + // Do not load any class from the app classpath using the boot loader. Let + // the built-in app class laoder load them. + break; + } stream = e->open_stream(file_name, CHECK_NULL); if (!context.check(stream, classpath_index)) { return NULL; @@ -1520,9 +1530,6 @@ } if (NULL == stream) { - if (DumpSharedSpaces) { - tty->print_cr("Preload Warning: Cannot find %s", class_name); - } return NULL; } @@ -1548,6 +1555,100 @@ return context.record_result(name, e, classpath_index, result, THREAD); } +#if INCLUDE_CDS +static char* skip_uri_protocol(char* source) { + if (strncmp(source, "file:", 5) == 0) { + // file: protocol path could start with file:/ or file:/// + // locate the char after all the forward slashes + int offset = 5; + while (*(source + offset) == '/') { + offset++; + } + source += offset; + // for non-windows platforms, move back one char as the path begins with a '/' +#ifndef _WINDOWS + source -= 1; +#endif + } else if (strncmp(source, "jrt:/", 5) == 0) { + source += 5; + } + return source; +} + +void ClassLoader::record_shared_class_loader_type(InstanceKlass* ik, const ClassFileStream* stream) { + assert(DumpSharedSpaces, "sanity"); + assert(stream != NULL, "sanity"); + + if (ik->is_anonymous()) { + // We do not archive anonymous classes. + return; + } + + if (stream->source() == NULL) { + if (ik->class_loader() == NULL) { + // JFR classes + ik->set_shared_classpath_index(0); + ik->set_class_loader_type(ClassLoader::BOOT_LOADER); + } + return; + } + + assert(has_jrt_entry(), "CDS dumping does not support exploded JDK build"); + + ModuleEntry* module = ik->module(); + ClassPathEntry* e = NULL; + int classpath_index = 0; + + // Check if the class is from the runtime image + if (module != NULL && (module->location() != NULL) && + (module->location()->starts_with("jrt:"))) { + e = _jrt_entry; + classpath_index = 0; + } else { + classpath_index = 1; + ResourceMark rm; + char* canonical_path = NEW_RESOURCE_ARRAY(char, JVM_MAXPATHLEN); + for (e = _first_append_entry; e != NULL; e = e->next()) { + if (get_canonical_path(e->name(), canonical_path, JVM_MAXPATHLEN)) { + char* src = (char*)stream->source(); + // save the path from the file: protocol or the module name from the jrt: protocol + // if no protocol prefix is found, src is the same as stream->source() after the following call + src = skip_uri_protocol(src); + if (strcmp(canonical_path, os::native_path((char*)src)) == 0) { + break; + } + classpath_index ++; + } + } + if (e == NULL) { + assert(ik->shared_classpath_index() < 0, + "must be a class from a custom jar which isn't in the class path or boot class path"); + return; + } + } + + if (classpath_index < _num_boot_entries) { + // ik is either: + // 1) a boot class loaded from the runtime image during vm initialization (classpath_index = 0); or + // 2) a user's class from -Xbootclasspath/a (classpath_index > 0) + // In the second case, the classpath_index, classloader_type will be recorded via + // context.record_result() in ClassLoader::load_class(Symbol* name, bool search_append_only, TRAPS). + if (classpath_index > 0) { + return; + } + } + + ResourceMark rm; + const char* const class_name = ik->name()->as_C_string(); + const char* const file_name = file_name_for_class_name(class_name, + ik->name()->utf8_length()); + assert(file_name != NULL, "invariant"); + Thread* THREAD = Thread::current(); + ClassLoaderExt::Context context(class_name, file_name, CATCH); + context.record_result(ik->name(), e, classpath_index, ik, THREAD); +} +#endif // INCLUDE_CDS + // Initialize the class loader's access to methods in libzip. Parse and // process the boot classpath into a list ClassPathEntry objects. Once // this list has been created, it must not change order (see class PackageInfo) @@ -1632,6 +1733,7 @@ #if INCLUDE_CDS void ClassLoader::initialize_shared_path() { if (DumpSharedSpaces) { + _num_boot_entries = _num_entries; ClassLoaderExt::setup_search_paths(); _shared_paths_misc_info->write_jint(0); // see comments in SharedPathsMiscInfo::check() } diff -r e704f55561c3 -r a993ec29ec75 hotspot/src/share/vm/classfile/classLoader.hpp --- a/hotspot/src/share/vm/classfile/classLoader.hpp Wed Aug 30 19:18:22 2017 -0400 +++ b/hotspot/src/share/vm/classfile/classLoader.hpp Mon Aug 28 15:34:04 2017 -0700 @@ -25,6 +25,7 @@ #ifndef SHARE_VM_CLASSFILE_CLASSLOADER_HPP #define SHARE_VM_CLASSFILE_CLASSLOADER_HPP +#include "classfile/jimage.hpp" #include "runtime/orderAccess.hpp" #include "runtime/perfData.hpp" #include "utilities/exceptions.hpp" @@ -47,6 +48,7 @@ class JImageFile; class ClassFileStream; +class PackageEntry; class ClassPathEntry : public CHeapObj { private: @@ -103,7 +105,6 @@ jlong pos; /* position of LOC header (if negative) or data */ } jzentry; - class ClassPathZipEntry: public ClassPathEntry { enum { _unknown = 0, @@ -249,6 +250,10 @@ // the entries on the _first_append_entry linked list. static int _num_entries; + // number of entries in the boot class path including the + // java runtime image + static int _num_boot_entries; + // Array of module names associated with the boot class loader CDS_ONLY(static GrowableArray* _boot_modules_array;) @@ -289,6 +294,7 @@ static bool get_canonical_path(const char* orig, char* out, int len); static const char* file_name_for_class_name(const char* class_name, int class_name_len); + static PackageEntry* get_package_entry(const char* class_name, ClassLoaderData* loader_data, TRAPS); public: static jboolean decompress(void *in, u8 inSize, void *out, u8 outSize, char **pmsg); @@ -436,7 +442,10 @@ static void initialize_module_loader_map(JImageFile* jimage); static s2 classloader_type(Symbol* class_name, ClassPathEntry* e, int classpath_index, TRAPS); + static void record_shared_class_loader_type(InstanceKlass* ik, const ClassFileStream* stream); #endif + static JImageLocationRef jimage_find_resource(JImageFile* jf, const char* module_name, + const char* file_name, jlong &size); static void trace_class_path(const char* msg, const char* name = NULL); diff -r e704f55561c3 -r a993ec29ec75 hotspot/src/share/vm/classfile/classLoaderExt.hpp --- a/hotspot/src/share/vm/classfile/classLoaderExt.hpp Wed Aug 30 19:18:22 2017 -0400 +++ b/hotspot/src/share/vm/classfile/classLoaderExt.hpp Mon Aug 28 15:34:04 2017 -0700 @@ -26,6 +26,7 @@ #define SHARE_VM_CLASSFILE_CLASSLOADEREXT_HPP #include "classfile/classLoader.hpp" +#include "classfile/systemDictionary.hpp" #include "oops/instanceKlass.hpp" #include "runtime/handles.hpp" @@ -56,8 +57,15 @@ if (ClassLoader::add_package(_file_name, classpath_index, THREAD)) { #if INCLUDE_CDS if (DumpSharedSpaces) { - s2 classloader_type = ClassLoader::classloader_type( - class_name, e, classpath_index, CHECK_(result)); + oop loader = result->class_loader(); + s2 classloader_type = ClassLoader::BOOT_LOADER; + if (SystemDictionary::is_system_class_loader(loader)) { + classloader_type = ClassLoader::APP_LOADER; + ClassLoaderExt::set_has_app_classes(); + } else if (SystemDictionary::is_platform_class_loader(loader)) { + classloader_type = ClassLoader::PLATFORM_LOADER; + ClassLoaderExt::set_has_platform_classes(); + } result->set_shared_classpath_index(classpath_index); result->set_class_loader_type(classloader_type); } @@ -82,6 +90,13 @@ return true; } static Klass* load_one_class(ClassListParser* parser, TRAPS); +#if INCLUDE_CDS + static void set_has_app_classes() {} + static void set_has_platform_classes() {} + static char* read_manifest(ClassPathEntry* entry, jint *manifest_size, TRAPS) { + return NULL; + } +#endif }; #endif // SHARE_VM_CLASSFILE_CLASSLOADEREXT_HPP diff -r e704f55561c3 -r a993ec29ec75 hotspot/src/share/vm/classfile/dictionary.cpp --- a/hotspot/src/share/vm/classfile/dictionary.cpp Wed Aug 30 19:18:22 2017 -0400 +++ b/hotspot/src/share/vm/classfile/dictionary.cpp Mon Aug 28 15:34:04 2017 -0700 @@ -366,11 +366,21 @@ for (int i = 0; i < table_size(); ++i) { DictionaryEntry* p = bucket(i); while (p != NULL) { - DictionaryEntry* tmp; - tmp = p->next(); - p->set_next(master_list); - master_list = p; - p = tmp; + DictionaryEntry* next = p->next(); + InstanceKlass*ik = p->instance_klass(); + // we cannot include signed classes in the archive because the certificates + // used during dump time may be different than those used during + // runtime (due to expiration, etc). + if (ik->signers() != NULL) { + ResourceMark rm; + tty->print_cr("Preload Warning: Skipping %s from signed JAR", + ik->name()->as_C_string()); + free_entry(p); + } else { + p->set_next(master_list); + master_list = p; + } + p = next; } set_entry(i, NULL); } diff -r e704f55561c3 -r a993ec29ec75 hotspot/src/share/vm/classfile/dictionary.hpp --- a/hotspot/src/share/vm/classfile/dictionary.hpp Wed Aug 30 19:18:22 2017 -0400 +++ b/hotspot/src/share/vm/classfile/dictionary.hpp Mon Aug 28 15:34:04 2017 -0700 @@ -49,21 +49,6 @@ DictionaryEntry* get_entry(int index, unsigned int hash, Symbol* name); protected: - DictionaryEntry* bucket(int i) const { - return (DictionaryEntry*)Hashtable::bucket(i); - } - - // The following method is not MT-safe and must be done under lock. - DictionaryEntry** bucket_addr(int i) { - return (DictionaryEntry**)Hashtable::bucket_addr(i); - } - - void add_entry(int index, DictionaryEntry* new_entry) { - Hashtable::add_entry(index, (HashtableEntry*)new_entry); - } - - void free_entry(DictionaryEntry* entry); - static size_t entry_size(); public: Dictionary(ClassLoaderData* loader_data, int table_size); @@ -107,6 +92,24 @@ void print_on(outputStream* st) const; void verify(); + DictionaryEntry* bucket(int i) const { + return (DictionaryEntry*)Hashtable::bucket(i); + } + + // The following method is not MT-safe and must be done under lock. + DictionaryEntry** bucket_addr(int i) { + return (DictionaryEntry**)Hashtable::bucket_addr(i); + } + + void add_entry(int index, DictionaryEntry* new_entry) { + Hashtable::add_entry(index, (HashtableEntry*)new_entry); + } + + void unlink_entry(DictionaryEntry* entry) { + Hashtable::unlink_entry((HashtableEntry*)entry); + } + + void free_entry(DictionaryEntry* entry); }; // An entry in the class loader data dictionaries, this describes a class as @@ -254,10 +257,6 @@ class SymbolPropertyTable : public Hashtable { friend class VMStructs; private: - SymbolPropertyEntry* bucket(int i) { - return (SymbolPropertyEntry*) Hashtable::bucket(i); - } - // The following method is not MT-safe and must be done under lock. SymbolPropertyEntry** bucket_addr(int i) { return (SymbolPropertyEntry**) Hashtable::bucket_addr(i); @@ -311,5 +310,9 @@ void methods_do(void f(Method*)); void verify(); + + SymbolPropertyEntry* bucket(int i) { + return (SymbolPropertyEntry*) Hashtable::bucket(i); + } }; #endif // SHARE_VM_CLASSFILE_DICTIONARY_HPP diff -r e704f55561c3 -r a993ec29ec75 hotspot/src/share/vm/classfile/klassFactory.cpp --- a/hotspot/src/share/vm/classfile/klassFactory.cpp Wed Aug 30 19:18:22 2017 -0400 +++ b/hotspot/src/share/vm/classfile/klassFactory.cpp Mon Aug 28 15:34:04 2017 -0700 @@ -70,11 +70,25 @@ ClassLoaderData* loader_data = ClassLoaderData::class_loader_data(class_loader()); int path_index = ik->shared_classpath_index(); - SharedClassPathEntry* ent = - (SharedClassPathEntry*)FileMapInfo::shared_classpath(path_index); + const char* pathname; + if (path_index < 0) { + // shared classes loaded by user defined class loader + // do not have shared_classpath_index + ModuleEntry* mod_entry = ik->module(); + if (mod_entry != NULL && (mod_entry->location() != NULL)) { + ResourceMark rm; + pathname = (const char*)(mod_entry->location()->as_C_string()); + } else { + pathname = ""; + } + } else { + SharedClassPathEntry* ent = + (SharedClassPathEntry*)FileMapInfo::shared_classpath(path_index); + pathname = ent == NULL ? NULL : ent->name(); + } ClassFileStream* stream = new ClassFileStream(ptr, end_ptr - ptr, - ent == NULL ? NULL : ent->name(), + pathname, ClassFileStream::verify); ClassFileParser parser(stream, class_name, @@ -215,8 +229,10 @@ TRACE_KLASS_CREATION(result, parser, THREAD); -#if INCLUDE_CDS && INCLUDE_JVMTI +#if INCLUDE_CDS if (DumpSharedSpaces) { + ClassLoader::record_shared_class_loader_type(result, stream); +#if INCLUDE_JVMTI assert(cached_class_file == NULL, "Sanity"); // Archive the class stream data into the optional data section JvmtiCachedClassFileData *p; @@ -233,8 +249,9 @@ p->length = len; memcpy(p->data, bytes, len); result->set_archived_class_data(p); +#endif // INCLUDE_JVMTI } -#endif +#endif // INCLUDE_CDS return result; } diff -r e704f55561c3 -r a993ec29ec75 hotspot/src/share/vm/classfile/stringTable.cpp --- a/hotspot/src/share/vm/classfile/stringTable.cpp Wed Aug 30 19:18:22 2017 -0400 +++ b/hotspot/src/share/vm/classfile/stringTable.cpp Mon Aug 28 15:34:04 2017 -0700 @@ -729,7 +729,6 @@ } G1CollectedHeap::heap()->end_archive_alloc_range(string_space, os::vm_allocation_granularity()); - assert(string_space->length() <= 2, "sanity"); return true; } diff -r e704f55561c3 -r a993ec29ec75 hotspot/src/share/vm/classfile/systemDictionary.cpp --- a/hotspot/src/share/vm/classfile/systemDictionary.cpp Wed Aug 30 19:18:22 2017 -0400 +++ b/hotspot/src/share/vm/classfile/systemDictionary.cpp Mon Aug 28 15:34:04 2017 -0700 @@ -66,6 +66,7 @@ #include "prims/resolvedMethodTable.hpp" #include "prims/methodHandles.hpp" #include "runtime/arguments.hpp" +#include "runtime/arguments_ext.hpp" #include "runtime/biasedLocking.hpp" #include "runtime/fieldType.hpp" #include "runtime/handles.inline.hpp" @@ -869,7 +870,7 @@ // during compilations. MutexLocker mu(Compile_lock, THREAD); update_dictionary(d_index, d_hash, p_index, p_hash, - k, class_loader, THREAD); + k, class_loader, THREAD); } if (JvmtiExport::should_post_class_load()) { @@ -1006,7 +1007,6 @@ // Create a new CLD for anonymous class, that uses the same class loader // as the host_klass guarantee(host_klass->class_loader() == class_loader(), "should be the same"); - guarantee(!DumpSharedSpaces, "must not create anonymous classes when dumping"); loader_data = ClassLoaderData::anonymous_class_loader_data(class_loader(), CHECK_NULL); } else { loader_data = ClassLoaderData::class_loader_data(class_loader()); @@ -1075,6 +1075,15 @@ Handle protection_domain, ClassFileStream* st, TRAPS) { +#if INCLUDE_CDS + ResourceMark rm(THREAD); + if (DumpSharedSpaces && !class_loader.is_null() && + !ArgumentsExt::using_AppCDS() && strcmp(class_name->as_C_string(), "Unnamed") != 0) { + // If AppCDS is not enabled, don't define the class at dump time (except for the "Unnamed" + // class, which is used by MethodHandles). + THROW_MSG_NULL(vmSymbols::java_lang_ClassNotFoundException(), class_name->as_C_string()); + } +#endif HandleMark hm(THREAD); @@ -1101,11 +1110,13 @@ InstanceKlass* k = NULL; #if INCLUDE_CDS - k = SystemDictionaryShared::lookup_from_stream(class_name, - class_loader, - protection_domain, - st, - CHECK_NULL); + if (!DumpSharedSpaces) { + k = SystemDictionaryShared::lookup_from_stream(class_name, + class_loader, + protection_domain, + st, + CHECK_NULL); + } #endif if (k == NULL) { @@ -1214,6 +1225,16 @@ "Cannot use sharing if java.base is patched"); ResourceMark rm; int path_index = ik->shared_classpath_index(); + ClassLoaderData* loader_data = class_loader_data(class_loader); + if (path_index < 0) { + // path_index < 0 indicates that the class is intended for a custom loader + // and should not be loaded by boot/platform/app loaders + if (loader_data->is_builtin_class_loader_data()) { + return false; + } else { + return true; + } + } SharedClassPathEntry* ent = (SharedClassPathEntry*)FileMapInfo::shared_classpath(path_index); if (!Universe::is_module_initialized()) { @@ -1227,7 +1248,6 @@ PackageEntry* pkg_entry = NULL; ModuleEntry* mod_entry = NULL; const char* pkg_string = NULL; - ClassLoaderData* loader_data = class_loader_data(class_loader); pkg_name = InstanceKlass::package_from_name(class_name, CHECK_false); if (pkg_name != NULL) { pkg_string = pkg_name->as_C_string(); @@ -1400,6 +1420,18 @@ } return ik; } + +void SystemDictionary::clear_invoke_method_table() { + SymbolPropertyEntry* spe = NULL; + for (int index = 0; index < _invoke_method_table->table_size(); index++) { + SymbolPropertyEntry* p = _invoke_method_table->bucket(index); + while (p != NULL) { + spe = p; + p = p->next(); + _invoke_method_table->free_entry(spe); + } + } +} #endif // INCLUDE_CDS InstanceKlass* SystemDictionary::load_instance_class(Symbol* class_name, Handle class_loader, TRAPS) { @@ -1446,7 +1478,6 @@ } } } else { - assert(!DumpSharedSpaces, "Archive dumped after module system initialization"); // After the module system has been initialized, check if the class' // package is in a module defined to the boot loader. if (pkg_name == NULL || pkg_entry == NULL || pkg_entry->in_unnamed_module()) { @@ -1965,8 +1996,19 @@ invoke_method_table()->methods_do(f); } +class RemoveClassesClosure : public CLDClosure { + public: + void do_cld(ClassLoaderData* cld) { + if (cld->is_system_class_loader_data() || cld->is_platform_class_loader_data()) { + cld->dictionary()->remove_classes_in_error_state(); + } + } +}; + void SystemDictionary::remove_classes_in_error_state() { ClassLoaderData::the_null_class_loader_data()->dictionary()->remove_classes_in_error_state(); + RemoveClassesClosure rcc; + ClassLoaderDataGraph::cld_do(&rcc); } // ---------------------------------------------------------------------------- @@ -2907,6 +2949,56 @@ } } +class CombineDictionariesClosure : public CLDClosure { + private: + Dictionary* _master_dictionary; + public: + CombineDictionariesClosure(Dictionary* master_dictionary) : + _master_dictionary(master_dictionary) {} + void do_cld(ClassLoaderData* cld) { + ResourceMark rm; + if (cld->is_system_class_loader_data() || cld->is_platform_class_loader_data()) { + for (int i = 0; i < cld->dictionary()->table_size(); ++i) { + Dictionary* curr_dictionary = cld->dictionary(); + DictionaryEntry* p = curr_dictionary->bucket(i); + while (p != NULL) { + Symbol* name = p->instance_klass()->name(); + unsigned int d_hash = _master_dictionary->compute_hash(name); + int d_index = _master_dictionary->hash_to_index(d_hash); + DictionaryEntry* next = p->next(); + if (p->literal()->class_loader_data() != cld) { + // This is an initiating class loader entry; don't use it + log_trace(cds)("Skipping initiating cl entry: %s", name->as_C_string()); + curr_dictionary->free_entry(p); + } else { + log_trace(cds)("Moved to boot dictionary: %s", name->as_C_string()); + curr_dictionary->unlink_entry(p); + p->set_pd_set(NULL); // pd_set is runtime only information and will be reconstructed. + _master_dictionary->add_entry(d_index, p); + } + p = next; + } + *curr_dictionary->bucket_addr(i) = NULL; + } + } + } +}; + +// Combining platform and system loader dictionaries into boot loader dictionaries. +// During run time, we only have one shared dictionary. +void SystemDictionary::combine_shared_dictionaries() { + assert(DumpSharedSpaces, "dump time only"); + Dictionary* master_dictionary = ClassLoaderData::the_null_class_loader_data()->dictionary(); + CombineDictionariesClosure cdc(master_dictionary); + ClassLoaderDataGraph::cld_do(&cdc); + + // These tables are no longer valid or necessary. Keeping them around will + // cause SystemDictionary::verify() to fail. Let's empty them. + _placeholders = new PlaceholderTable(_placeholder_table_size); + _loader_constraints = new LoaderConstraintTable(_loader_constraint_size); + + NOT_PRODUCT(SystemDictionary::verify()); +} // caller needs ResourceMark const char* SystemDictionary::loader_name(const oop loader) { diff -r e704f55561c3 -r a993ec29ec75 hotspot/src/share/vm/classfile/systemDictionary.hpp --- a/hotspot/src/share/vm/classfile/systemDictionary.hpp Wed Aug 30 19:18:22 2017 -0400 +++ b/hotspot/src/share/vm/classfile/systemDictionary.hpp Mon Aug 28 15:34:04 2017 -0700 @@ -385,6 +385,7 @@ public: // Sharing support. static void reorder_dictionary_for_sharing(); + static void combine_shared_dictionaries(); static size_t count_bytes_for_buckets(); static size_t count_bytes_for_table(); static void copy_buckets(char* top, char* end); @@ -643,6 +644,7 @@ TRAPS); static bool is_system_class_loader(oop class_loader); static bool is_platform_class_loader(oop class_loader); + static void clear_invoke_method_table(); protected: static InstanceKlass* find_shared_class(Symbol* class_name); diff -r e704f55561c3 -r a993ec29ec75 hotspot/src/share/vm/interpreter/rewriter.cpp --- a/hotspot/src/share/vm/interpreter/rewriter.cpp Wed Aug 30 19:18:22 2017 -0400 +++ b/hotspot/src/share/vm/interpreter/rewriter.cpp Mon Aug 28 15:34:04 2017 -0700 @@ -109,6 +109,11 @@ MetadataFactory::free_metadata(loader_data, cache); _pool->set_cache(NULL); // so the verifier isn't confused } + + DEBUG_ONLY( + if (DumpSharedSpaces) { + cache->verify_just_initialized(); + }) } diff -r e704f55561c3 -r a993ec29ec75 hotspot/src/share/vm/logging/logTag.hpp --- a/hotspot/src/share/vm/logging/logTag.hpp Wed Aug 30 19:18:22 2017 -0400 +++ b/hotspot/src/share/vm/logging/logTag.hpp Mon Aug 28 15:34:04 2017 -0700 @@ -140,6 +140,7 @@ LOG_TAG(timer) \ LOG_TAG(update) \ LOG_TAG(unload) /* Trace unloading of classes */ \ + LOG_TAG(unshareable) \ LOG_TAG(verification) \ LOG_TAG(verify) \ LOG_TAG(vmoperation) \ diff -r e704f55561c3 -r a993ec29ec75 hotspot/src/share/vm/memory/metaspaceClosure.hpp --- a/hotspot/src/share/vm/memory/metaspaceClosure.hpp Wed Aug 30 19:18:22 2017 -0400 +++ b/hotspot/src/share/vm/memory/metaspaceClosure.hpp Mon Aug 28 15:34:04 2017 -0700 @@ -275,7 +275,8 @@ address, bool, UniqueMetaspaceClosure::my_hash, // solaris compiler doesn't like: primitive_hash
UniqueMetaspaceClosure::my_equals, // solaris compiler doesn't like: primitive_equals
- 16384> _has_been_visited; + 15889, // prime number + ResourceObj::C_HEAP> _has_been_visited; }; #endif // SHARE_VM_MEMORY_METASPACE_ITERATOR_HPP diff -r e704f55561c3 -r a993ec29ec75 hotspot/src/share/vm/memory/metaspaceShared.cpp --- a/hotspot/src/share/vm/memory/metaspaceShared.cpp Wed Aug 30 19:18:22 2017 -0400 +++ b/hotspot/src/share/vm/memory/metaspaceShared.cpp Mon Aug 28 15:34:04 2017 -0700 @@ -374,25 +374,63 @@ // Global object for holding classes that have been loaded. Since this // is run at a safepoint just before exit, this is the entire set of classes. static GrowableArray* _global_klass_objects; + +static void collect_array_classes(Klass* k) { + _global_klass_objects->append_if_missing(k); + if (k->is_array_klass()) { + // Add in the array classes too + ArrayKlass* ak = ArrayKlass::cast(k); + Klass* h = ak->higher_dimension(); + if (h != NULL) { + h->array_klasses_do(collect_array_classes); + } + } +} + class CollectClassesClosure : public KlassClosure { void do_klass(Klass* k) { if (!(k->is_instance_klass() && InstanceKlass::cast(k)->is_in_error_state())) { _global_klass_objects->append_if_missing(k); } + if (k->is_array_klass()) { + // Add in the array classes too + ArrayKlass* ak = ArrayKlass::cast(k); + Klass* h = ak->higher_dimension(); + if (h != NULL) { + h->array_klasses_do(collect_array_classes); + } + } } }; static void remove_unshareable_in_classes() { for (int i = 0; i < _global_klass_objects->length(); i++) { Klass* k = _global_klass_objects->at(i); - k->remove_unshareable_info(); + if (!k->is_objArray_klass()) { + // InstanceKlass and TypeArrayKlass will in turn call remove_unshareable_info + // on their array classes. + assert(k->is_instance_klass() || k->is_typeArray_klass(), "must be"); + k->remove_unshareable_info(); + } + } +} + +static void remove_java_mirror_in_classes() { + for (int i = 0; i < _global_klass_objects->length(); i++) { + Klass* k = _global_klass_objects->at(i); + if (!k->is_objArray_klass()) { + // InstanceKlass and TypeArrayKlass will in turn call remove_unshareable_info + // on their array classes. + assert(k->is_instance_klass() || k->is_typeArray_klass(), "must be"); + k->remove_java_mirror(); + } } } static void rewrite_nofast_bytecode(Method* method) { - RawBytecodeStream bcs(method); + BytecodeStream bcs(method); while (!bcs.is_last_bytecode()) { - Bytecodes::Code opcode = bcs.raw_next(); + Bytecodes::Code opcode = bcs.next(); switch (opcode) { case Bytecodes::_getfield: *bcs.bcp() = Bytecodes::_nofast_getfield; break; case Bytecodes::_putfield: *bcs.bcp() = Bytecodes::_nofast_putfield; break; @@ -446,6 +484,17 @@ } } +NOT_PRODUCT( +static void assert_not_anonymous_class(InstanceKlass* k) { + assert(!(k->is_anonymous()), "cannot archive anonymous classes"); +} + +// Anonymous classes are not stored inside any dictionaries. They are created by +// SystemDictionary::parse_stream() with a non-null host_klass. +static void assert_no_anonymoys_classes_in_dictionaries() { + ClassLoaderDataGraph::dictionary_classes_do(assert_not_anonymous_class); +}) + // Objects of the Metadata types (such as Klass and ConstantPool) have C++ vtables. // (In GCC this is the field ::_vptr, i.e., first word in the object.) // @@ -957,8 +1006,8 @@ } memcpy(p, obj, bytes); bool isnew = _new_loc_table->put(obj, (address)p); + log_trace(cds)("Copy: " PTR_FORMAT " ==> " PTR_FORMAT " %d", p2i(obj), p2i(p), bytes); assert(isnew, "must be"); - log_trace(cds)("Copy: " PTR_FORMAT " ==> " PTR_FORMAT " %d", p2i(obj), p2i(p), bytes); _alloc_stats->record(ref->msotype(), int(newtop - oldtop), read_only); if (ref->msotype() == MetaspaceObj::SymbolType) { @@ -1151,6 +1200,9 @@ // Reorder the system dictionary. Moving the symbols affects // how the hash table indices are calculated. SystemDictionary::reorder_dictionary_for_sharing(); + tty->print("Removing java_mirror ... "); + remove_java_mirror_in_classes(); + tty->print_cr("done. "); NOT_PRODUCT(SystemDictionary::verify();) size_t buckets_bytes = SystemDictionary::count_bytes_for_buckets(); @@ -1218,11 +1270,20 @@ rewrite_nofast_bytecodes_and_calculate_fingerprints(); tty->print_cr("done. "); + // Move classes from platform/system dictionaries into the boot dictionary + SystemDictionary::combine_shared_dictionaries(); + // Remove all references outside the metadata tty->print("Removing unshareable information ... "); remove_unshareable_in_classes(); tty->print_cr("done. "); + // We don't support archiving anonymous classes. Verify that they are not stored in + // the any dictionaries. + NOT_PRODUCT(assert_no_anonymoys_classes_in_dictionaries()); + + SystemDictionaryShared::finalize_verification_constraints(); + ArchiveCompactor::initialize(); ArchiveCompactor::copy_and_compact(); @@ -1312,6 +1373,14 @@ ArchiveCompactor::alloc_stats()->print_stats(int(_ro_region.used()), int(_rw_region.used()), int(_mc_region.used()), int(_md_region.used())); } + + if (PrintSystemDictionaryAtExit) { + SystemDictionary::print(); + } + // There may be other pending VM operations that operate on the InstanceKlasses, + // which will fail because InstanceKlasses::remove_unshareable_info() + // has been called. Forget these operations and exit the VM directly. + vm_direct_exit(0); } void VM_PopulateDumpSharedSpace::print_region_stats() { @@ -1438,10 +1507,6 @@ exit(1); } } - - // Copy the verification constraints from C_HEAP-alloced GrowableArrays to RO-alloced - // Arrays - SystemDictionaryShared::finalize_verification_constraints(); } void MetaspaceShared::prepare_for_dumping() { @@ -1509,17 +1574,11 @@ link_and_cleanup_shared_classes(CATCH); tty->print_cr("Rewriting and linking classes: done"); + SystemDictionary::clear_invoke_method_table(); + VM_PopulateDumpSharedSpace op; VMThread::execute(&op); } - - if (PrintSystemDictionaryAtExit) { - SystemDictionary::print(); - } - - // Since various initialization steps have been undone by this process, - // it is not reasonable to continue running a java process. - exit(0); } @@ -1529,8 +1588,14 @@ while (parser.parse_one_line()) { Klass* klass = ClassLoaderExt::load_one_class(&parser, THREAD); - - CLEAR_PENDING_EXCEPTION; + if (HAS_PENDING_EXCEPTION) { + if (klass == NULL && + (PENDING_EXCEPTION->klass()->name() == vmSymbols::java_lang_ClassNotFoundException())) { + // print a warning only when the pending exception is class not found + tty->print_cr("Preload Warning: Cannot find %s", parser.current_class_name()); + } + CLEAR_PENDING_EXCEPTION; + } if (klass != NULL) { if (log_is_enabled(Trace, cds)) { ResourceMark rm; diff -r e704f55561c3 -r a993ec29ec75 hotspot/src/share/vm/oops/arrayKlass.cpp --- a/hotspot/src/share/vm/oops/arrayKlass.cpp Wed Aug 30 19:18:22 2017 -0400 +++ b/hotspot/src/share/vm/oops/arrayKlass.cpp Mon Aug 28 15:34:04 2017 -0700 @@ -187,12 +187,29 @@ void ArrayKlass::remove_unshareable_info() { Klass::remove_unshareable_info(); + if (_higher_dimension != NULL) { + ArrayKlass *ak = ArrayKlass::cast(higher_dimension()); + ak->remove_unshareable_info(); + } +} + +void ArrayKlass::remove_java_mirror() { + Klass::remove_java_mirror(); + if (_higher_dimension != NULL) { + ArrayKlass *ak = ArrayKlass::cast(higher_dimension()); + ak->remove_java_mirror(); + } } void ArrayKlass::restore_unshareable_info(ClassLoaderData* loader_data, Handle protection_domain, TRAPS) { assert(loader_data == ClassLoaderData::the_null_class_loader_data(), "array classes belong to null loader"); Klass::restore_unshareable_info(loader_data, protection_domain, CHECK); // Klass recreates the component mirror also + + if (_higher_dimension != NULL) { + ArrayKlass *ak = ArrayKlass::cast(higher_dimension()); + ak->restore_unshareable_info(loader_data, protection_domain, CHECK); + } } // Printing diff -r e704f55561c3 -r a993ec29ec75 hotspot/src/share/vm/oops/arrayKlass.hpp --- a/hotspot/src/share/vm/oops/arrayKlass.hpp Wed Aug 30 19:18:22 2017 -0400 +++ b/hotspot/src/share/vm/oops/arrayKlass.hpp Mon Aug 28 15:34:04 2017 -0700 @@ -130,6 +130,7 @@ // CDS support - remove and restore oops from metadata. Oops are not shared. virtual void remove_unshareable_info(); + virtual void remove_java_mirror(); virtual void restore_unshareable_info(ClassLoaderData* loader_data, Handle protection_domain, TRAPS); // Printing diff -r e704f55561c3 -r a993ec29ec75 hotspot/src/share/vm/oops/constantPool.cpp --- a/hotspot/src/share/vm/oops/constantPool.cpp Wed Aug 30 19:18:22 2017 -0400 +++ b/hotspot/src/share/vm/oops/constantPool.cpp Mon Aug 28 15:34:04 2017 -0700 @@ -257,10 +257,14 @@ } objArrayOop rr = resolved_references(); + Array* ref_map = reference_map(); if (rr != NULL) { - for (int i = 0; i < rr->length(); i++) { + int ref_map_len = ref_map == NULL ? 0 : ref_map->length(); + int rr_len = rr->length(); + for (int i = 0; i < rr_len; i++) { oop p = rr->obj_at(i); - if (p != NULL) { + rr->obj_at_put(i, NULL); + if (p != NULL && i < ref_map_len) { int index = object_to_cp_index(i); // Skip the entry if the string hash code is 0 since the string // is not included in the shared string_table, see StringTable::copy_shared_string. @@ -271,11 +275,10 @@ // have a 'bad' reference in the archived resolved_reference // array. rr->obj_at_put(i, op); - } else { - rr->obj_at_put(i, NULL); } } } + oop archived = MetaspaceShared::archive_heap_object(rr, THREAD); _cache->set_archived_references(archived); set_resolved_references(NULL); @@ -346,6 +349,26 @@ // class redefinition. Since shared ConstantPools cannot be deallocated anyway, // we always set _on_stack to true to avoid having to change _flags during runtime. _flags |= (_on_stack | _is_shared); + int num_klasses = 0; + for (int index = 1; index < length(); index++) { // Index 0 is unused + assert(!tag_at(index).is_unresolved_klass_in_error(), "This must not happen during dump time"); + if (tag_at(index).is_klass()) { + // This class was resolved as a side effect of executing Java code + // during dump time. We need to restore it back to an UnresolvedClass, + // so that the proper class loading and initialization can happen + // at runtime. + CPKlassSlot kslot = klass_slot_at(index); + int resolved_klass_index = kslot.resolved_klass_index(); + int name_index = kslot.name_index(); + assert(tag_at(name_index).is_symbol(), "sanity"); + resolved_klasses()->at_put(resolved_klass_index, NULL); + tag_at_put(index, JVM_CONSTANT_UnresolvedClass); + assert(klass_name_at(index) == symbol_at(name_index), "sanity"); + } + } + if (cache() != NULL) { + cache()->remove_unshareable_info(); + } } int ConstantPool::cp_to_object_index(int cp_index) { diff -r e704f55561c3 -r a993ec29ec75 hotspot/src/share/vm/oops/cpCache.cpp --- a/hotspot/src/share/vm/oops/cpCache.cpp Wed Aug 30 19:18:22 2017 -0400 +++ b/hotspot/src/share/vm/oops/cpCache.cpp Mon Aug 28 15:34:04 2017 -0700 @@ -23,6 +23,8 @@ */ #include "precompiled.hpp" +#include "interpreter/bytecodeStream.hpp" +#include "interpreter/bytecodes.hpp" #include "interpreter/interpreter.hpp" #include "interpreter/rewriter.hpp" #include "logging/log.hpp" @@ -49,6 +51,24 @@ assert(constant_pool_index() == index, ""); } +void ConstantPoolCacheEntry::verify_just_initialized(bool f2_used) { + assert((_indices & (~cp_index_mask)) == 0, "sanity"); + assert(_f1 == NULL, "sanity"); + assert(_flags == 0, "sanity"); + if (!f2_used) { + assert(_f2 == 0, "sanity"); + } +} + +void ConstantPoolCacheEntry::reinitialize(bool f2_used) { + _indices &= cp_index_mask; + _f1 = NULL; + _flags = 0; + if (!f2_used) { + _f2 = 0; + } +} + int ConstantPoolCacheEntry::make_flags(TosState state, int option_bits, int field_index_or_method_params) { @@ -609,6 +629,66 @@ } } +void ConstantPoolCache::verify_just_initialized() { + DEBUG_ONLY(walk_entries_for_initialization(/*check_only = */ true)); +} + +void ConstantPoolCache::remove_unshareable_info() { + walk_entries_for_initialization(/*check_only = */ false); +} + +void ConstantPoolCache::walk_entries_for_initialization(bool check_only) { + assert(DumpSharedSpaces, "sanity"); + // When dumping the archive, we want to clean up the ConstantPoolCache + // to remove any effect of linking due to the execution of Java code -- + // each ConstantPoolCacheEntry will have the same contents as if + // ConstantPoolCache::initialize has just returned: + // + // - We keep the ConstantPoolCache::constant_pool_index() bits for all entries. + // - We keep the "f2" field for entries used by invokedynamic and invokehandle + // - All other bits in the entries are cleared to zero. + ResourceMark rm; + + InstanceKlass* ik = constant_pool()->pool_holder(); + bool* f2_used = NEW_RESOURCE_ARRAY(bool, length()); + memset(f2_used, 0, sizeof(bool) * length()); + + // Find all the slots that we need to preserve f2 + for (int i = 0; i < ik->methods()->length(); i++) { + Method* m = ik->methods()->at(i); + RawBytecodeStream bcs(m); + while (!bcs.is_last_bytecode()) { + Bytecodes::Code opcode = bcs.raw_next(); + switch (opcode) { + case Bytecodes::_invokedynamic: { + int index = Bytes::get_native_u4(bcs.bcp() + 1); + int cp_cache_index = constant_pool()->invokedynamic_cp_cache_index(index); + f2_used[cp_cache_index] = 1; + } + break; + case Bytecodes::_invokehandle: { + int cp_cache_index = Bytes::get_native_u2(bcs.bcp() + 1); + f2_used[cp_cache_index] = 1; + } + break; + default: + break; + } + } + } + + if (check_only) { + DEBUG_ONLY( + for (int i=0; iverify_just_initialized(f2_used[i]); + }) + } else { + for (int i=0; ireinitialize(f2_used[i]); + } + } +} + void ConstantPoolCache::deallocate_contents(ClassLoaderData* data) { assert(!is_shared(), "shared caches are not deallocated"); data->remove_handle(_resolved_references); diff -r e704f55561c3 -r a993ec29ec75 hotspot/src/share/vm/oops/cpCache.hpp --- a/hotspot/src/share/vm/oops/cpCache.hpp Wed Aug 30 19:18:22 2017 -0400 +++ b/hotspot/src/share/vm/oops/cpCache.hpp Mon Aug 28 15:34:04 2017 -0700 @@ -393,6 +393,9 @@ // When shifting flags as a 32-bit int, make sure we don't need an extra mask for tos_state: assert((((u4)-1 >> tos_state_shift) & ~tos_state_mask) == 0, "no need for tos_state mask"); } + + void verify_just_initialized(bool f2_used); + void reinitialize(bool f2_used); }; @@ -464,7 +467,11 @@ // Assembly code support static int resolved_references_offset_in_bytes() { return offset_of(ConstantPoolCache, _resolved_references); } + // CDS support + void remove_unshareable_info(); + void verify_just_initialized(); private: + void walk_entries_for_initialization(bool check_only); void set_length(int length) { _length = length; } static int header_size() { return sizeof(ConstantPoolCache) / wordSize; } diff -r e704f55561c3 -r a993ec29ec75 hotspot/src/share/vm/oops/instanceKlass.cpp --- a/hotspot/src/share/vm/oops/instanceKlass.cpp Wed Aug 30 19:18:22 2017 -0400 +++ b/hotspot/src/share/vm/oops/instanceKlass.cpp Mon Aug 28 15:34:04 2017 -0700 @@ -747,10 +747,10 @@ char* message = NEW_RESOURCE_ARRAY(char, msglen); if (NULL == message) { // Out of memory: can't create detailed error message - THROW_MSG(vmSymbols::java_lang_NoClassDefFoundError(), className); + THROW_MSG(vmSymbols::java_lang_NoClassDefFoundError(), className); } else { jio_snprintf(message, msglen, "%s%s", desc, className); - THROW_MSG(vmSymbols::java_lang_NoClassDefFoundError(), message); + THROW_MSG(vmSymbols::java_lang_NoClassDefFoundError(), message); } } @@ -2067,14 +2067,14 @@ m->remove_unshareable_info(); } + // do array classes also. + if (array_klasses() != NULL) { + array_klasses()->remove_unshareable_info(); + } + // These are not allocated from metaspace, but they should should all be empty - // during dump time, so we don't need to worry about them in InstanceKlass::metaspace_pointers_do(). + // during dump time, so we don't need to worry about them in InstanceKlass::iterate(). guarantee(_source_debug_extension == NULL, "must be"); - guarantee(_oop_map_cache == NULL, "must be"); - guarantee(_init_thread == NULL, "must be"); - guarantee(_oop_map_cache == NULL, "must be"); - guarantee(_jni_ids == NULL, "must be"); - guarantee(_methods_jmethod_ids == NULL, "must be"); guarantee(_dep_context == DependencyContext::EMPTY, "must be"); guarantee(_osr_nmethods_head == NULL, "must be"); @@ -2082,12 +2082,20 @@ guarantee(_breakpoints == NULL, "must be"); guarantee(_previous_versions == NULL, "must be"); #endif + + _init_thread = NULL; + _methods_jmethod_ids = NULL; + _jni_ids = NULL; + _oop_map_cache = NULL; } -static void restore_unshareable_in_class(Klass* k, TRAPS) { - // Array classes have null protection domain. - // --> see ArrayKlass::complete_create_array_klass() - k->restore_unshareable_info(ClassLoaderData::the_null_class_loader_data(), Handle(), CHECK); +void InstanceKlass::remove_java_mirror() { + Klass::remove_java_mirror(); + + // do array classes also. + if (array_klasses() != NULL) { + array_klasses()->remove_java_mirror(); + } } void InstanceKlass::restore_unshareable_info(ClassLoaderData* loader_data, Handle protection_domain, TRAPS) { @@ -2114,7 +2122,11 @@ // restore constant pool resolved references constants()->restore_unshareable_info(CHECK); - array_klasses_do(restore_unshareable_in_class, CHECK); + if (array_klasses() != NULL) { + // Array classes have null protection domain. + // --> see ArrayKlass::complete_create_array_klass() + array_klasses()->restore_unshareable_info(ClassLoaderData::the_null_class_loader_data(), Handle(), CHECK); + } } // returns true IFF is_in_error_state() has been changed as a result of this call. diff -r e704f55561c3 -r a993ec29ec75 hotspot/src/share/vm/oops/instanceKlass.hpp --- a/hotspot/src/share/vm/oops/instanceKlass.hpp Wed Aug 30 19:18:22 2017 -0400 +++ b/hotspot/src/share/vm/oops/instanceKlass.hpp Mon Aug 28 15:34:04 2017 -0700 @@ -327,8 +327,6 @@ } void set_class_loader_type(s2 loader_type) { - assert(( _misc_flags & loader_type_bits()) == 0, - "Should only be called once for each class."); switch (loader_type) { case ClassLoader::BOOT_LOADER: _misc_flags |= _misc_is_shared_boot_class; @@ -1335,6 +1333,7 @@ public: // CDS support - remove and restore oops from metadata. Oops are not shared. virtual void remove_unshareable_info(); + virtual void remove_java_mirror(); virtual void restore_unshareable_info(ClassLoaderData* loader_data, Handle protection_domain, TRAPS); // jvm support diff -r e704f55561c3 -r a993ec29ec75 hotspot/src/share/vm/oops/klass.cpp --- a/hotspot/src/share/vm/oops/klass.cpp Wed Aug 30 19:18:22 2017 -0400 +++ b/hotspot/src/share/vm/oops/klass.cpp Mon Aug 28 15:34:04 2017 -0700 @@ -512,11 +512,13 @@ void Klass::remove_unshareable_info() { assert (DumpSharedSpaces, "only called for DumpSharedSpaces"); TRACE_REMOVE_ID(this); + if (log_is_enabled(Trace, cds, unshareable)) { + ResourceMark rm; + log_trace(cds, unshareable)("remove: %s", external_name()); + } set_subklass(NULL); set_next_sibling(NULL); - // Clear the java mirror - set_java_mirror(NULL); set_next_link(NULL); // Null out class_loader_data because we don't share that yet. @@ -524,10 +526,23 @@ set_is_shared(); } +void Klass::remove_java_mirror() { + assert (DumpSharedSpaces, "only called for DumpSharedSpaces"); + if (log_is_enabled(Trace, cds, unshareable)) { + ResourceMark rm; + log_trace(cds, unshareable)("remove java_mirror: %s", external_name()); + } + set_java_mirror(NULL); +} + void Klass::restore_unshareable_info(ClassLoaderData* loader_data, Handle protection_domain, TRAPS) { assert(is_klass(), "ensure C++ vtable is restored"); assert(is_shared(), "must be set"); TRACE_RESTORE_ID(this); + if (log_is_enabled(Trace, cds, unshareable)) { + ResourceMark rm; + log_trace(cds, unshareable)("restore: %s", external_name()); + } // If an exception happened during CDS restore, some of these fields may already be // set. We leave the class on the CLD list, even if incomplete so that we don't diff -r e704f55561c3 -r a993ec29ec75 hotspot/src/share/vm/oops/klass.hpp --- a/hotspot/src/share/vm/oops/klass.hpp Wed Aug 30 19:18:22 2017 -0400 +++ b/hotspot/src/share/vm/oops/klass.hpp Mon Aug 28 15:34:04 2017 -0700 @@ -479,6 +479,7 @@ // CDS support - remove and restore oops from metadata. Oops are not shared. virtual void remove_unshareable_info(); + virtual void remove_java_mirror(); virtual void restore_unshareable_info(ClassLoaderData* loader_data, Handle protection_domain, TRAPS); protected: diff -r e704f55561c3 -r a993ec29ec75 hotspot/src/share/vm/oops/method.cpp --- a/hotspot/src/share/vm/oops/method.cpp Wed Aug 30 19:18:22 2017 -0400 +++ b/hotspot/src/share/vm/oops/method.cpp Mon Aug 28 15:34:04 2017 -0700 @@ -944,10 +944,6 @@ _from_compiled_entry = cds_adapter->get_c2i_entry_trampoline(); assert(*((int*)_from_compiled_entry) == 0, "must be NULL during dump time, to be initialized at run time"); - - // In case of DumpSharedSpaces, _method_data should always be NULL. - assert(_method_data == NULL, "unexpected method data?"); - set_method_data(NULL); clear_method_counters(); } diff -r e704f55561c3 -r a993ec29ec75 hotspot/src/share/vm/runtime/arguments.cpp --- a/hotspot/src/share/vm/runtime/arguments.cpp Wed Aug 30 19:18:22 2017 -0400 +++ b/hotspot/src/share/vm/runtime/arguments.cpp Mon Aug 28 15:34:04 2017 -0700 @@ -1291,13 +1291,11 @@ "jdk.module.limitmods", "jdk.module.path", "jdk.module.upgrade.path", - "jdk.module.addmods.0", "jdk.module.patch.0" }; const char* unsupported_options[] = { "-m", // cannot use at dump time "--limit-modules", // ignored at dump time "--module-path", // ignored at dump time "--upgrade-module-path", // ignored at dump time - "--add-modules", // ignored at dump time "--patch-module" // ignored at dump time }; assert(ARRAY_SIZE(unsupported_properties) == ARRAY_SIZE(unsupported_options), "must be"); @@ -2667,17 +2665,11 @@ } // Do final processing now that all arguments have been parsed - result = finalize_vm_init_args(); + result = finalize_vm_init_args(patch_mod_javabase); if (result != JNI_OK) { return result; } -#if INCLUDE_CDS - if (UseSharedSpaces && patch_mod_javabase) { - no_shared_spaces("CDS is disabled when " JAVA_BASE_NAME " module is patched."); - } -#endif - return JNI_OK; } @@ -3602,7 +3594,7 @@ return nonEmptyDirs; } -jint Arguments::finalize_vm_init_args() { +jint Arguments::finalize_vm_init_args(bool patch_mod_javabase) { // check if the default lib/endorsed directory exists; if so, error char path[JVM_MAXPATHLEN]; const char* fileSep = os::file_separator(); @@ -3723,6 +3715,17 @@ } #endif +#if INCLUDE_CDS + if (DumpSharedSpaces) { + // Disable biased locking now as it interferes with the clean up of + // the archived Klasses and Java string objects (at dump time only). + UseBiasedLocking = false; + } + if (UseSharedSpaces && patch_mod_javabase) { + no_shared_spaces("CDS is disabled when " JAVA_BASE_NAME " module is patched."); + } +#endif + return JNI_OK; } diff -r e704f55561c3 -r a993ec29ec75 hotspot/src/share/vm/runtime/arguments.hpp --- a/hotspot/src/share/vm/runtime/arguments.hpp Wed Aug 30 19:18:22 2017 -0400 +++ b/hotspot/src/share/vm/runtime/arguments.hpp Mon Aug 28 15:34:04 2017 -0700 @@ -536,7 +536,7 @@ const JavaVMInitArgs *java_options_args, const JavaVMInitArgs *cmd_line_args); static jint parse_each_vm_init_arg(const JavaVMInitArgs* args, bool* patch_mod_javabase, Flag::Flags origin); - static jint finalize_vm_init_args(); + static jint finalize_vm_init_args(bool patch_mod_javabase); static bool is_bad_option(const JavaVMOption* option, jboolean ignore, const char* option_type); static bool is_bad_option(const JavaVMOption* option, jboolean ignore) { diff -r e704f55561c3 -r a993ec29ec75 hotspot/src/share/vm/runtime/arguments_ext.hpp --- a/hotspot/src/share/vm/runtime/arguments_ext.hpp Wed Aug 30 19:18:22 2017 -0400 +++ b/hotspot/src/share/vm/runtime/arguments_ext.hpp Mon Aug 28 15:34:04 2017 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2017, 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 @@ -36,6 +36,7 @@ // Otherwise returns false. static inline bool process_options(const JavaVMOption *option) { return false; } static inline void report_unsupported_options() { } + static inline bool using_AppCDS() { return false; } }; void ArgumentsExt::set_gc_specific_flags() { diff -r e704f55561c3 -r a993ec29ec75 hotspot/src/share/vm/runtime/javaCalls.cpp --- a/hotspot/src/share/vm/runtime/javaCalls.cpp Wed Aug 30 19:18:22 2017 -0400 +++ b/hotspot/src/share/vm/runtime/javaCalls.cpp Mon Aug 28 15:34:04 2017 -0700 @@ -308,9 +308,6 @@ } void JavaCalls::call_helper(JavaValue* result, const methodHandle& method, JavaCallArguments* args, TRAPS) { - // During dumping, Java execution environment is not fully initialized. Also, Java execution - // may cause undesirable side-effects in the class metadata. - assert(!DumpSharedSpaces, "must not execute Java bytecodes when dumping"); JavaThread* thread = (JavaThread*)THREAD; assert(thread->is_Java_thread(), "must be called by a java thread"); diff -r e704f55561c3 -r a993ec29ec75 hotspot/src/share/vm/runtime/thread.cpp --- a/hotspot/src/share/vm/runtime/thread.cpp Wed Aug 30 19:18:22 2017 -0400 +++ b/hotspot/src/share/vm/runtime/thread.cpp Mon Aug 28 15:34:04 2017 -0700 @@ -3717,14 +3717,6 @@ Thread* THREAD = Thread::current(); - // At this point, the Universe is initialized, but we have not executed - // any byte code. Now is a good time (the only time) to dump out the - // internal state of the JVM for sharing. - if (DumpSharedSpaces) { - MetaspaceShared::preload_and_dump(CHECK_JNI_ERR); - ShouldNotReachHere(); - } - // Always call even when there are not JVMTI environments yet, since environments // may be attached late and JVMTI must track phases of VM execution JvmtiExport::enter_early_start_phase(); @@ -3887,6 +3879,12 @@ #ifdef ASSERT _vm_complete = true; #endif + + if (DumpSharedSpaces) { + MetaspaceShared::preload_and_dump(CHECK_JNI_ERR); + ShouldNotReachHere(); + } + return JNI_OK; } diff -r e704f55561c3 -r a993ec29ec75 hotspot/src/share/vm/utilities/exceptions.cpp --- a/hotspot/src/share/vm/utilities/exceptions.cpp Wed Aug 30 19:18:22 2017 -0400 +++ b/hotspot/src/share/vm/utilities/exceptions.cpp Mon Aug 28 15:34:04 2017 -0700 @@ -87,13 +87,9 @@ #endif // ASSERT if (thread->is_VM_thread() - || !thread->can_call_java() - || DumpSharedSpaces ) { + || !thread->can_call_java()) { // We do not care what kind of exception we get for the vm-thread or a thread which // is compiling. We just install a dummy exception object - // - // We also cannot throw a proper exception when dumping, because we cannot run - // Java bytecodes now. A dummy exception will suffice. thread->set_pending_exception(Universe::vm_exception(), file, line); return true; } @@ -114,13 +110,9 @@ } if (thread->is_VM_thread() - || !thread->can_call_java() - || DumpSharedSpaces ) { + || !thread->can_call_java()) { // We do not care what kind of exception we get for the vm-thread or a thread which // is compiling. We just install a dummy exception object - // - // We also cannot throw a proper exception when dumping, because we cannot run - // Java bytecodes now. A dummy exception will suffice. thread->set_pending_exception(Universe::vm_exception(), file, line); return true; } diff -r e704f55561c3 -r a993ec29ec75 hotspot/test/runtime/SharedArchiveFile/BootAppendTests.java --- a/hotspot/test/runtime/SharedArchiveFile/BootAppendTests.java Wed Aug 30 19:18:22 2017 -0400 +++ b/hotspot/test/runtime/SharedArchiveFile/BootAppendTests.java Mon Aug 28 15:34:04 2017 -0700 @@ -205,7 +205,11 @@ OutputAnalyzer out = CDSTestUtils.runWithArchive(opts); CDSTestUtils.checkExec(out, opts, "[class,load] org.omg.CORBA.Context"); if (!CDSTestUtils.isUnableToMap(out)) { - out.shouldMatch(".*\\[class,load\\] org.omg.CORBA.Context source:.*bootAppend.jar"); + if (mode.equals("off")) { + out.shouldMatch(".*\\[class,load\\] org.omg.CORBA.Context source:.*bootAppend.jar"); + } else { + CDSTestUtils.checkExec(out, opts, "[class,load] org.omg.CORBA.Context source: shared objects file"); + } } } } diff -r e704f55561c3 -r a993ec29ec75 hotspot/test/runtime/SharedArchiveFile/NonBootLoaderClasses.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/runtime/SharedArchiveFile/NonBootLoaderClasses.java Mon Aug 28 15:34:04 2017 -0700 @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2017, 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 NonBootLoaderClasses + * @summary Test to ensure platform and app classes are not being archived + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @library /test/lib + * @modules java.base/jdk.internal.misc + * java.management + * @run main NonBootLoaderClasses + */ + +import jdk.test.lib.cds.CDSOptions; +import jdk.test.lib.cds.CDSTestUtils; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; +import java.io.File; + +public class NonBootLoaderClasses { + public static void main(String[] args) throws Exception { + final String PLATFORM_CLASS = "jdk/dynalink/DynamicLinker"; + final String APP_CLASS = "com/sun/tools/javac/Main"; + String[] classes = {PLATFORM_CLASS, APP_CLASS}; + String classList = + CDSTestUtils.makeClassList(classes).getPath(); + String archiveName = "NonBootLoaderClasses.jsa"; + CDSOptions opts = (new CDSOptions()) + .addPrefix("-XX:ExtraSharedClassListFile=" + classList) + .setArchiveName(archiveName); + CDSTestUtils.createArchiveAndCheck(opts); + + // Print the shared dictionary and inspect the output + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder( + "-XX:+UnlockDiagnosticVMOptions", "-XX:SharedArchiveFile=./" + archiveName, + "-XX:+PrintSharedArchiveAndExit", "-XX:+PrintSharedDictionary"); + OutputAnalyzer out = CDSTestUtils.executeAndLog(pb, "print-shared-archive"); + if (!CDSTestUtils.isUnableToMap(out)) { + out.shouldContain("archive is valid") + .shouldHaveExitValue(0) // Should report success in error code. + .shouldNotContain(PLATFORM_CLASS) + .shouldNotContain(APP_CLASS); + } + } +} diff -r e704f55561c3 -r a993ec29ec75 hotspot/test/runtime/modules/PatchModule/PatchModuleCDS.java --- a/hotspot/test/runtime/modules/PatchModule/PatchModuleCDS.java Wed Aug 30 19:18:22 2017 -0400 +++ b/hotspot/test/runtime/modules/PatchModule/PatchModuleCDS.java Mon Aug 28 15:34:04 2017 -0700 @@ -69,7 +69,7 @@ "-XX:+UnlockDiagnosticVMOptions", "-XX:SharedArchiveFile=" + filename, "-Xshare:dump", - "--patch-module=java.base=" + System.getProperty("test.classes"), + "--patch-module=java.naming=" + System.getProperty("test.classes"), "-Xlog:class+path=info", "-version"); new OutputAnalyzer(pb.start())