8261941: Use ClassLoader for unregistered classes during -Xshare:dump

Reviewed-by: iklam, minqi
This commit is contained in:
Calvin Cheung 2021-09-16 17:32:02 +00:00
parent 7e92abe7a4
commit 12fa7073c5
10 changed files with 171 additions and 116 deletions

@ -29,6 +29,7 @@
#include "cds/classListParser.hpp"
#include "cds/lambdaFormInvokers.hpp"
#include "cds/metaspaceShared.hpp"
#include "cds/unregisteredClasses.hpp"
#include "classfile/classLoaderExt.hpp"
#include "classfile/javaClasses.inline.hpp"
#include "classfile/symbolTable.hpp"
@ -120,6 +121,13 @@ int ClassListParser::parse(TRAPS) {
return 0; // THROW
}
ResourceMark rm(THREAD);
char* ex_msg = (char*)"";
oop message = java_lang_Throwable::message(PENDING_EXCEPTION);
if (message != NULL) {
ex_msg = java_lang_String::as_utf8_string(message);
}
log_warning(cds)("%s: %s", PENDING_EXCEPTION->klass()->external_name(), ex_msg);
// We might have an invalid class name or an bad class. Warn about it
// and keep going to the next line.
CLEAR_PENDING_EXCEPTION;
@ -458,7 +466,7 @@ InstanceKlass* ClassListParser::load_class_from_source(Symbol* class_name, TRAPS
THROW_NULL(vmSymbols::java_lang_ClassNotFoundException());
}
InstanceKlass* k = ClassLoaderExt::load_class(class_name, _source, CHECK_NULL);
InstanceKlass* k = UnregisteredClasses::load_class(class_name, _source, CHECK_NULL);
if (k->local_interfaces()->length() != _interfaces->length()) {
print_specified_interfaces();
print_actual_interfaces(k);
@ -466,16 +474,14 @@ InstanceKlass* ClassListParser::load_class_from_source(Symbol* class_name, TRAPS
_interfaces->length(), k->local_interfaces()->length());
}
bool added = SystemDictionaryShared::add_unregistered_class_for_static_archive(THREAD, k);
assert(k->is_shared_unregistered_class(), "must be");
bool added = SystemDictionaryShared::add_unregistered_class(THREAD, k);
if (!added) {
// We allow only a single unregistered class for each unique name.
error("Duplicated class %s", _class_name);
}
// This tells JVM_FindLoadedClass to not find this class.
k->set_shared_classpath_index(UNREGISTERED_INDEX);
k->clear_shared_class_loader_type();
return k;
}

