8266764: [REDO] JDK-8255493 Support for pre-generated java.lang.invoke classes in CDS dynamic archive
Reviewed-by: ccheung, iklam
This commit is contained in:
parent
8c71144a23
commit
2066f497b9
src/hotspot/share
cds
archiveBuilder.cppdynamicArchive.cppdynamicArchive.hpplambdaFormInvokers.cpplambdaFormInvokers.hppmetaspaceShared.cpp
classfile
prims
runtime
test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive
@ -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 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
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 @@ public:
|
||||
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) {
|
||||
|
@ -2200,6 +2200,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
|
||||
@ -2256,7 +2269,7 @@ public:
|
||||
|
||||
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; }
|
||||
@ -2273,7 +2286,7 @@ public:
|
||||
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();
|
||||
}
|
||||
|
@ -297,12 +297,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;") \
|
||||
@ -694,13 +688,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*/
|
||||
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include "cds/classListWriter.hpp"
|
||||
#include "cds/dynamicArchive.hpp"
|
||||
#include "cds/heapShared.hpp"
|
||||
#include "cds/lambdaFormInvokers.hpp"
|
||||
#include "classfile/classFileStream.hpp"
|
||||
#include "classfile/classLoader.hpp"
|
||||
#include "classfile/classLoaderData.hpp"
|
||||
@ -420,10 +421,12 @@ JVM_END
|
||||
extern volatile jint vm_created;
|
||||
|
||||
JVM_ENTRY_NO_ENV(void, JVM_BeforeHalt())
|
||||
#if INCLUDE_CDS
|
||||
// Link all classes for dynamic CDS dumping before vm exit.
|
||||
if (DynamicDumpSharedSpaces) {
|
||||
MetaspaceShared::link_and_cleanup_shared_classes(THREAD);
|
||||
DynamicArchive::prepare_for_dynamic_dumping_at_exit();
|
||||
}
|
||||
#endif
|
||||
EventShutdown event;
|
||||
if (event.should_commit()) {
|
||||
event.set_reason("Shutdown requested from Java");
|
||||
@ -3643,7 +3646,7 @@ JVM_END
|
||||
|
||||
JVM_ENTRY(jboolean, JVM_IsDumpingClassList(JNIEnv *env))
|
||||
#if INCLUDE_CDS
|
||||
return ClassListWriter::is_enabled();
|
||||
return ClassListWriter::is_enabled() || DynamicDumpSharedSpaces;
|
||||
#else
|
||||
return false;
|
||||
#endif // INCLUDE_CDS
|
||||
@ -3651,13 +3654,20 @@ JVM_END
|
||||
|
||||
JVM_ENTRY(void, JVM_LogLambdaFormInvoker(JNIEnv *env, jstring line))
|
||||
#if INCLUDE_CDS
|
||||
assert(ClassListWriter::is_enabled(), "Should be set and open");
|
||||
assert(ClassListWriter::is_enabled() || DynamicDumpSharedSpaces, "Should be set and open or do dynamic dump");
|
||||
if (line != NULL) {
|
||||
ResourceMark rm(THREAD);
|
||||
Handle h_line (THREAD, JNIHandles::resolve_non_null(line));
|
||||
char* c_line = java_lang_String::as_utf8_string(h_line());
|
||||
ClassListWriter w;
|
||||
w.stream()->print_cr("%s %s", LAMBDA_FORM_TAG, c_line);
|
||||
if (DynamicDumpSharedSpaces) {
|
||||
// Note: LambdaFormInvokers::append_filtered and LambdaFormInvokers::append take same format which is not
|
||||
// same as below the print format. The line does not include LAMBDA_FORM_TAG.
|
||||
LambdaFormInvokers::append_filtered(os::strdup((const char*)c_line, mtInternal));
|
||||
}
|
||||
if (ClassListWriter::is_enabled()) {
|
||||
ClassListWriter w;
|
||||
w.stream()->print_cr("%s %s", LAMBDA_FORM_TAG, c_line);
|
||||
}
|
||||
}
|
||||
#endif // INCLUDE_CDS
|
||||
JVM_END
|
||||
@ -3676,7 +3686,7 @@ JVM_ENTRY(void, JVM_DumpDynamicArchive(JNIEnv *env, jstring archiveName))
|
||||
ResourceMark rm(THREAD);
|
||||
Handle file_handle(THREAD, JNIHandles::resolve_non_null(archiveName));
|
||||
char* archive_name = java_lang_String::as_utf8_string(file_handle());
|
||||
DynamicArchive::dump(archive_name, THREAD);
|
||||
DynamicArchive::dump(archive_name, CHECK);
|
||||
#endif // INCLUDE_CDS
|
||||
JVM_END
|
||||
|
||||
|
@ -509,7 +509,15 @@ void before_exit(JavaThread* thread) {
|
||||
|
||||
#if INCLUDE_CDS
|
||||
if (DynamicDumpSharedSpaces) {
|
||||
ExceptionMark em(thread);
|
||||
DynamicArchive::dump();
|
||||
if (thread->has_pending_exception()) {
|
||||
ResourceMark rm(thread);
|
||||
oop pending_exception = thread->pending_exception();
|
||||
log_error(cds)("ArchiveClassesAtExit has failed %s: %s", pending_exception->klass()->external_name(),
|
||||
java_lang_String::as_utf8_string(java_lang_Throwable::message(pending_exception)));
|
||||
thread->clear_pending_exception();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -154,6 +154,7 @@ Mutex* DumpTimeTable_lock = NULL;
|
||||
Mutex* CDSLambda_lock = NULL;
|
||||
Mutex* DumpRegion_lock = NULL;
|
||||
Mutex* ClassListFile_lock = NULL;
|
||||
Mutex* LambdaFormInvokers_lock = NULL;
|
||||
#endif // INCLUDE_CDS
|
||||
Mutex* Bootclasspath_lock = NULL;
|
||||
|
||||
@ -344,6 +345,7 @@ void mutex_init() {
|
||||
def(CDSLambda_lock , PaddedMutex , leaf, true, _safepoint_check_never);
|
||||
def(DumpRegion_lock , PaddedMutex , leaf, true, _safepoint_check_never);
|
||||
def(ClassListFile_lock , PaddedMutex , leaf, true, _safepoint_check_never);
|
||||
def(LambdaFormInvokers_lock , PaddedMutex , nonleaf+2, false, _safepoint_check_always);
|
||||
#endif // INCLUDE_CDS
|
||||
def(Bootclasspath_lock , PaddedMutex , leaf, false, _safepoint_check_never);
|
||||
|
||||
|
@ -132,6 +132,7 @@ extern Mutex* DumpTimeTable_lock; // SystemDictionaryShared::find
|
||||
extern Mutex* CDSLambda_lock; // SystemDictionaryShared::get_shared_lambda_proxy_class
|
||||
extern Mutex* DumpRegion_lock; // Symbol::operator new(size_t sz, int len)
|
||||
extern Mutex* ClassListFile_lock; // ClassListWriter()
|
||||
extern Mutex* LambdaFormInvokers_lock; // Protecting LambdaFormInvokers::_lambdaform_lines
|
||||
#endif // INCLUDE_CDS
|
||||
#if INCLUDE_JFR
|
||||
extern Mutex* JfrStacktrace_lock; // used to guard access to the JFR stacktrace table
|
||||
|
@ -25,6 +25,7 @@
|
||||
|
||||
#include "precompiled.hpp"
|
||||
#include "jvm.h"
|
||||
#include "cds/dynamicArchive.hpp"
|
||||
#include "cds/metaspaceShared.hpp"
|
||||
#include "classfile/classLoader.hpp"
|
||||
#include "classfile/javaClasses.hpp"
|
||||
@ -3300,12 +3301,14 @@ void JavaThread::invoke_shutdown_hooks() {
|
||||
this->clear_pending_exception();
|
||||
}
|
||||
|
||||
#if INCLUDE_CDS
|
||||
// Link all classes for dynamic CDS dumping before vm exit.
|
||||
// Same operation is being done in JVM_BeforeHalt for handling the
|
||||
// case where the application calls System.exit().
|
||||
if (DynamicDumpSharedSpaces) {
|
||||
MetaspaceShared::link_and_cleanup_shared_classes(this);
|
||||
DynamicArchive::prepare_for_dynamic_dumping_at_exit();
|
||||
}
|
||||
#endif
|
||||
|
||||
EXCEPTION_MARK;
|
||||
Klass* shutdown_klass =
|
||||
|
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8266764
|
||||
* @summary test dynamic dump with OOM
|
||||
* @requires vm.cds
|
||||
* @requires vm.gc.Serial & vm.gc == null
|
||||
* @comment Test dynamic dump at OOM, currently only works with SerialGC
|
||||
* @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds /test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes
|
||||
* @compile ./test-classes/MiniStoreOom.java
|
||||
* @build LambHello sun.hotspot.WhiteBox
|
||||
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar ministore.jar MiniStoreOom
|
||||
* @run driver jdk.test.lib.helpers.ClassFileInstaller sun.hotspot.WhiteBox
|
||||
* @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. TestDynamicDumpAtOom
|
||||
*/
|
||||
|
||||
import jdk.test.lib.helpers.ClassFileInstaller;
|
||||
import jdk.test.lib.cds.CDSTestUtils.Result;
|
||||
|
||||
public class TestDynamicDumpAtOom extends DynamicArchiveTestBase {
|
||||
private static final String mainClass = "MiniStoreOom";
|
||||
private static final String jarFile = "ministore.jar";
|
||||
private static void doTest(String topArchiveName) throws Exception {
|
||||
dump(topArchiveName,
|
||||
"-Xmx64M",
|
||||
"-XX:+UseSerialGC",
|
||||
"-Xlog:cds",
|
||||
"-Xlog:cds+dynamic=debug",
|
||||
"-cp",
|
||||
jarFile,
|
||||
mainClass,
|
||||
"1024").assertAbnormalExit(output -> {
|
||||
output.shouldContain("ArchiveClassesAtExit has failed")
|
||||
.shouldContain("java.lang.OutOfMemoryError: Java heap space");
|
||||
});
|
||||
}
|
||||
|
||||
static void testDefaultBase() throws Exception {
|
||||
String topArchiveName = getNewArchiveName("top");
|
||||
doTest(topArchiveName);
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
runTest(TestDynamicDumpAtOom::testDefaultBase);
|
||||
}
|
||||
}
|
75
test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/TestDynamicRegenerateHolderClasses.java
Normal file
75
test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/TestDynamicRegenerateHolderClasses.java
Normal file
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8255493
|
||||
* @summary LambHello World test for regenerate lambda holder classes in dynamic archive
|
||||
* @requires vm.cds
|
||||
* @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds /test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes
|
||||
* @build LambHello sun.hotspot.WhiteBox
|
||||
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar lambhello.jar LambHello
|
||||
* @run driver jdk.test.lib.helpers.ClassFileInstaller sun.hotspot.WhiteBox
|
||||
* @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. TestDynamicRegenerateHolderClasses
|
||||
*/
|
||||
|
||||
import jdk.test.lib.helpers.ClassFileInstaller;
|
||||
|
||||
public class TestDynamicRegenerateHolderClasses extends DynamicArchiveTestBase {
|
||||
static String CHECK_MESSAGES[] = {"java.lang.invoke.Invokers$Holder source: shared objects file (top)",
|
||||
"java.lang.invoke.DirectMethodHandle$Holder source: shared objects file (top)",
|
||||
"java.lang.invoke.DelegatingMethodHandle$Holder source: shared objects file (top)",
|
||||
"java.lang.invoke.LambdaForm$Holder source: shared objects file (top)"};
|
||||
public static void main(String[] args) throws Exception {
|
||||
runTest(TestDynamicRegenerateHolderClasses::testDefaultBase);
|
||||
}
|
||||
|
||||
static void testDefaultBase() throws Exception {
|
||||
String topArchiveName = getNewArchiveName("top");
|
||||
doTest(topArchiveName);
|
||||
}
|
||||
|
||||
private static void doTest(String topArchiveName) throws Exception {
|
||||
String appJar = ClassFileInstaller.getJarPath("lambhello.jar");
|
||||
String mainClass = "LambHello";
|
||||
dump(topArchiveName,
|
||||
"-Xlog:cds",
|
||||
"-Xlog:cds+dynamic=debug",
|
||||
"-cp", appJar, mainClass)
|
||||
.assertNormalExit(output -> {
|
||||
output.shouldContain("Written dynamic archive 0x");
|
||||
});
|
||||
run(topArchiveName,
|
||||
"-Xlog:class+load",
|
||||
"-Xlog:cds+dynamic=debug,cds=debug,class+load",
|
||||
"-cp", appJar, mainClass)
|
||||
.assertNormalExit(output -> {
|
||||
output.shouldContain("LambHello source: shared objects file (top)")
|
||||
.shouldHaveExitValue(0);
|
||||
for (String s : CHECK_MESSAGES) {
|
||||
output.shouldContain(s);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
import java.util.HashMap;
|
||||
public class MiniStoreOom {
|
||||
private static HashMap<Integer, Byte[]> store = new HashMap<Integer, Byte[]>();
|
||||
public static void main(String... args) throws Exception {
|
||||
int size = Integer.valueOf(args[0]);
|
||||
int i = 0;
|
||||
while (i++ < Integer.MAX_VALUE) {
|
||||
store.put(i, new Byte[size]);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user