8254309: appcds GCDuringDump.java failed - class must exist

Reviewed-by: ccheung, iklam
This commit is contained in:
Yumin Qi 2020-10-31 00:08:37 +00:00
parent 36c150b199
commit 9d5c9cc78b
8 changed files with 149 additions and 14 deletions

@ -37,6 +37,7 @@
#include "interpreter/linkResolver.hpp"
#include "logging/log.hpp"
#include "logging/logTag.hpp"
#include "memory/archiveUtils.hpp"
#include "memory/metaspaceShared.hpp"
#include "memory/resourceArea.hpp"
#include "oops/constantPool.hpp"
@ -572,6 +573,7 @@ Klass* ClassListParser::load_current_class(TRAPS) {
klass = java_lang_Class::as_Klass(obj);
} else { // load classes in bootclasspath/a
if (HAS_PENDING_EXCEPTION) {
ArchiveUtils::check_for_oom(PENDING_EXCEPTION); // exit on OOM
CLEAR_PENDING_EXCEPTION;
}
@ -582,6 +584,8 @@ Klass* ClassListParser::load_current_class(TRAPS) {
} else {
if (!HAS_PENDING_EXCEPTION) {
THROW_NULL(vmSymbols::java_lang_ClassNotFoundException());
} else {
ArchiveUtils::check_for_oom(PENDING_EXCEPTION); // exit on OOM
}
}
}
@ -590,6 +594,9 @@ Klass* ClassListParser::load_current_class(TRAPS) {
// If "source:" tag is specified, all super class and super interfaces must be specified in the
// class list file.
klass = load_class_from_source(class_name_symbol, CHECK_NULL);
if (HAS_PENDING_EXCEPTION) {
ArchiveUtils::check_for_oom(PENDING_EXCEPTION); // exit on OOM
}
}
if (klass != NULL && klass->is_instance_klass() && is_id_specified()) {

@ -319,3 +319,12 @@ void ArchiveUtils::log_to_classlist(BootstrapInfo* bootstrap_specifier, TRAPS) {
}
}
}
void ArchiveUtils::check_for_oom(oop exception) {
assert(exception != nullptr, "Sanity check");
if (exception->is_a(SystemDictionary::OutOfMemoryError_klass())) {
vm_exit_during_cds_dumping(
err_msg("Out of memory. Please run with a larger Java heap, current MaxHeapSize = " SIZE_FORMAT "M",
MaxHeapSize/M));
}
}

@ -243,6 +243,7 @@ public:
class ArchiveUtils {
public:
static void log_to_classlist(BootstrapInfo* bootstrap_specifier, TRAPS) NOT_CDS_RETURN;
static void check_for_oom(oop exception) NOT_CDS_RETURN;
};
#endif // SHARE_MEMORY_ARCHIVEUTILS_HPP

