Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
Closed
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
18 changes: 12 additions & 6 deletions src/hotspot/share/cds/classListParser.cpp
Expand Up @@ -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"
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -458,24 +466,22 @@ 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);
error("The number of interfaces (%d) specified in class list does not match the class file (%d)",
_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;
}

Expand Down
115 changes: 115 additions & 0 deletions src/hotspot/share/cds/unregisteredClasses.cpp
@@ -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;
}
}
39 changes: 39 additions & 0 deletions src/hotspot/share/cds/unregisteredClasses.hpp
@@ -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
91 changes: 0 additions & 91 deletions src/hotspot/share/classfile/classLoaderExt.cpp
Expand Up @@ -25,15 +25,13 @@
#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"
#include "classfile/classLoadInfo.hpp"
#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"
Expand All @@ -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;
Expand Down Expand Up @@ -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;
}
2 changes: 0 additions & 2 deletions src/hotspot/share/classfile/classLoaderExt.hpp
Expand Up @@ -59,7 +59,6 @@ class ClassLoaderExt: public ClassLoader { // AllStatic
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);
Expand Down Expand Up @@ -113,7 +112,6 @@ class ClassLoaderExt: public ClassLoader { // AllStatic
}

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;
}
Expand Down
13 changes: 1 addition & 12 deletions src/hotspot/share/classfile/systemDictionaryShared.cpp
Expand Up @@ -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
Expand All @@ -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.
Expand Down
1 change: 0 additions & 1 deletion src/hotspot/share/classfile/systemDictionaryShared.hpp
Expand Up @@ -208,7 +208,6 @@ class SystemDictionaryShared: public SystemDictionary {

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

Expand Down
1 change: 1 addition & 0 deletions src/hotspot/share/classfile/vmClassMacros.hpp
Expand Up @@ -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 ) \
Expand Down