Skip to content
Permalink
Browse files
8261941: Use ClassLoader for unregistered classes during -Xshare:dump
Reviewed-by: iklam, minqi
  • Loading branch information
calvinccheung committed Sep 16, 2021
1 parent 7e92abe commit 12fa7073c58eb9f204bb60ab6192738f8aece626
Showing 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,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;
}

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

@@ -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 ) \

1 comment on commit 12fa707

@openjdk-notifier
Copy link

@openjdk-notifier openjdk-notifier bot commented on 12fa707 Sep 16, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.