@ -0,0 +1,115 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
#include "precompiled.hpp"
#include "cds/unregisteredClasses.hpp"
#include "classfile/classFileStream.hpp"
#include "classfile/classLoader.inline.hpp"
#include "classfile/classLoaderExt.hpp"
#include "classfile/javaClasses.inline.hpp"
#include "classfile/symbolTable.hpp"
#include "classfile/systemDictionaryShared.hpp"
#include "classfile/vmSymbols.hpp"
#include "memory/oopFactory.hpp"
#include "memory/resourceArea.hpp"
#include "oops/instanceKlass.hpp"
#include "runtime/handles.hpp"
#include "runtime/javaCalls.hpp"
#include "services/threadService.hpp"
// Load the class of the given name from the location given by path. The path is specified by
// the "source:" in the class list file (see classListParser.cpp), and can be a directory or
// a JAR file.
InstanceKlass* UnregisteredClasses::load_class(Symbol* name, const char* path, TRAPS) {
assert(name != NULL, "invariant");
assert(DumpSharedSpaces, "this function is only used with -Xshare:dump");
{
PerfClassTraceTime vmtimer(ClassLoader::perf_sys_class_lookup_time(),
THREAD->get_thread_stat()->perf_timers_addr(),
PerfClassTraceTime::CLASS_LOAD);
}
Symbol* path_symbol = SymbolTable::new_symbol(path);
Handle url_classloader = get_url_classloader(path_symbol, CHECK_NULL);
Handle ext_class_name = java_lang_String::externalize_classname(name, CHECK_NULL);
JavaValue result(T_OBJECT);
JavaCallArguments args(2);
args.set_receiver(url_classloader);
args.push_oop(ext_class_name);
args.push_int(JNI_FALSE);
JavaCalls::call_virtual(&result,
vmClasses::URLClassLoader_klass(),
vmSymbols::loadClass_name(),
vmSymbols::string_boolean_class_signature(),
&args,
CHECK_NULL);
assert(result.get_type() == T_OBJECT, "just checking");
oop obj = result.get_oop();
return InstanceKlass::cast(java_lang_Class::as_Klass(obj));
}
class URLClassLoaderTable : public ResourceHashtable<
Symbol*, Handle,
137, // prime number
ResourceObj::C_HEAP> {};
static URLClassLoaderTable* _url_classloader_table = NULL;
Handle UnregisteredClasses::create_url_classloader(Symbol* path, TRAPS) {
ResourceMark rm(THREAD);
JavaValue result(T_OBJECT);
Handle path_string = java_lang_String::create_from_str(path->as_C_string(), CHECK_NH);
JavaCalls::call_static(&result,
vmClasses::jdk_internal_loader_ClassLoaders_klass(),
vmSymbols::toFileURL_name(),
vmSymbols::toFileURL_signature(),
path_string, CHECK_NH);
assert(result.get_type() == T_OBJECT, "just checking");
oop url_h = result.get_oop();
objArrayHandle urls = oopFactory::new_objArray_handle(vmClasses::URL_klass(), 1, CHECK_NH);
urls->obj_at_put(0, url_h);
Handle url_classloader = JavaCalls::construct_new_instance(
vmClasses::URLClassLoader_klass(),
vmSymbols::url_array_classloader_void_signature(),
urls, Handle(), CHECK_NH);
return url_classloader;
}
Handle UnregisteredClasses::get_url_classloader(Symbol* path, TRAPS) {
if (_url_classloader_table == NULL) {
_url_classloader_table = new (ResourceObj::C_HEAP, mtClass)URLClassLoaderTable();
}
Handle* url_classloader_ptr = _url_classloader_table->get(path);
if (url_classloader_ptr != NULL) {
return *url_classloader_ptr;
} else {
Handle url_classloader = create_url_classloader(path, CHECK_NH);
_url_classloader_table->put(path, url_classloader);
path->increment_refcount();
return url_classloader;
}
}

@ -0,0 +1,39 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
#ifndef SHARE_CDS_UNREGISTEREDCLASSES_HPP
#define SHARE_CDS_UNREGISTEREDCLASSES_HPP
#include "runtime/handles.hpp"
class UnregisteredClasses: AllStatic {
public:
static InstanceKlass* load_class(Symbol* h_name, const char* path, TRAPS);
private:
static Handle create_url_classloader(Symbol* path, TRAPS);
static Handle get_url_classloader(Symbol* path, TRAPS);
};
#endif // SHARE_CDS_UNREGISTEREDCLASSES_HPP

