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:
Yumin Qi 2021-05-16 02:26:46 +00:00
parent 8c71144a23
commit 2066f497b9
16 changed files with 407 additions and 75 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"
@ -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);
}
}

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