Skip to content
Permalink
Browse files
8266764: [REDO] JDK-8255493 Support for pre-generated java.lang.invok…
…e classes in CDS dynamic archive

Reviewed-by: ccheung, iklam
  • Loading branch information
yminqi committed May 16, 2021
1 parent 8c71144 commit 2066f497b9677971ece0b8a4d855f87a2f4c4018
@@ -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"
@@ -169,7 +170,6 @@ void DynamicArchiveBuilder::init_header() {
assert(FileMapInfo::dynamic_info() == mapinfo, "must be");
_header = mapinfo->dynamic_header();

Thread* THREAD = Thread::current();
FileMapInfo* base_info = FileMapInfo::current_info();
_header->set_base_header_crc(base_info->crc());
for (int i = 0; i < MetaspaceShared::n_regions; i++) {
@@ -250,7 +250,6 @@ void DynamicArchiveBuilder::sort_methods(InstanceKlass* ik) const {
}
#endif

Thread* THREAD = Thread::current();
Method::sort_methods(ik->methods(), /*set_idnums=*/true, dynamic_dump_method_comparator);
if (ik->default_methods() != NULL) {
Method::sort_methods(ik->default_methods(), /*set_idnums=*/false, dynamic_dump_method_comparator);
@@ -331,6 +330,20 @@ class VM_PopulateDynamicDumpSharedSpace: public VM_GC_Sync_Operation {
}
};

void DynamicArchive::prepare_for_dynamic_dumping_at_exit() {
EXCEPTION_MARK;
ResourceMark rm(THREAD);
MetaspaceShared::link_and_cleanup_shared_classes(THREAD);
if (HAS_PENDING_EXCEPTION) {
log_error(cds)("ArchiveClassesAtExit has failed");
log_error(cds)("%s: %s", PENDING_EXCEPTION->klass()->external_name(),
java_lang_String::as_utf8_string(java_lang_Throwable::message(PENDING_EXCEPTION)));
// We cannot continue to dump the archive anymore.
DynamicDumpSharedSpaces = false;
CLEAR_PENDING_EXCEPTION;
}
}

bool DynamicArchive::_has_been_dumped_once = false;

void DynamicArchive::dump(const char* archive_name, TRAPS) {
@@ -344,20 +357,20 @@ 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();
} 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");
}
}
}

@@ -60,6 +60,7 @@ class DynamicArchiveHeader : public FileMapHeader {
class DynamicArchive : AllStatic {
static bool _has_been_dumped_once;
public:
static void prepare_for_dynamic_dumping_at_exit();
static void dump(const char* archive_name, TRAPS);
static void dump();
static bool has_been_dumped_once() { return _has_been_dumped_once; }
@@ -23,6 +23,7 @@
*/

#include "precompiled.hpp"
#include "cds/archiveBuilder.hpp"
#include "cds/lambdaFormInvokers.hpp"
#include "cds/metaspaceShared.hpp"
#include "classfile/classLoadInfo.hpp"
@@ -45,31 +46,76 @@
#include "oops/typeArrayOop.inline.hpp"
#include "runtime/handles.inline.hpp"
#include "runtime/javaCalls.hpp"
#include "runtime/mutexLocker.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) {
MutexLocker ml(Thread::current(), LambdaFormInvokers_lock);
if (_lambdaform_lines == NULL) {
_lambdaform_lines = new GrowableArray<char*>(100);
_lambdaform_lines = new GrowableArrayCHeap<char*, mtClassShared>(150);
}
_lambdaform_lines->append(line);
}

// convenient output
class PrintLambdaFormMessage {
public:
PrintLambdaFormMessage() {
log_info(cds)("Regenerate MethodHandle Holder classes...");
}
~PrintLambdaFormMessage() {
log_info(cds)("Regenerate MethodHandle Holder classes...done");
}
};

void LambdaFormInvokers::regenerate_holder_classes(TRAPS) {
assert(_lambdaform_lines != NULL, "Bad List");
PrintLambdaFormMessage plm;
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!");

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++) {
Handle h_line = java_lang_String::create_from_str(_lambdaform_lines->at(i), CHECK);
list_lines->obj_at_put(i, h_line());
}

objArrayHandle list_lines;
{
MutexLocker ml(Thread::current(), LambdaFormInvokers_lock);
list_lines = oopFactory::new_objArray_handle(vmClasses::String_klass(), len, CHECK);
for (int i = 0; i < len; i++) {
Handle h_line = java_lang_String::create_from_str(_lambdaform_lines->at(i), CHECK);
list_lines->obj_at_put(i, h_line());
}
} // Before calling into java, release vm lock.
//
// Object[] CDS.generateLambdaFormHolderClasses(String[] lines)
// the returned Object[] layout:
@@ -81,9 +127,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 +152,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 +190,53 @@ 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_info(cds, lambda)("Replaced class %s, old: " INTPTR_FORMAT " new: " INTPTR_FORMAT,
name, p2i(klass), p2i(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++;
}
}
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");
}
log_debug(cds)("Total LF lines stored into static archive: %d", count);
}
}

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(str);
}
log_debug(cds)("Total LF lines read from static archive: %d", _static_archive_invokers->length());
}
}

void LambdaFormInvokers::serialize(SerializeClosure* soc) {
soc->do_ptr((void**)&_static_archive_invokers);
}
@@ -26,21 +26,24 @@
#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 dump_static_archive_invokers();
static void read_static_archive_invokers();
static void regenerate_holder_classes(TRAPS);
static GrowableArray<char*>* lambdaform_lines() {
return _lambdaform_lines;
}
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();
@@ -598,6 +601,8 @@ bool MetaspaceShared::link_class_for_cds(InstanceKlass* ik, TRAPS) {
void MetaspaceShared::link_and_cleanup_shared_classes(TRAPS) {
// Collect all loaded ClassLoaderData.
ResourceMark rm;

LambdaFormInvokers::regenerate_holder_classes(CHECK);
CollectCLDClosure collect_cld;
{
// ClassLoaderDataGraph::loaded_cld_do requires ClassLoaderDataGraph_lock.
@@ -718,12 +723,6 @@ void MetaspaceShared::preload_and_dump_impl(TRAPS) {
log_info(cds)("Reading extra data: done.");
}

if (LambdaFormInvokers::lambdaform_lines() != NULL) {
log_info(cds)("Regenerate MethodHandle Holder classes...");
LambdaFormInvokers::regenerate_holder_classes(CHECK);
log_info(cds)("Regenerate MethodHandle Holder classes done.");
}

HeapShared::init_for_dumping(CHECK);

// Rewrite and link classes
@@ -904,12 +903,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;
@@ -1408,6 +1407,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) {

0 comments on commit 2066f49

Please sign in to comment.