Skip to content
Permalink
Browse files
8255493: Support for pre-generated java.lang.invoke classes in CDS dy…
…namic archive

Reviewed-by: iklam, ccheung
  • Loading branch information
yminqi committed May 4, 2021
1 parent 770dfc1 commit 8b37d4877087052e2ce721b795217feea24802b3
Showing 11 changed files with 260 additions and 59 deletions.
@@ -259,6 +259,7 @@ void ArchiveBuilder::gather_klasses_and_symbols() {
log_info(cds)(" instance classes = %5d", _num_instance_klasses);
log_info(cds)(" obj array classes = %5d", _num_obj_array_klasses);
log_info(cds)(" type array classes = %5d", _num_type_array_klasses);
log_info(cds)(" symbols = %5d", _symbols->length());

if (DumpSharedSpaces) {
// To ensure deterministic contents in the static archive, we need to ensure that
@@ -27,6 +27,7 @@
#include "cds/archiveBuilder.hpp"
#include "cds/archiveUtils.inline.hpp"
#include "cds/dynamicArchive.hpp"
#include "cds/lambdaFormInvokers.hpp"
#include "cds/metaspaceShared.hpp"
#include "classfile/classLoaderData.inline.hpp"
#include "classfile/symbolTable.hpp"
@@ -344,29 +345,34 @@ void DynamicArchive::dump(const char* archive_name, TRAPS) {
} else {
// prevent multiple dumps.
set_has_been_dumped_once();
}
ArchiveClassesAtExit = archive_name;
if (Arguments::init_shared_archive_paths()) {
dump();
} else {
ArchiveClassesAtExit = nullptr;
THROW_MSG(vmSymbols::java_lang_RuntimeException(),
ArchiveClassesAtExit = archive_name;
if (Arguments::init_shared_archive_paths()) {
dump(CHECK);
} else {
ArchiveClassesAtExit = nullptr;
THROW_MSG(vmSymbols::java_lang_RuntimeException(),
"Could not setup SharedDynamicArchivePath");
}
// prevent do dynamic dump at exit.
ArchiveClassesAtExit = nullptr;
if (!Arguments::init_shared_archive_paths()) {
THROW_MSG(vmSymbols::java_lang_RuntimeException(),
}
// prevent do dynamic dump at exit.
ArchiveClassesAtExit = nullptr;
if (!Arguments::init_shared_archive_paths()) {
THROW_MSG(vmSymbols::java_lang_RuntimeException(),
"Could not restore SharedDynamicArchivePath");
}
}
}

void DynamicArchive::dump() {
void DynamicArchive::dump(TRAPS) {
if (Arguments::GetSharedDynamicArchivePath() == NULL) {
log_warning(cds, dynamic)("SharedDynamicArchivePath is not specified");
return;
}

// regenerate lambdaform holder classes
log_info(cds, dynamic)("Regenerate lambdaform holder classes ...");
LambdaFormInvokers::regenerate_holder_classes(CHECK);
log_info(cds, dynamic)("Regenerate lambdaform holder classes ...done");

VM_PopulateDynamicDumpSharedSpace op;
VMThread::execute(&op);
}
@@ -61,7 +61,7 @@ class DynamicArchive : AllStatic {
static bool _has_been_dumped_once;
public:
static void dump(const char* archive_name, TRAPS);
static void dump();
static void dump(TRAPS);
static bool has_been_dumped_once() { return _has_been_dumped_once; }
static void set_has_been_dumped_once() { _has_been_dumped_once = true; }
static bool is_mapped() { return FileMapInfo::dynamic_info() != NULL; }
@@ -23,6 +23,7 @@
*/

#include "precompiled.hpp"
#include "cds/archiveBuilder.hpp"
#include "cds/lambdaFormInvokers.hpp"
#include "cds/metaspaceShared.hpp"
#include "classfile/classLoadInfo.hpp"
@@ -46,23 +47,52 @@
#include "runtime/handles.inline.hpp"
#include "runtime/javaCalls.hpp"

GrowableArray<char*>* LambdaFormInvokers::_lambdaform_lines = NULL;
GrowableArrayCHeap<char*, mtClassShared>* LambdaFormInvokers::_lambdaform_lines = nullptr;
Array<Array<char>*>* LambdaFormInvokers::_static_archive_invokers = nullptr;

#define NUM_FILTER 4
static const char* filter[NUM_FILTER] = {"java.lang.invoke.Invokers$Holder",
"java.lang.invoke.DirectMethodHandle$Holder",
"java.lang.invoke.DelegatingMethodHandle$Holder",
"java.lang.invoke.LambdaForm$Holder"};

static bool should_be_archived(char* line) {
for (int k = 0; k < NUM_FILTER; k++) {
if (strstr(line, filter[k]) != nullptr) {
return true;
}
}
return false;
}

void LambdaFormInvokers::append_filtered(char* line) {
if (should_be_archived(line)) {
append(line);
}
}
#undef NUM_FILTER

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

void LambdaFormInvokers::regenerate_holder_classes(TRAPS) {
assert(_lambdaform_lines != NULL, "Bad List");
if (_lambdaform_lines == nullptr || _lambdaform_lines->length() == 0) {
log_info(cds)("Nothing to regenerate for holder classes");
return;
}

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!");
log_debug(cds)("Total lambdaform lines %d", _lambdaform_lines->length());

HandleMark hm(THREAD);
int len = _lambdaform_lines->length();
objArrayHandle list_lines = oopFactory::new_objArray_handle(vmClasses::String_klass(), len, CHECK);
for (int i = 0; i < len; i++) {
@@ -81,9 +111,16 @@ void LambdaFormInvokers::regenerate_holder_classes(TRAPS) {
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;
if (!PENDING_EXCEPTION->is_a(vmClasses::OutOfMemoryError_klass())) {
log_error(cds)("%s: %s", PENDING_EXCEPTION->klass()->external_name(),
java_lang_String::as_utf8_string(java_lang_Throwable::message(PENDING_EXCEPTION)));
if (DumpSharedSpaces) {
log_error(cds)("Failed to generate LambdaForm holder classes. Is your classlist out of date?");
} else {
log_error(cds)("Failed to generate LambdaForm holder classes. Was the base archive generated with an outdated classlist?");
}
CLEAR_PENDING_EXCEPTION;
}
return;
}

@@ -99,20 +136,10 @@ void LambdaFormInvokers::regenerate_holder_classes(TRAPS) {
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);
char *buf = NEW_RESOURCE_ARRAY(char, 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;
}
reload_class(class_name, st, CHECK);
}
}

@@ -147,5 +174,51 @@ void LambdaFormInvokers::reload_class(char* name, ClassFileStream& st, TRAPS) {

// exclude the existing class from dump
SystemDictionaryShared::set_excluded(InstanceKlass::cast(klass));
log_info(cds, lambda)("Replaced class %s, old: %p new: %p", name, klass, result);
SystemDictionaryShared::init_dumptime_info(result);
log_debug(cds, lambda)("Replaced class %s, old: %p new: %p", name, klass, result);
}

void LambdaFormInvokers::dump_static_archive_invokers() {
if (_lambdaform_lines != nullptr && _lambdaform_lines->length() > 0) {
int count = 0;
int len = _lambdaform_lines->length();
for (int i = 0; i < len; i++) {
char* str = _lambdaform_lines->at(i);
if (should_be_archived(str)) {
count++;
}
}
log_debug(cds)("Number of LF invoker lines stored: %d", count);
if (count > 0) {
_static_archive_invokers = ArchiveBuilder::new_ro_array<Array<char>*>(count);
int index = 0;
for (int i = 0; i < len; i++) {
char* str = _lambdaform_lines->at(i);
if (should_be_archived(str)) {
size_t str_len = strlen(str) + 1; // including terminating zero
Array<char>* line = ArchiveBuilder::new_ro_array<char>((int)str_len);
strncpy(line->adr_at(0), str, str_len);

_static_archive_invokers->at_put(index, line);
ArchivePtrMarker::mark_pointer(_static_archive_invokers->adr_at(index));
index++;
}
}
assert(index == count, "Should match");
}
}
}

void LambdaFormInvokers::read_static_archive_invokers() {
if (_static_archive_invokers != nullptr) {
for (int i = 0; i < _static_archive_invokers->length(); i++) {
Array<char>* line = _static_archive_invokers->at(i);
char* str = line->adr_at(0);
append_filtered(str);
}
}
}

void LambdaFormInvokers::serialize(SerializeClosure* soc) {
soc->do_ptr((void**)&_static_archive_invokers);
}
@@ -26,21 +26,26 @@
#define SHARE_CDS_LAMBDAFORMINVOKERS_HPP
#include "memory/allStatic.hpp"
#include "runtime/handles.hpp"
#include "utilities/growableArray.hpp"

template <class T>
class GrowableArray;
class ClassFileStream;
template <class T> class Array;

class LambdaFormInvokers : public AllStatic {
private:
static GrowableArray<char*>* _lambdaform_lines;
static GrowableArrayCHeap<char*, mtClassShared>* _lambdaform_lines;
// for storing LF form lines (LF_RESOLVE only) in read only table.
static Array<Array<char>*>* _static_archive_invokers;
static void reload_class(char* name, ClassFileStream& st, TRAPS);
public:

static void append(char* line);
static void append_filtered(char* line);
static void regenerate_holder_classes(TRAPS);
static GrowableArray<char*>* lambdaform_lines() {
static GrowableArrayCHeap<char*, mtClassShared>* lambdaform_lines() {
return _lambdaform_lines;
}
static void dump_static_archive_invokers();
static void read_static_archive_invokers();
static void serialize(SerializeClosure* soc);
};
#endif // SHARE_CDS_LAMBDAFORMINVOKERS_HPP
@@ -361,6 +361,7 @@ void MetaspaceShared::serialize(SerializeClosure* soc) {

CDS_JAVA_HEAP_ONLY(ClassLoaderDataShared::serialize(soc);)

LambdaFormInvokers::serialize(soc);
soc->do_tag(666);
}

@@ -460,6 +461,8 @@ char* VM_PopulateDumpSharedSpace::dump_read_only_tables() {

SystemDictionaryShared::write_to_archive();

// Write lambform lines into archive
LambdaFormInvokers::dump_static_archive_invokers();
// Write the other data to the output array.
DumpRegion* ro_region = ArchiveBuilder::current()->ro_region();
char* start = ro_region->top();
@@ -925,12 +928,12 @@ void MetaspaceShared::initialize_runtime_shared_and_meta_spaces() {
char* cds_end = dynamic_mapped ? dynamic_mapinfo->mapped_end() : static_mapinfo->mapped_end();
set_shared_metaspace_range(cds_base, static_mapinfo->mapped_end(), cds_end);
_relocation_delta = static_mapinfo->relocation_delta();
_requested_base_address = static_mapinfo->requested_base_address();
if (dynamic_mapped) {
FileMapInfo::set_shared_path_table(dynamic_mapinfo);
} else {
FileMapInfo::set_shared_path_table(static_mapinfo);
}
_requested_base_address = static_mapinfo->requested_base_address();
} else {
set_shared_metaspace_range(NULL, NULL, NULL);
UseSharedSpaces = false;
@@ -1444,6 +1447,12 @@ void MetaspaceShared::initialize_shared_spaces() {
dynamic_mapinfo->unmap_region(MetaspaceShared::bm);
}

// Set up LambdaFormInvokers::_lambdaform_lines for dynamic dump
if (DynamicDumpSharedSpaces) {
// Read stored LF format lines stored in static archive
LambdaFormInvokers::read_static_archive_invokers();
}

if (PrintSharedArchiveAndExit) {
// Print archive names
if (dynamic_mapinfo != nullptr) {
@@ -2205,6 +2205,19 @@ SystemDictionaryShared::find_record(RunTimeSharedDictionary* static_dict, RunTim

unsigned int hash = SystemDictionaryShared::hash_for_shared_dictionary_quick(name);
const RunTimeSharedClassInfo* record = NULL;
if (DynamicArchive::is_mapped()) {
// Those regenerated holder classes are in dynamic archive
if (name == vmSymbols::java_lang_invoke_Invokers_Holder() ||
name == vmSymbols::java_lang_invoke_DirectMethodHandle_Holder() ||
name == vmSymbols::java_lang_invoke_LambdaForm_Holder() ||
name == vmSymbols::java_lang_invoke_DelegatingMethodHandle_Holder()) {
record = dynamic_dict->lookup(name, hash, 0);
if (record != nullptr) {
return record;
}
}
}

if (!MetaspaceShared::is_shared_dynamic(name)) {
// The names of all shared classes in the static dict must also be in the
// static archive
@@ -2261,7 +2274,7 @@ class SharedDictionaryPrinter : StackObj {

void do_value(const RunTimeSharedClassInfo* record) {
ResourceMark rm;
_st->print_cr("%4d: %s %s", (_index++), record->_klass->external_name(),
_st->print_cr("%4d: %s %s", _index++, record->_klass->external_name(),
class_loader_name_for_shared(record->_klass));
}
int index() const { return _index; }
@@ -2278,7 +2291,7 @@ class SharedLambdaDictionaryPrinter : StackObj {
ResourceMark rm;
Klass* k = record->proxy_klass_head();
while (k != nullptr) {
_st->print_cr("%4d: %s %s", (++_index), k->external_name(),
_st->print_cr("%4d: %s %s", _index++, k->external_name(),
class_loader_name_for_shared(k));
k = k->next_link();
}
@@ -296,12 +296,6 @@
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;") \
template(dumpSharedArchive, "dumpSharedArchive") \
template(dumpSharedArchive_signature, "(ZLjava/lang/String;)V") \
\
/* Intrinsic Annotation (JDK 9 and above) */ \
template(jdk_internal_vm_annotation_DontInline_signature, "Ljdk/internal/vm/annotation/DontInline;") \
@@ -693,13 +687,22 @@
/* jfr signatures */ \
JFR_TEMPLATES(template) \
\
/* cds */ \
template(jdk_internal_loader_ClassLoaders, "jdk/internal/loader/ClassLoaders") \
template(java_util_concurrent_ConcurrentHashMap, "java/util/concurrent/ConcurrentHashMap") \
template(java_util_ArrayList, "java/util/ArrayList") \
template(toFileURL_name, "toFileURL") \
template(toFileURL_signature, "(Ljava/lang/String;)Ljava/net/URL;") \
template(url_void_signature, "(Ljava/net/URL;)V") \
/* CDS */ \
template(dumpSharedArchive, "dumpSharedArchive") \
template(dumpSharedArchive_signature, "(ZLjava/lang/String;)V") \
template(generateLambdaFormHolderClasses, "generateLambdaFormHolderClasses") \
template(generateLambdaFormHolderClasses_signature, "([Ljava/lang/String;)[Ljava/lang/Object;") \
template(java_lang_invoke_Invokers_Holder, "java/lang/invoke/Invokers$Holder") \
template(java_lang_invoke_DirectMethodHandle_Holder, "java/lang/invoke/DirectMethodHandle$Holder") \
template(java_lang_invoke_LambdaForm_Holder, "java/lang/invoke/LambdaForm$Holder") \
template(java_lang_invoke_DelegatingMethodHandle_Holder, "java/lang/invoke/DelegatingMethodHandle$Holder") \
template(jdk_internal_loader_ClassLoaders, "jdk/internal/loader/ClassLoaders") \
template(jdk_internal_misc_CDS, "jdk/internal/misc/CDS") \
template(java_util_concurrent_ConcurrentHashMap, "java/util/concurrent/ConcurrentHashMap") \
template(java_util_ArrayList, "java/util/ArrayList") \
template(toFileURL_name, "toFileURL") \
template(toFileURL_signature, "(Ljava/lang/String;)Ljava/net/URL;") \
template(url_void_signature, "(Ljava/net/URL;)V") \
\
/*end*/

1 comment on commit 8b37d48

@openjdk-notifier
Copy link

@openjdk-notifier openjdk-notifier bot commented on 8b37d48 May 4, 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.