Skip to content
Permalink
Browse files
8247536: Support for pre-generated java.lang.invoke classes in CDS st…
…atic archive

Reviewed-by: iklam, mchung
  • Loading branch information
yminqi committed Oct 10, 2020
1 parent 7ec9c8e commit e4469d2c8caa500ea50c49b40d078f1083bd1a33
@@ -126,6 +126,7 @@ ifneq ($(call check-jvm-feature, cds), true)
dynamicArchive.cpp \
filemap.cpp \
heapShared.cpp \
lambdaFormInvokers.cpp \
metaspaceShared.cpp \
metaspaceShared_$(HOTSPOT_TARGET_CPU).cpp \
metaspaceShared_$(HOTSPOT_TARGET_CPU_ARCH).cpp \
@@ -146,6 +146,7 @@ JVM_IsArrayClass
JVM_IsDynamicDumpingEnabled
JVM_IsSharingEnabled
JVM_IsConstructorIx
JVM_IsDumpingClassList
JVM_IsHiddenClass
JVM_IsInterface
JVM_IsPrimitiveClass
@@ -158,6 +159,7 @@ JVM_LatestUserDefinedLoader
JVM_LoadLibrary
JVM_LookupDefineClass
JVM_LookupLambdaProxyClassFromArchive
JVM_LogLambdaFormInvoker
JVM_MaxMemory
JVM_MaxObjectInspectionAge
JVM_MonitorNotify
@@ -6056,7 +6056,18 @@ void ClassFileParser::mangle_hidden_class_name(InstanceKlass* const ik) {
// use an illegal char such as ';' because that causes serialization issues
// and issues with hidden classes that create their own hidden classes.
char addr_buf[20];
jio_snprintf(addr_buf, 20, INTPTR_FORMAT, p2i(ik));
if (DumpSharedSpaces) {
// We want stable names for the archived hidden classes (only for static
// archive for now). Spaces under default_SharedBaseAddress() will be
// occupied by the archive at run time, so we know that no dynamically
// loaded InstanceKlass will be placed under there.
static volatile size_t counter = 0;
Atomic::cmpxchg(&counter, (size_t)0, Arguments::default_SharedBaseAddress()); // initialize it
size_t new_id = Atomic::add(&counter, (size_t)1);
jio_snprintf(addr_buf, 20, SIZE_FORMAT_HEX, new_id);
} else {
jio_snprintf(addr_buf, 20, INTPTR_FORMAT, p2i(ik));
}
size_t new_name_len = _class_name->utf8_length() + 2 + strlen(addr_buf);
char* new_name = NEW_RESOURCE_ARRAY(char, new_name_len);
jio_snprintf(new_name, new_name_len, "%s+%s",
@@ -28,6 +28,7 @@
#include "classfile/classListParser.hpp"
#include "classfile/classLoaderExt.hpp"
#include "classfile/javaClasses.inline.hpp"
#include "classfile/lambdaFormInvokers.hpp"
#include "classfile/symbolTable.hpp"
#include "classfile/systemDictionary.hpp"
#include "classfile/systemDictionaryShared.hpp"
@@ -86,6 +87,12 @@ bool ClassListParser::parse_one_line() {
if (*_line == '#') { // comment
continue;
}
// The line is output TRACE_RESOLVE
if (strncmp(_line, LambdaFormInvokers::lambda_form_invoker_tag(),
strlen(LambdaFormInvokers::lambda_form_invoker_tag())) == 0) {
LambdaFormInvokers::append(os::strdup((const char*)_line, mtInternal));
continue;
}
break;
}

@@ -1244,6 +1244,8 @@ oop java_lang_Class::process_archived_mirror(Klass* k, oop mirror,
java_lang_Class:set_init_lock(archived_mirror, NULL);

set_protection_domain(archived_mirror, NULL);
set_signers(archived_mirror, NULL);
set_source_file(archived_mirror, NULL);
}

// clear class loader and mirror_module_field
@@ -31,11 +31,7 @@
class ClassFileStream;
class ClassLoaderData;
class ClassLoadInfo;
template <typename>
class GrowableArray;
class Klass;
class Symbol;
class TempNewSymbol;

/*
* KlassFactory is an interface to implementations of the following mapping/function:
@@ -63,18 +59,12 @@ class TempNewSymbol;

class KlassFactory : AllStatic {

// approved clients
friend class ClassLoader;
friend class ClassLoaderExt;
friend class SystemDictionary;

private:
public:
static InstanceKlass* create_from_stream(ClassFileStream* stream,
Symbol* name,
ClassLoaderData* loader_data,
const ClassLoadInfo& cl_info,
TRAPS);
public:
static InstanceKlass* check_shared_class_file_load_hook(
InstanceKlass* ik,
Symbol* class_name,
@@ -0,0 +1,153 @@
/*
* Copyright (c) 2020, 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 "classfile/classLoadInfo.hpp"
#include "classfile/classFileStream.hpp"
#include "classfile/dictionary.hpp"
#include "classfile/javaClasses.inline.hpp"
#include "classfile/klassFactory.hpp"
#include "classfile/lambdaFormInvokers.hpp"
#include "classfile/symbolTable.hpp"
#include "classfile/systemDictionary.hpp"
#include "classfile/systemDictionaryShared.hpp"
#include "classfile/vmSymbols.hpp"
#include "logging/log.hpp"
#include "memory/oopFactory.hpp"
#include "memory/metaspaceShared.hpp"
#include "memory/resourceArea.hpp"
#include "oops/instanceKlass.hpp"
#include "oops/klass.hpp"
#include "oops/objArrayKlass.hpp"
#include "oops/objArrayOop.hpp"
#include "oops/oop.inline.hpp"
#include "oops/typeArrayOop.inline.hpp"
#include "runtime/handles.inline.hpp"
#include "runtime/javaCalls.hpp"

GrowableArray<char*>* LambdaFormInvokers::_lambdaform_lines = NULL;

void LambdaFormInvokers::append(char* line) {
if (_lambdaform_lines == NULL) {
_lambdaform_lines = new GrowableArray<char*>(100);
}
_lambdaform_lines->append(line);
}

void LambdaFormInvokers::regenerate_holder_classes(TRAPS) {
assert(_lambdaform_lines != NULL, "Bad List");
ResourceMark rm(THREAD);

Symbol* cds_name = vmSymbols::jdk_internal_misc_CDS();
Klass* cds_klass = SystemDictionary::resolve_or_null(cds_name, THREAD);
guarantee(cds_klass != NULL, "jdk/internal/misc/CDS must exist!");

int len = _lambdaform_lines->length();
objArrayHandle list_lines = oopFactory::new_objArray_handle(SystemDictionary::String_klass(), len, CHECK);
for (int i = 0; i < len; i++) {
char* record = _lambdaform_lines->at(i);
record += strlen(lambda_form_invoker_tag()) + 1; // skip the @lambda_form_invoker prefix
Handle h_line = java_lang_String::create_from_str(record, CHECK);
list_lines->obj_at_put(i, h_line());
}

//
// Object[] CDS.generateLambdaFormHolderClasses(String[] lines)
// the returned Object[] layout:
// name, byte[], name, byte[] ....
Symbol* method = vmSymbols::generateLambdaFormHolderClasses();
Symbol* signrs = vmSymbols::generateLambdaFormHolderClasses_signature();

JavaValue result(T_OBJECT);
JavaCalls::call_static(&result, cds_klass, method, signrs, list_lines, THREAD);

if (HAS_PENDING_EXCEPTION) {
log_info(cds)("%s: %s", THREAD->pending_exception()->klass()->external_name(),
java_lang_String::as_utf8_string(java_lang_Throwable::message(THREAD->pending_exception())));
CLEAR_PENDING_EXCEPTION;
return;
}

objArrayHandle h_array(THREAD, (objArrayOop)result.get_jobject());
int sz = h_array->length();
assert(sz % 2 == 0 && sz >= 2, "Must be even size of length");
for (int i = 0; i < sz; i+= 2) {
Handle h_name(THREAD, h_array->obj_at(i));
typeArrayHandle h_bytes(THREAD, (typeArrayOop)h_array->obj_at(i+1));
assert(h_name != NULL, "Class name is NULL");
assert(h_bytes != NULL, "Class bytes is NULL");

char *class_name = java_lang_String::as_utf8_string(h_name());
int len = h_bytes->length();
// make a copy of class bytes so GC will not affect us.
char *buf = resource_allocate_bytes(THREAD, len);
memcpy(buf, (char*)h_bytes->byte_at_addr(0), len);
ClassFileStream st((u1*)buf, len, NULL, ClassFileStream::verify);

reload_class(class_name, st, THREAD);
// free buf
resource_free_bytes(buf, len);

if (HAS_PENDING_EXCEPTION) {
log_info(cds)("Exception happened: %s", PENDING_EXCEPTION->klass()->name()->as_C_string());
log_info(cds)("Could not create InstanceKlass for class %s", class_name);
CLEAR_PENDING_EXCEPTION;
return;
}
}
}

// class_handle - the class name, bytes_handle - the class bytes
void LambdaFormInvokers::reload_class(char* name, ClassFileStream& st, TRAPS) {
Symbol* class_name = SymbolTable::new_symbol((const char*)name);
// the class must exist
Klass* klass = SystemDictionary::resolve_or_null(class_name, THREAD);
if (klass == NULL) {
log_info(cds)("Class %s not present, skip", name);
return;
}
assert(klass->is_instance_klass(), "Should be");

ClassLoaderData* cld = ClassLoaderData::the_null_class_loader_data();
Handle protection_domain;
ClassLoadInfo cl_info(protection_domain);

InstanceKlass* result = KlassFactory::create_from_stream(&st,
class_name,
cld,
cl_info,
CHECK);

{
MutexLocker mu_r(THREAD, Compile_lock); // add_to_hierarchy asserts this.
SystemDictionary::add_to_hierarchy(result, THREAD);
}
// new class not linked yet.
MetaspaceShared::try_link_class(result, THREAD);
assert(!HAS_PENDING_EXCEPTION, "Invariant");

// exclude the existing class from dump
SystemDictionaryShared::set_excluded(InstanceKlass::cast(klass));
log_info(cds)("Replaced class %s, old: %p new: %p", name, klass, result);
}
@@ -0,0 +1,50 @@
/*
* Copyright (c) 2020, 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_MEMORY_LAMBDAFORMINVOKERS_HPP
#define SHARE_MEMORY_LAMBDAFORMINVOKERS_HPP
#include "memory/allStatic.hpp"
#include "runtime/handles.hpp"

template <class T>
class GrowableArray;
class ClassFileStream;

class LambdaFormInvokers : public AllStatic {
private:
static GrowableArray<char*>* _lambdaform_lines;
static void reload_class(char* name, ClassFileStream& st, TRAPS);
public:

static void append(char* line);
static void regenerate_holder_classes(TRAPS);
static GrowableArray<char*>* lambdaform_lines() {
return _lambdaform_lines;
}

static const char* lambda_form_invoker_tag() {
return "@lambda-form-invoker";
}
};
#endif // SHARE_MEMORY_LAMBDAFORMINVOKERS_HPP
@@ -624,9 +624,9 @@ class SystemDictionary : AllStatic {
// Return Symbol or throw exception if name given is can not be a valid Symbol.
static Symbol* class_name_symbol(const char* name, Symbol* exception, TRAPS);

protected:
// Setup link to hierarchy
static void add_to_hierarchy(InstanceKlass* k, TRAPS);
protected:

// Basic find on loaded classes
static InstanceKlass* find_class(unsigned int hash,
@@ -1407,6 +1407,14 @@ bool SystemDictionaryShared::is_excluded_class(InstanceKlass* k) {
return (p == NULL) ? true : p->is_excluded();
}

void SystemDictionaryShared::set_excluded(InstanceKlass* k) {
Arguments::assert_is_dumping_archive();
DumpTimeSharedClassInfo* info = find_or_allocate_info_for(k);
if (info != NULL) {
info->set_excluded();
}
}

void SystemDictionaryShared::set_class_has_failed_verification(InstanceKlass* ik) {
Arguments::assert_is_dumping_archive();
DumpTimeSharedClassInfo* p = find_or_allocate_info_for(ik);
@@ -308,6 +308,7 @@ class SystemDictionaryShared: public SystemDictionary {
static void check_excluded_classes();
static void validate_before_archiving(InstanceKlass* k);
static bool is_excluded_class(InstanceKlass* k);
static void set_excluded(InstanceKlass* k);
static void dumptime_classes_do(class MetaspaceClosure* it);
static size_t estimate_size_for_archive();
static void write_to_archive(bool is_static_archive = true);
@@ -282,14 +282,17 @@
template(base_name, "base") \
/* Type Annotations (JDK 8 and above) */ \
template(type_annotations_name, "typeAnnotations") \
/* used by CDS */ \
template(jdk_internal_misc_CDS, "jdk/internal/misc/CDS") \
template(generateLambdaFormHolderClasses, "generateLambdaFormHolderClasses") \
template(generateLambdaFormHolderClasses_signature, "([Ljava/lang/String;)[Ljava/lang/Object;") \
\
/* Intrinsic Annotation (JDK 9 and above) */ \
template(jdk_internal_vm_annotation_DontInline_signature, "Ljdk/internal/vm/annotation/DontInline;") \
template(jdk_internal_vm_annotation_ForceInline_signature, "Ljdk/internal/vm/annotation/ForceInline;") \
template(jdk_internal_vm_annotation_Hidden_signature, "Ljdk/internal/vm/annotation/Hidden;") \
template(jdk_internal_vm_annotation_IntrinsicCandidate_signature, "Ljdk/internal/vm/annotation/IntrinsicCandidate;") \
template(jdk_internal_vm_annotation_Stable_signature, "Ljdk/internal/vm/annotation/Stable;") \
\
/* Support for JSR 292 & invokedynamic (JDK 1.7 and above) */ \
template(java_lang_invoke_CallSite, "java/lang/invoke/CallSite") \
template(java_lang_invoke_ConstantCallSite, "java/lang/invoke/ConstantCallSite") \
@@ -203,9 +203,15 @@ JVM_IsDynamicDumpingEnabled(JNIEnv* env);
JNIEXPORT jboolean JNICALL
JVM_IsSharingEnabled(JNIEnv* env);

JNIEXPORT jboolean JNICALL
JVM_IsDumpingClassList(JNIEnv* env);

JNIEXPORT jlong JNICALL
JVM_GetRandomSeedForDumping();

JNIEXPORT void JNICALL
JVM_LogLambdaFormInvoker(JNIEnv* env, jstring line);

/*
* java.lang.Throwable
*/

1 comment on commit e4469d2

@bridgekeeper

This comment has been minimized.

Copy link

@bridgekeeper bridgekeeper bot commented on e4469d2 Oct 10, 2020

Please sign in to comment.