@ -25,7 +25,6 @@
#include "precompiled.hpp"
#include "cds/filemap.hpp"
#include "classfile/classFileParser.hpp"
#include "classfile/classFileStream.hpp"
#include "classfile/classLoader.inline.hpp"
#include "classfile/classLoaderExt.hpp"
#include "classfile/classLoaderData.inline.hpp"
@ -33,7 +32,6 @@
#include "classfile/klassFactory.hpp"
#include "classfile/modules.hpp"
#include "classfile/systemDictionary.hpp"
#include "classfile/systemDictionaryShared.hpp"
#include "classfile/vmSymbols.hpp"
#include "gc/shared/collectedHeap.hpp"
#include "logging/log.hpp"
@ -46,9 +44,7 @@
#include "runtime/arguments.hpp"
#include "runtime/handles.inline.hpp"
#include "runtime/java.hpp"
#include "runtime/javaCalls.hpp"
#include "runtime/os.hpp"
#include "services/threadService.hpp"
#include "utilities/stringUtils.hpp"
jshort ClassLoaderExt::_app_class_paths_start_index = ClassLoaderExt::max_classpath_index;
@ -250,90 +246,3 @@ void ClassLoaderExt::record_result(const s2 classpath_index, InstanceKlass* resu
result->set_shared_classpath_index(classpath_index);
result->set_shared_class_loader_type(classloader_type);
}
// Load the class of the given name from the location given by path. The path is specified by
// the "source:" in the class list file (see classListParser.cpp), and can be a directory or
// a JAR file.
InstanceKlass* ClassLoaderExt::load_class(Symbol* name, const char* path, TRAPS) {
assert(name != NULL, "invariant");
assert(DumpSharedSpaces, "this function is only used with -Xshare:dump");
ResourceMark rm(THREAD);
const char* class_name = name->as_C_string();
const char* file_name = file_name_for_class_name(class_name,
name->utf8_length());
assert(file_name != NULL, "invariant");
// Lookup stream for parsing .class file
ClassFileStream* stream = NULL;
ClassPathEntry* e = find_classpath_entry_from_cache(THREAD, path);
if (e == NULL) {
THROW_NULL(vmSymbols::java_lang_ClassNotFoundException());
}
{
PerfClassTraceTime vmtimer(perf_sys_class_lookup_time(),
THREAD->get_thread_stat()->perf_timers_addr(),
PerfClassTraceTime::CLASS_LOAD);
stream = e->open_stream(THREAD, file_name);
}
if (stream == NULL) {
// open_stream could return NULL even when no exception has be thrown (JDK-8263632).
THROW_NULL(vmSymbols::java_lang_ClassNotFoundException());
return NULL;
}
stream->set_verify(true);
ClassLoaderData* loader_data = ClassLoaderData::the_null_class_loader_data();
Handle protection_domain;
ClassLoadInfo cl_info(protection_domain);
InstanceKlass* k = KlassFactory::create_from_stream(stream,
name,
loader_data,
cl_info,
CHECK_NULL);
return k;
}
struct CachedClassPathEntry {
const char* _path;
ClassPathEntry* _entry;
};
static GrowableArray<CachedClassPathEntry>* cached_path_entries = NULL;
ClassPathEntry* ClassLoaderExt::find_classpath_entry_from_cache(JavaThread* current, const char* path) {
// This is called from dump time so it's single threaded and there's no need for a lock.
assert(DumpSharedSpaces, "this function is only used with -Xshare:dump");
if (cached_path_entries == NULL) {
cached_path_entries = new (ResourceObj::C_HEAP, mtClass) GrowableArray<CachedClassPathEntry>(20, mtClass);
}
CachedClassPathEntry ccpe;
for (int i=0; i<cached_path_entries->length(); i++) {
ccpe = cached_path_entries->at(i);
if (strcmp(ccpe._path, path) == 0) {
if (i != 0) {
// Put recent entries at the beginning to speed up searches.
cached_path_entries->remove_at(i);
cached_path_entries->insert_before(0, ccpe);
}
return ccpe._entry;
}
}
struct stat st;
if (os::stat(path, &st) != 0) {
// File or directory not found
return NULL;
}
ClassPathEntry* new_entry = NULL;
new_entry = create_class_path_entry(current, path, &st, false, false);
if (new_entry == NULL) {
return NULL;
}
ccpe._path = strdup(path);
ccpe._entry = new_entry;
cached_path_entries->insert_before(0, ccpe);
return new_entry;
}

@ -59,7 +59,6 @@ private:
static bool _has_non_jar_in_classpath;
static char* read_manifest(JavaThread* current, ClassPathEntry* entry, jint *manifest_size, bool clean_text);
static ClassPathEntry* find_classpath_entry_from_cache(JavaThread* current, const char* path);
public:
static void process_jar_manifest(JavaThread* current, ClassPathEntry* entry, bool check_for_duplicates);
@ -113,7 +112,6 @@ public:
}
static void record_result(const s2 classpath_index, InstanceKlass* result);
static InstanceKlass* load_class(Symbol* h_name, const char* path, TRAPS);
static void set_has_app_classes() {
_has_app_classes = true;
}

