mirror of
https://github.com/openjdk/jdk.git
synced 2026-02-03 06:58:23 +00:00
8186842: Use Java class loaders for creating the CDS archive
Co-authored-by: Ioi Lam <ioi.lam@oracle.com> Reviewed-by: coleenp, jiangli, iklam, mseledtsov
This commit is contained in:
parent
de3cc93ca6
commit
cf223c0791
@ -2928,7 +2928,7 @@ void TemplateTable::invokevirtual(int byte_no) {
|
||||
__ 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);
|
||||
}
|
||||
|
||||
|
||||
@ -147,6 +147,7 @@ ClassPathEntry* ClassLoader::_jrt_entry = NULL;
|
||||
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<char*>* ClassLoader::_boot_modules_array = NULL;
|
||||
GrowableArray<char*>* ClassLoader::_platform_modules_array = NULL;
|
||||
@ -242,7 +243,7 @@ const char* ClassLoader::package_from_name(const char* const class_name, bool* b
|
||||
|
||||
// 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 @@ ClassFileStream* ClassPathImageEntry::open_stream(const char* name, TRAPS) {
|
||||
#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 @@ ClassFileStream* ClassPathImageEntry::open_stream(const char* name, TRAPS) {
|
||||
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 @@ InstanceKlass* ClassLoader::load_class(Symbol* name, bool search_append_only, TR
|
||||
// This would include:
|
||||
// [--patch-module=<module>=<file>(<pathsep><file>)*]; [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 @@ InstanceKlass* ClassLoader::load_class(Symbol* name, bool search_append_only, TR
|
||||
|
||||
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 @@ InstanceKlass* ClassLoader::load_class(Symbol* name, bool search_append_only, TR
|
||||
}
|
||||
|
||||
if (NULL == stream) {
|
||||
if (DumpSharedSpaces) {
|
||||
tty->print_cr("Preload Warning: Cannot find %s", class_name);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -1548,6 +1555,100 @@ InstanceKlass* ClassLoader::load_class(Symbol* name, bool search_append_only, TR
|
||||
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 @@ void ClassLoader::initialize() {
|
||||
#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()
|
||||
}
|
||||
|
||||
@ -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<mtClass> {
|
||||
private:
|
||||
@ -103,7 +105,6 @@ typedef struct {
|
||||
jlong pos; /* position of LOC header (if negative) or data */
|
||||
} jzentry;
|
||||
|
||||
|
||||
class ClassPathZipEntry: public ClassPathEntry {
|
||||
enum {
|
||||
_unknown = 0,
|
||||
@ -249,6 +250,10 @@ class ClassLoader: AllStatic {
|
||||
// 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<char*>* _boot_modules_array;)
|
||||
|
||||
@ -289,6 +294,7 @@ class ClassLoader: AllStatic {
|
||||
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 @@ class ClassLoader: AllStatic {
|
||||
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);
|
||||
|
||||
|
||||
@ -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 @@ public:
|
||||
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 @@ public:
|
||||
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
|
||||
|
||||
@ -366,11 +366,21 @@ void Dictionary::reorder_dictionary_for_sharing() {
|
||||
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);
|
||||
}
|
||||
|
||||
@ -49,21 +49,6 @@ class Dictionary : public Hashtable<InstanceKlass*, mtClass> {
|
||||
DictionaryEntry* get_entry(int index, unsigned int hash, Symbol* name);
|
||||
|
||||
protected:
|
||||
DictionaryEntry* bucket(int i) const {
|
||||
return (DictionaryEntry*)Hashtable<InstanceKlass*, mtClass>::bucket(i);
|
||||
}
|
||||
|
||||
// The following method is not MT-safe and must be done under lock.
|
||||
DictionaryEntry** bucket_addr(int i) {
|
||||
return (DictionaryEntry**)Hashtable<InstanceKlass*, mtClass>::bucket_addr(i);
|
||||
}
|
||||
|
||||
void add_entry(int index, DictionaryEntry* new_entry) {
|
||||
Hashtable<InstanceKlass*, mtClass>::add_entry(index, (HashtableEntry<InstanceKlass*, mtClass>*)new_entry);
|
||||
}
|
||||
|
||||
void free_entry(DictionaryEntry* entry);
|
||||
|
||||
static size_t entry_size();
|
||||
public:
|
||||
Dictionary(ClassLoaderData* loader_data, int table_size);
|
||||
@ -107,6 +92,24 @@ public:
|
||||
|
||||
void print_on(outputStream* st) const;
|
||||
void verify();
|
||||
DictionaryEntry* bucket(int i) const {
|
||||
return (DictionaryEntry*)Hashtable<InstanceKlass*, mtClass>::bucket(i);
|
||||
}
|
||||
|
||||
// The following method is not MT-safe and must be done under lock.
|
||||
DictionaryEntry** bucket_addr(int i) {
|
||||
return (DictionaryEntry**)Hashtable<InstanceKlass*, mtClass>::bucket_addr(i);
|
||||
}
|
||||
|
||||
void add_entry(int index, DictionaryEntry* new_entry) {
|
||||
Hashtable<InstanceKlass*, mtClass>::add_entry(index, (HashtableEntry<InstanceKlass*, mtClass>*)new_entry);
|
||||
}
|
||||
|
||||
void unlink_entry(DictionaryEntry* entry) {
|
||||
Hashtable<InstanceKlass*, mtClass>::unlink_entry((HashtableEntry<InstanceKlass*, mtClass>*)entry);
|
||||
}
|
||||
|
||||
void free_entry(DictionaryEntry* entry);
|
||||
};
|
||||
|
||||
// An entry in the class loader data dictionaries, this describes a class as
|
||||
@ -254,10 +257,6 @@ class SymbolPropertyEntry : public HashtableEntry<Symbol*, mtSymbol> {
|
||||
class SymbolPropertyTable : public Hashtable<Symbol*, mtSymbol> {
|
||||
friend class VMStructs;
|
||||
private:
|
||||
SymbolPropertyEntry* bucket(int i) {
|
||||
return (SymbolPropertyEntry*) Hashtable<Symbol*, mtSymbol>::bucket(i);
|
||||
}
|
||||
|
||||
// The following method is not MT-safe and must be done under lock.
|
||||
SymbolPropertyEntry** bucket_addr(int i) {
|
||||
return (SymbolPropertyEntry**) Hashtable<Symbol*, mtSymbol>::bucket_addr(i);
|
||||
@ -311,5 +310,9 @@ public:
|
||||
void methods_do(void f(Method*));
|
||||
|
||||
void verify();
|
||||
|
||||
SymbolPropertyEntry* bucket(int i) {
|
||||
return (SymbolPropertyEntry*) Hashtable<Symbol*, mtSymbol>::bucket(i);
|
||||
}
|
||||
};
|
||||
#endif // SHARE_VM_CLASSFILE_DICTIONARY_HPP
|
||||
|
||||
@ -70,11 +70,25 @@ InstanceKlass* KlassFactory::check_shared_class_file_load_hook(
|
||||
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 @@ InstanceKlass* KlassFactory::create_from_stream(ClassFileStream* stream,
|
||||
|
||||
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 @@ InstanceKlass* KlassFactory::create_from_stream(ClassFileStream* stream,
|
||||
p->length = len;
|
||||
memcpy(p->data, bytes, len);
|
||||
result->set_archived_class_data(p);
|
||||
#endif // INCLUDE_JVMTI
|
||||
}
|
||||
#endif
|
||||
#endif // INCLUDE_CDS
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -729,7 +729,6 @@ bool StringTable::copy_shared_string(GrowableArray<MemRegion> *string_space,
|
||||
}
|
||||
|
||||
G1CollectedHeap::heap()->end_archive_alloc_range(string_space, os::vm_allocation_granularity());
|
||||
assert(string_space->length() <= 2, "sanity");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@ -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 @@ Klass* SystemDictionary::resolve_instance_class_or_null(Symbol* name,
|
||||
// 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 @@ InstanceKlass* SystemDictionary::parse_stream(Symbol* class_name,
|
||||
// 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 @@ InstanceKlass* SystemDictionary::resolve_from_stream(Symbol* class_name,
|
||||
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* SystemDictionary::resolve_from_stream(Symbol* class_name,
|
||||
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 @@ bool SystemDictionary::is_shared_class_visible(Symbol* class_name,
|
||||
"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 @@ bool SystemDictionary::is_shared_class_visible(Symbol* class_name,
|
||||
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 @@ InstanceKlass* SystemDictionary::load_shared_class(InstanceKlass* ik,
|
||||
}
|
||||
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 @@ InstanceKlass* SystemDictionary::load_instance_class(Symbol* class_name, Handle
|
||||
}
|
||||
}
|
||||
} 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 @@ void SystemDictionary::methods_do(void f(Method*)) {
|
||||
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 @@ int SystemDictionaryDCmd::num_arguments() {
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
@ -385,6 +385,7 @@ public:
|
||||
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 @@ public:
|
||||
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);
|
||||
|
||||
@ -109,6 +109,11 @@ void Rewriter::make_constant_pool_cache(TRAPS) {
|
||||
MetadataFactory::free_metadata(loader_data, cache);
|
||||
_pool->set_cache(NULL); // so the verifier isn't confused
|
||||
}
|
||||
|
||||
DEBUG_ONLY(
|
||||
if (DumpSharedSpaces) {
|
||||
cache->verify_just_initialized();
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -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) \
|
||||
|
||||
@ -275,7 +275,8 @@ private:
|
||||
address, bool,
|
||||
UniqueMetaspaceClosure::my_hash, // solaris compiler doesn't like: primitive_hash<address>
|
||||
UniqueMetaspaceClosure::my_equals, // solaris compiler doesn't like: primitive_equals<address>
|
||||
16384> _has_been_visited;
|
||||
15889, // prime number
|
||||
ResourceObj::C_HEAP> _has_been_visited;
|
||||
};
|
||||
|
||||
#endif // SHARE_VM_MEMORY_METASPACE_ITERATOR_HPP
|
||||
|
||||
@ -374,25 +374,63 @@ address MetaspaceShared::cds_i2i_entry_code_buffers(size_t total_size) {
|
||||
// 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<Klass*>* _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 @@ static void relocate_cached_class_file() {
|
||||
}
|
||||
}
|
||||
|
||||
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 <Type>::_vptr, i.e., first word in the object.)
|
||||
//
|
||||
@ -957,8 +1006,8 @@ public:
|
||||
}
|
||||
memcpy(p, obj, bytes);
|
||||
bool isnew = _new_loc_table->put(obj, (address)p);
|
||||
assert(isnew, "must be");
|
||||
log_trace(cds)("Copy: " PTR_FORMAT " ==> " PTR_FORMAT " %d", p2i(obj), p2i(p), bytes);
|
||||
assert(isnew, "must be");
|
||||
|
||||
_alloc_stats->record(ref->msotype(), int(newtop - oldtop), read_only);
|
||||
if (ref->msotype() == MetaspaceObj::SymbolType) {
|
||||
@ -1151,6 +1200,9 @@ char* VM_PopulateDumpSharedSpace::dump_read_only_tables() {
|
||||
// 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 @@ void VM_PopulateDumpSharedSpace::doit() {
|
||||
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 @@ void VM_PopulateDumpSharedSpace::doit() {
|
||||
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 @@ void MetaspaceShared::link_and_cleanup_shared_classes(TRAPS) {
|
||||
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 @@ void MetaspaceShared::preload_and_dump(TRAPS) {
|
||||
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 @@ int MetaspaceShared::preload_classes(const char* class_list_path, TRAPS) {
|
||||
|
||||
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;
|
||||
|
||||
@ -187,12 +187,29 @@ void ArrayKlass::metaspace_pointers_do(MetaspaceClosure* it) {
|
||||
|
||||
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
|
||||
|
||||
@ -130,6 +130,7 @@ class ArrayKlass: public Klass {
|
||||
|
||||
// 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
|
||||
|
||||
@ -257,10 +257,14 @@ void ConstantPool::archive_resolved_references(Thread* THREAD) {
|
||||
}
|
||||
|
||||
objArrayOop rr = resolved_references();
|
||||
Array<u2>* 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 @@ void ConstantPool::archive_resolved_references(Thread* THREAD) {
|
||||
// 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 @@ void ConstantPool::remove_unshareable_info() {
|
||||
// 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) {
|
||||
|
||||
@ -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 @@ void ConstantPoolCacheEntry::initialize_entry(int index) {
|
||||
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::initialize(const intArray& inverse_index_map,
|
||||
}
|
||||
}
|
||||
|
||||
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; i<length(); i++) {
|
||||
entry_at(i)->verify_just_initialized(f2_used[i]);
|
||||
})
|
||||
} else {
|
||||
for (int i=0; i<length(); i++) {
|
||||
entry_at(i)->reinitialize(f2_used[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ConstantPoolCache::deallocate_contents(ClassLoaderData* data) {
|
||||
assert(!is_shared(), "shared caches are not deallocated");
|
||||
data->remove_handle(_resolved_references);
|
||||
|
||||
@ -393,6 +393,9 @@ class ConstantPoolCacheEntry VALUE_OBJ_CLASS_SPEC {
|
||||
// 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 @@ class ConstantPoolCache: public MetaspaceObj {
|
||||
// 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; }
|
||||
|
||||
@ -747,10 +747,10 @@ void InstanceKlass::initialize_impl(TRAPS) {
|
||||
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 @@ void InstanceKlass::remove_unshareable_info() {
|
||||
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 @@ void InstanceKlass::remove_unshareable_info() {
|
||||
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 @@ void InstanceKlass::restore_unshareable_info(ClassLoaderData* loader_data, Handl
|
||||
// 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.
|
||||
|
||||
@ -327,8 +327,6 @@ class InstanceKlass: public Klass {
|
||||
}
|
||||
|
||||
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 @@ private:
|
||||
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
|
||||
|
||||
@ -512,11 +512,13 @@ void Klass::metaspace_pointers_do(MetaspaceClosure* it) {
|
||||
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 @@ void Klass::remove_unshareable_info() {
|
||||
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
|
||||
|
||||
@ -479,6 +479,7 @@ protected:
|
||||
|
||||
// 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:
|
||||
|
||||
@ -944,10 +944,6 @@ void Method::unlink_method() {
|
||||
_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();
|
||||
}
|
||||
|
||||
@ -1291,13 +1291,11 @@ void Arguments::check_unsupported_dumping_properties() {
|
||||
"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 @@ jint Arguments::parse_vm_init_args(const JavaVMInitArgs *java_tool_options_args,
|
||||
}
|
||||
|
||||
// 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 @@ static int check_non_empty_dirs(const char* path) {
|
||||
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 @@ jint Arguments::finalize_vm_init_args() {
|
||||
}
|
||||
#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;
|
||||
}
|
||||
|
||||
|
||||
@ -536,7 +536,7 @@ class Arguments : AllStatic {
|
||||
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) {
|
||||
|
||||
@ -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 @@ public:
|
||||
// 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() {
|
||||
|
||||
@ -308,9 +308,6 @@ void JavaCalls::call(JavaValue* result, const methodHandle& method, JavaCallArgu
|
||||
}
|
||||
|
||||
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");
|
||||
|
||||
@ -3717,14 +3717,6 @@ jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) {
|
||||
|
||||
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 @@ jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) {
|
||||
#ifdef ASSERT
|
||||
_vm_complete = true;
|
||||
#endif
|
||||
|
||||
if (DumpSharedSpaces) {
|
||||
MetaspaceShared::preload_and_dump(CHECK_JNI_ERR);
|
||||
ShouldNotReachHere();
|
||||
}
|
||||
|
||||
return JNI_OK;
|
||||
}
|
||||
|
||||
|
||||
@ -87,13 +87,9 @@ bool Exceptions::special_exception(Thread* thread, const char* file, int line, H
|
||||
#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 @@ bool Exceptions::special_exception(Thread* thread, const char* file, int line, S
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@ -205,7 +205,11 @@ public class BootAppendTests {
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -69,7 +69,7 @@ public class PatchModuleCDS {
|
||||
"-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())
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user