@ -259,15 +259,6 @@ void HeapShared::run_full_gc_in_vm_thread() {
void HeapShared::archive_java_heap_objects(GrowableArray<MemRegion> *closed,
GrowableArray<MemRegion> *open) {
if (!is_heap_object_archiving_allowed()) {
log_info(cds)(
"Archived java heap is not supported as UseG1GC, "
"UseCompressedOops and UseCompressedClassPointers are required."
"Current settings: UseG1GC=%s, UseCompressedOops=%s, UseCompressedClassPointers=%s.",
BOOL_TO_STR(UseG1GC), BOOL_TO_STR(UseCompressedOops),
BOOL_TO_STR(UseCompressedClassPointers));
return;
}
G1HeapVerifier::verify_ready_for_archiving();
@ -1035,7 +1026,13 @@ void HeapShared::init_subgraph_entry_fields(ArchivableStaticFieldInfo fields[],
TempNewSymbol field_name = SymbolTable::new_symbol(info->field_name);
Klass* k = SystemDictionary::resolve_or_null(klass_name, THREAD);
assert(k != NULL && !HAS_PENDING_EXCEPTION, "class must exist");
if (HAS_PENDING_EXCEPTION) {
ResourceMark rm(THREAD);
ArchiveUtils::check_for_oom(PENDING_EXCEPTION); // exit on OOM
log_info(cds)("%s: %s", PENDING_EXCEPTION->klass()->external_name(),
java_lang_String::as_utf8_string(java_lang_Throwable::message(PENDING_EXCEPTION)));
vm_exit_during_initialization("VM exits due to exception, use -Xlog:cds,exceptions=trace for detail");
}
InstanceKlass* ik = InstanceKlass::cast(k);
assert(InstanceKlass::cast(ik)->is_shared_boot_class(),
"Only support boot classes");
@ -1052,8 +1049,8 @@ void HeapShared::init_subgraph_entry_fields(ArchivableStaticFieldInfo fields[],
}
void HeapShared::init_subgraph_entry_fields(Thread* THREAD) {
assert(is_heap_object_archiving_allowed(), "Sanity check");
_dump_time_subgraph_info_table = new (ResourceObj::C_HEAP, mtClass)DumpTimeKlassSubGraphInfoTable();
init_subgraph_entry_fields(closed_archive_subgraph_entry_fields,
num_closed_archive_subgraph_entry_fields,
THREAD);
@ -1068,8 +1065,10 @@ void HeapShared::init_subgraph_entry_fields(Thread* THREAD) {
}
void HeapShared::init_for_dumping(Thread* THREAD) {
_dumped_interned_strings = new (ResourceObj::C_HEAP, mtClass)DumpedInternedStrings();
init_subgraph_entry_fields(THREAD);
if (is_heap_object_archiving_allowed()) {
_dumped_interned_strings = new (ResourceObj::C_HEAP, mtClass)DumpedInternedStrings();
init_subgraph_entry_fields(THREAD);
}
}
void HeapShared::archive_object_subgraphs(ArchivableStaticFieldInfo fields[],

@ -1166,6 +1166,15 @@ bool MetaspaceShared::try_link_class(InstanceKlass* ik, TRAPS) {
#if INCLUDE_CDS_JAVA_HEAP
void VM_PopulateDumpSharedSpace::dump_java_heap_objects() {
if(!HeapShared::is_heap_object_archiving_allowed()) {
log_info(cds)(
"Archived java heap is not supported as UseG1GC, "
"UseCompressedOops and UseCompressedClassPointers are required."
"Current settings: UseG1GC=%s, UseCompressedOops=%s, UseCompressedClassPointers=%s.",
BOOL_TO_STR(UseG1GC), BOOL_TO_STR(UseCompressedOops),
BOOL_TO_STR(UseCompressedClassPointers));
return;
}
// Find all the interned strings that should be dumped.
int i;
for (i = 0; i < _global_klass_objects->length(); i++) {

@ -325,6 +325,7 @@ hotspot_appcds_dynamic = \
-runtime/cds/appcds/dynamicArchive \
-runtime/cds/appcds/loaderConstraints/DynamicLoaderConstraintsTest.java \
-runtime/cds/appcds/javaldr/ArrayTest.java \
-runtime/cds/appcds/javaldr/ExceptionDuringDumpAtObjectsInitPhase.java \
-runtime/cds/appcds/javaldr/GCSharedStringsDuringDump.java \
-runtime/cds/appcds/javaldr/HumongousDuringDump.java \
-runtime/cds/appcds/javaldr/LockDuringDump.java \

@ -0,0 +1,86 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
/*
* @test
* @summary Out of memory When dumping the CDS archive
* @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds /test/hotspot/jtreg/runtime/cds/appcds/test-classes
* @requires vm.cds.archived.java.heap
* @requires vm.jvmti
* @run driver ExceptionDuringDumpAtObjectsInitPhase
*/
import jdk.test.lib.cds.CDSOptions;
import jdk.test.lib.process.OutputAnalyzer;
import jdk.test.lib.process.ProcessTools;
public class ExceptionDuringDumpAtObjectsInitPhase {
public static String appClasses[] = {
Hello.class.getName(),
};
public static String agentClasses[] = {
GCDuringDumpTransformer.class.getName(),
GCDuringDumpTransformer.MyCleaner.class.getName(),
};
public static void main(String[] args) throws Throwable {
String agentJar =
ClassFileInstaller.writeJar("GCDuringDumpTransformer.jar",
ClassFileInstaller.Manifest.fromSourceFile("GCDuringDumpTransformer.mf"),
agentClasses);
String appJar =
ClassFileInstaller.writeJar("GCDuringDumpApp.jar", appClasses);
String gcLog = Boolean.getBoolean("test.cds.verbose.gc") ?
"-Xlog:gc*=info,gc+region=trace,gc+alloc+region=debug" : "-showversion";
// 1. Test with exception
System.out.println("1. Exception during dump");
TestCommon.dump(appJar,
TestCommon.list(Hello.class.getName()),
"-XX:+UnlockDiagnosticVMOptions",
"-XX:+AllowArchivingWithJavaAgent",
"-javaagent:" + agentJar,
"-Xlog:cds,class+load",
"-Xmx32m",
"-Dtest.with.exception=true",
gcLog).shouldNotHaveExitValue(0)
.shouldContain("Preload Error: Failed to load jdk/internal/math/FDBigInteger")
.shouldContain("VM exits due to exception, use -Xlog:cds,exceptions=trace for detail");
// 2. Test with OOM
System.out.println("2. OOM during dump");
TestCommon.dump(appJar,
TestCommon.list(Hello.class.getName()),
"-XX:+UnlockDiagnosticVMOptions",
"-XX:+AllowArchivingWithJavaAgent",
"-javaagent:" + agentJar,
"-Dtest.with.oom=true",
"-Xlog:cds,class+load",
"-Xmx12M",
gcLog).shouldNotHaveExitValue(0)
.shouldContain("Out of memory. Please run with a larger Java heap, current MaxHeapSize");
}
}

@ -26,10 +26,16 @@ import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.Instrumentation;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.ref.Cleaner;
import java.util.ArrayList;
import java.util.List;
import java.security.ProtectionDomain;
public class GCDuringDumpTransformer implements ClassFileTransformer {
static boolean TEST_WITH_CLEANER = Boolean.getBoolean("test.with.cleaner");
static boolean TEST_WITH_EXCEPTION = Boolean.getBoolean("test.with.exception");
static boolean TEST_WITH_OOM = Boolean.getBoolean("test.with.oom");
static List<byte[]> waste = new ArrayList();
static Cleaner cleaner;
static Thread thread;
static Object garbage;
@ -45,6 +51,22 @@ public class GCDuringDumpTransformer implements ClassFileTransformer {
public byte[] transform(ClassLoader loader, String name, Class<?> classBeingRedefined,
ProtectionDomain pd, byte[] buffer) throws IllegalClassFormatException {
// jdk/internal/math/FDBigInteger is loaded as part of archived heap.
if (name.equals("jdk/internal/math/FDBigInteger")) {
System.out.println("Transforming class jdk/internal/math/FDBigInteger");
if (TEST_WITH_EXCEPTION) {
System.out.println("Return bad buffer for " + name);
return new byte[] {1, 2, 3, 4, 5, 6, 7, 8};
}
if (TEST_WITH_OOM) {
// fill until OOM
System.out.println("Fill objects until OOM");
for (;;) {
waste.add(new byte[64*1024]);
}
}
}
if (TEST_WITH_CLEANER) {
if (name.equals("Hello")) {
garbage = null;
@ -60,7 +82,6 @@ public class GCDuringDumpTransformer implements ClassFileTransformer {
} catch (Throwable t2) {}
}
}
return null;
}
@ -68,6 +89,8 @@ public class GCDuringDumpTransformer implements ClassFileTransformer {
public static void premain(String agentArguments, Instrumentation instrumentation) {
System.out.println("ClassFileTransformer.premain() is called: TEST_WITH_CLEANER = " + TEST_WITH_CLEANER);
System.out.println("ClassFileTransformer.premain() is called: TEST_WITH_EXCEPTION = " + TEST_WITH_EXCEPTION);
System.out.println("ClassFileTransformer.premain() is called: TEST_WITH_OOM = " + TEST_WITH_OOM);
instrumentation.addTransformer(new GCDuringDumpTransformer(), /*canRetransform=*/true);
savedInstrumentation = instrumentation;
}