@ -430,6 +430,7 @@ class UnregisteredClassesTable : public ResourceHashtable<
static UnregisteredClassesTable* _unregistered_classes_table = NULL;
// true == class was successfully added; false == a duplicated class (with the same name) already exists.
bool SystemDictionaryShared::add_unregistered_class(Thread* current, InstanceKlass* klass) {
// We don't allow duplicated unregistered classes with the same name.
// We only archive the first class with that name that succeeds putting
@ -448,18 +449,6 @@ bool SystemDictionaryShared::add_unregistered_class(Thread* current, InstanceKla
return (klass == *v);
}
// true == class was successfully added; false == a duplicated class (with the same name) already exists.
bool SystemDictionaryShared::add_unregistered_class_for_static_archive(Thread* current, InstanceKlass* k) {
assert(DumpSharedSpaces, "only when dumping");
if (add_unregistered_class(current, k)) {
MutexLocker mu_r(current, Compile_lock); // add_to_hierarchy asserts this.
SystemDictionary::add_to_hierarchy(k);
return true;
} else {
return false;
}
}
// This function is called to lookup the super/interfaces of shared classes for
// unregistered loaders. E.g., SharedClass in the below example
// where "super:" (and optionally "interface:") have been specified.

@ -208,7 +208,6 @@ public:
static bool is_builtin_loader(ClassLoaderData* loader_data);
static bool add_unregistered_class_for_static_archive(Thread* current, InstanceKlass* k);
static InstanceKlass* lookup_super_for_unregistered_class(Symbol* class_name,
Symbol* super_name, bool is_superclass);

@ -135,6 +135,7 @@
/* support for CDS */ \
do_klass(ByteArrayInputStream_klass, java_io_ByteArrayInputStream ) \
do_klass(URL_klass, java_net_URL ) \
do_klass(URLClassLoader_klass, java_net_URLClassLoader ) \
do_klass(Jar_Manifest_klass, java_util_jar_Manifest ) \
do_klass(jdk_internal_loader_BuiltinClassLoader_klass,jdk_internal_loader_BuiltinClassLoader ) \
do_klass(jdk_internal_loader_ClassLoaders_klass, jdk_internal_loader_ClassLoaders ) \

@ -116,6 +116,7 @@
template(java_security_ProtectionDomain, "java/security/ProtectionDomain") \
template(java_security_SecureClassLoader, "java/security/SecureClassLoader") \
template(java_net_URL, "java/net/URL") \
template(java_net_URLClassLoader, "java/net/URLClassLoader") \
template(java_util_jar_Manifest, "java/util/jar/Manifest") \
template(java_io_OutputStream, "java/io/OutputStream") \
template(java_io_Reader, "java/io/Reader") \
@ -551,6 +552,7 @@
template(threadgroup_runnable_void_signature, "(Ljava/lang/ThreadGroup;Ljava/lang/Runnable;)V") \
template(threadgroup_string_void_signature, "(Ljava/lang/ThreadGroup;Ljava/lang/String;)V") \
template(string_class_signature, "(Ljava/lang/String;)Ljava/lang/Class;") \
template(string_boolean_class_signature, "(Ljava/lang/String;Z)Ljava/lang/Class;") \
template(object_object_object_signature, "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;") \
template(string_string_string_signature, "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;") \
template(string_string_signature, "(Ljava/lang/String;)Ljava/lang/String;") \
@ -709,6 +711,7 @@
template(toFileURL_name, "toFileURL") \
template(toFileURL_signature, "(Ljava/lang/String;)Ljava/net/URL;") \
template(url_void_signature, "(Ljava/net/URL;)V") \
template(url_array_classloader_void_signature, "([Ljava/net/URL;Ljava/lang/ClassLoader;)V") \
\
/*end*/

@ -349,10 +349,6 @@ class InstanceKlass: public Klass {
// Check if the class can be shared in CDS
bool is_shareable() const;
void clear_shared_class_loader_type() {
_misc_flags &= ~shared_loader_type_bits();
}
bool shared_loading_failed() const {
return (_misc_flags & _misc_shared_loading_failed) != 0;
}