8214388: CDS dumping fails with java heap fragmentation
Force a full GC with a single thread before writing heap archive regions Reviewed-by: sjohanss, jiangli
This commit is contained in:
parent
ea532aa075
commit
a0a108fb01
src/hotspot/share
classfile
gc
memory
test/hotspot/jtreg/runtime/appcds
@ -375,6 +375,9 @@ public:
|
||||
inline int remain() {
|
||||
return (int)(_end - _p);
|
||||
}
|
||||
int last_line_no() {
|
||||
return _line_no - 1;
|
||||
}
|
||||
|
||||
void corrupted(const char *p, const char *msg);
|
||||
|
||||
|
@ -81,6 +81,13 @@ void G1Arguments::initialize() {
|
||||
vm_exit_during_initialization("The flag -XX:+UseG1GC can not be combined with -XX:ParallelGCThreads=0", NULL);
|
||||
}
|
||||
|
||||
// When dumping the CDS archive we want to reduce fragmentation by
|
||||
// triggering a full collection. To get as low fragmentation as
|
||||
// possible we only use one worker thread.
|
||||
if (DumpSharedSpaces) {
|
||||
FLAG_SET_ERGO(uint, ParallelGCThreads, 1);
|
||||
}
|
||||
|
||||
if (FLAG_IS_DEFAULT(G1ConcRefinementThreads)) {
|
||||
FLAG_SET_ERGO(uint, G1ConcRefinementThreads, ParallelGCThreads);
|
||||
}
|
||||
|
@ -273,6 +273,69 @@ public:
|
||||
};
|
||||
|
||||
// Should be only used at CDS dump time
|
||||
class VerifyReadyForArchivingRegionClosure : public HeapRegionClosure {
|
||||
bool _seen_free;
|
||||
bool _has_holes;
|
||||
bool _has_unexpected_holes;
|
||||
bool _has_humongous;
|
||||
public:
|
||||
bool has_holes() {return _has_holes;}
|
||||
bool has_unexpected_holes() {return _has_unexpected_holes;}
|
||||
bool has_humongous() {return _has_humongous;}
|
||||
|
||||
VerifyReadyForArchivingRegionClosure() : HeapRegionClosure() {
|
||||
_seen_free = false;
|
||||
_has_holes = false;
|
||||
_has_unexpected_holes = false;
|
||||
_has_humongous = false;
|
||||
}
|
||||
virtual bool do_heap_region(HeapRegion* hr) {
|
||||
const char* hole = "";
|
||||
|
||||
if (hr->is_free()) {
|
||||
_seen_free = true;
|
||||
} else {
|
||||
if (_seen_free) {
|
||||
_has_holes = true;
|
||||
if (hr->is_humongous()) {
|
||||
hole = " hole";
|
||||
} else {
|
||||
_has_unexpected_holes = true;
|
||||
hole = " hole **** unexpected ****";
|
||||
}
|
||||
}
|
||||
}
|
||||
if (hr->is_humongous()) {
|
||||
_has_humongous = true;
|
||||
}
|
||||
log_info(gc, region, cds)("HeapRegion " INTPTR_FORMAT " %s%s", p2i(hr->bottom()), hr->get_type_str(), hole);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// We want all used regions to be moved to the bottom-end of the heap, so we have
|
||||
// a contiguous range of free regions at the top end of the heap. This way, we can
|
||||
// avoid fragmentation while allocating the archive regions.
|
||||
//
|
||||
// Before calling this, a full GC should have been executed with a single worker thread,
|
||||
// so that no old regions would be moved to the middle of the heap.
|
||||
void G1HeapVerifier::verify_ready_for_archiving() {
|
||||
VerifyReadyForArchivingRegionClosure cl;
|
||||
G1CollectedHeap::heap()->heap_region_iterate(&cl);
|
||||
if (cl.has_holes()) {
|
||||
log_warning(gc, verify)("All free regions should be at the top end of the heap, but"
|
||||
" we found holes. This is probably caused by (unmovable) humongous"
|
||||
" allocations, and may lead to fragmentation while"
|
||||
" writing archive heap memory regions.");
|
||||
}
|
||||
if (cl.has_humongous()) {
|
||||
log_warning(gc, verify)("(Unmovable) humongous regions have been found and"
|
||||
" may lead to fragmentation while"
|
||||
" writing archive heap memory regions.");
|
||||
}
|
||||
assert(!cl.has_unexpected_holes(), "all holes should have been caused by humongous regions");
|
||||
}
|
||||
|
||||
class VerifyArchivePointerRegionClosure: public HeapRegionClosure {
|
||||
private:
|
||||
G1CollectedHeap* _g1h;
|
||||
|
@ -115,6 +115,7 @@ public:
|
||||
void verify_dirty_region(HeapRegion* hr) PRODUCT_RETURN;
|
||||
void verify_dirty_young_regions() PRODUCT_RETURN;
|
||||
|
||||
static void verify_ready_for_archiving();
|
||||
static void verify_archive_regions();
|
||||
};
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2002, 2018, 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
|
||||
@ -60,6 +60,9 @@ const char* GCCause::to_string(GCCause::Cause cause) {
|
||||
case _wb_full_gc:
|
||||
return "WhiteBox Initiated Full GC";
|
||||
|
||||
case _archive_time_gc:
|
||||
return "Full GC for -Xshare:dump";
|
||||
|
||||
case _no_gc:
|
||||
return "No GC";
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2002, 2018, 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
|
||||
@ -52,6 +52,7 @@ class GCCause : public AllStatic {
|
||||
_wb_young_gc,
|
||||
_wb_conc_mark,
|
||||
_wb_full_gc,
|
||||
_archive_time_gc,
|
||||
|
||||
/* implementation independent, but reserved for GC use */
|
||||
_no_gc,
|
||||
|
@ -683,7 +683,7 @@ size_t FileMapInfo::write_archive_heap_regions(GrowableArray<MemRegion> *heap_me
|
||||
int arr_len = heap_mem == NULL ? 0 : heap_mem->length();
|
||||
if(arr_len > max_num_regions) {
|
||||
fail_stop("Unable to write archive heap memory regions: "
|
||||
"number of memory regions exceeds maximum due to fragmentation."
|
||||
"number of memory regions exceeds maximum due to fragmentation. "
|
||||
"Please increase java heap size "
|
||||
"(current MaxHeapSize is " SIZE_FORMAT ", InitialHeapSize is " SIZE_FORMAT ").",
|
||||
MaxHeapSize, InitialHeapSize);
|
||||
|
@ -193,6 +193,8 @@ void HeapShared::archive_java_heap_objects(GrowableArray<MemRegion> *closed,
|
||||
return;
|
||||
}
|
||||
|
||||
G1HeapVerifier::verify_ready_for_archiving();
|
||||
|
||||
{
|
||||
NoSafepointVerifier nsv;
|
||||
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include "classfile/classLoaderExt.hpp"
|
||||
#include "classfile/dictionary.hpp"
|
||||
#include "classfile/loaderConstraints.hpp"
|
||||
#include "classfile/javaClasses.inline.hpp"
|
||||
#include "classfile/placeholders.hpp"
|
||||
#include "classfile/symbolTable.hpp"
|
||||
#include "classfile/stringTable.hpp"
|
||||
@ -350,7 +351,11 @@ void MetaspaceShared::post_initialize(TRAPS) {
|
||||
}
|
||||
}
|
||||
|
||||
static GrowableArray<Handle>* _extra_interned_strings = NULL;
|
||||
|
||||
void MetaspaceShared::read_extra_data(const char* filename, TRAPS) {
|
||||
_extra_interned_strings = new (ResourceObj::C_HEAP, mtInternal)GrowableArray<Handle>(10000, true);
|
||||
|
||||
HashtableTextDump reader(filename);
|
||||
reader.check_version("VERSION: 1.0");
|
||||
|
||||
@ -358,15 +363,45 @@ void MetaspaceShared::read_extra_data(const char* filename, TRAPS) {
|
||||
int utf8_length;
|
||||
int prefix_type = reader.scan_prefix(&utf8_length);
|
||||
ResourceMark rm(THREAD);
|
||||
char* utf8_buffer = NEW_RESOURCE_ARRAY(char, utf8_length);
|
||||
if (utf8_length == 0x7fffffff) {
|
||||
// buf_len will overflown 32-bit value.
|
||||
vm_exit_during_initialization(err_msg("string length too large: %d", utf8_length));
|
||||
}
|
||||
int buf_len = utf8_length+1;
|
||||
char* utf8_buffer = NEW_RESOURCE_ARRAY(char, buf_len);
|
||||
reader.get_utf8(utf8_buffer, utf8_length);
|
||||
utf8_buffer[utf8_length] = '\0';
|
||||
|
||||
if (prefix_type == HashtableTextDump::SymbolPrefix) {
|
||||
SymbolTable::new_symbol(utf8_buffer, utf8_length, THREAD);
|
||||
SymbolTable::new_permanent_symbol(utf8_buffer, THREAD);
|
||||
} else{
|
||||
assert(prefix_type == HashtableTextDump::StringPrefix, "Sanity");
|
||||
utf8_buffer[utf8_length] = '\0';
|
||||
oop s = StringTable::intern(utf8_buffer, THREAD);
|
||||
|
||||
if (HAS_PENDING_EXCEPTION) {
|
||||
log_warning(cds, heap)("[line %d] extra interned string allocation failed; size too large: %d",
|
||||
reader.last_line_no(), utf8_length);
|
||||
CLEAR_PENDING_EXCEPTION;
|
||||
} else {
|
||||
#if INCLUDE_G1GC
|
||||
if (UseG1GC) {
|
||||
typeArrayOop body = java_lang_String::value(s);
|
||||
const HeapRegion* hr = G1CollectedHeap::heap()->heap_region_containing(body);
|
||||
if (hr->is_humongous()) {
|
||||
// Don't keep it alive, so it will be GC'ed before we dump the strings, in order
|
||||
// to maximize free heap space and minimize fragmentation.
|
||||
log_warning(cds, heap)("[line %d] extra interned string ignored; size too large: %d",
|
||||
reader.last_line_no(), utf8_length);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
// Interned strings are GC'ed if there are no references to it, so let's
|
||||
// add a reference to keep this string alive.
|
||||
assert(s != NULL, "must succeed");
|
||||
Handle h(THREAD, s);
|
||||
_extra_interned_strings->append(h);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -451,8 +486,6 @@ address MetaspaceShared::cds_i2i_entry_code_buffers(size_t total_size) {
|
||||
return _cds_i2i_entry_code_buffers;
|
||||
}
|
||||
|
||||
// CDS code for dumping shared archive.
|
||||
|
||||
// Global object for holding classes that have been loaded. Since this
|
||||
// is run at a safepoint just before exit, this is the entire set of classes.
|
||||
static GrowableArray<Klass*>* _global_klass_objects;
|
||||
@ -1686,6 +1719,13 @@ void MetaspaceShared::preload_and_dump(TRAPS) {
|
||||
link_and_cleanup_shared_classes(CATCH);
|
||||
tty->print_cr("Rewriting and linking classes: done");
|
||||
|
||||
if (HeapShared::is_heap_object_archiving_allowed()) {
|
||||
// Avoid fragmentation while archiving heap objects.
|
||||
Universe::heap()->soft_ref_policy()->set_should_clear_all_soft_refs(true);
|
||||
Universe::heap()->collect(GCCause::_archive_time_gc);
|
||||
Universe::heap()->soft_ref_policy()->set_should_clear_all_soft_refs(false);
|
||||
}
|
||||
|
||||
VM_PopulateDumpSharedSpace op;
|
||||
VMThread::execute(&op);
|
||||
}
|
||||
|
@ -57,16 +57,12 @@ public class LotsOfClasses {
|
||||
opts.addSuffix("--add-modules");
|
||||
opts.addSuffix("ALL-SYSTEM");
|
||||
opts.addSuffix("-Xlog:hashtables");
|
||||
opts.addSuffix("-Xms500m");
|
||||
opts.addSuffix("-Xmx500m");
|
||||
opts.addSuffix("-Xlog:gc+region+cds");
|
||||
opts.addSuffix("-Xlog:gc+region=trace");
|
||||
|
||||
OutputAnalyzer out = CDSTestUtils.createArchive(opts);
|
||||
try {
|
||||
CDSTestUtils.checkDump(out);
|
||||
} catch (java.lang.RuntimeException re) {
|
||||
out.shouldContain(
|
||||
"number of memory regions exceeds maximum due to fragmentation");
|
||||
}
|
||||
CDSTestUtils.checkDump(out);
|
||||
}
|
||||
|
||||
static void findAllClasses(ArrayList<String> list) throws Throwable {
|
||||
|
@ -146,8 +146,11 @@ public class ArchivedIntegerCacheTest {
|
||||
"-Xmx1g",
|
||||
"-XX:NewSize=1g",
|
||||
"-Xlog:cds+heap=info",
|
||||
"-Xlog:gc+region+cds",
|
||||
"-Xlog:gc+region=trace",
|
||||
use_whitebox_jar);
|
||||
TestCommon.checkDump(output,
|
||||
"Cannot archive the sub-graph referenced from [Ljava.lang.Integer; object");
|
||||
"Cannot archive the sub-graph referenced from [Ljava.lang.Integer; object",
|
||||
"humongous regions have been found and may lead to fragmentation");
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright (c) 2018, 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 Test how CDS dumping handles the existence of humongous G1 regions.
|
||||
* @library /test/lib /test/hotspot/jtreg/runtime/appcds /test/hotspot/jtreg/runtime/appcds/test-classes
|
||||
* @requires vm.cds.archived.java.heap
|
||||
* @requires vm.flavor != "minimal"
|
||||
* @modules java.base/jdk.internal.misc
|
||||
* jdk.jartool/sun.tools.jar
|
||||
* java.management
|
||||
* @build HumongousDuringDumpTransformer Hello
|
||||
* @run main/othervm/timeout=240 HumongousDuringDump
|
||||
*/
|
||||
|
||||
import jdk.test.lib.cds.CDSOptions;
|
||||
import jdk.test.lib.process.OutputAnalyzer;
|
||||
import jdk.test.lib.process.ProcessTools;
|
||||
|
||||
public class HumongousDuringDump {
|
||||
public static String appClasses[] = {
|
||||
"Hello",
|
||||
};
|
||||
public static String agentClasses[] = {
|
||||
"HumongousDuringDumpTransformer",
|
||||
};
|
||||
|
||||
public static void main(String[] args) throws Throwable {
|
||||
String agentJar =
|
||||
ClassFileInstaller.writeJar("HumongousDuringDumpTransformer.jar",
|
||||
ClassFileInstaller.Manifest.fromSourceFile("HumongousDuringDumpTransformer.mf"),
|
||||
agentClasses);
|
||||
|
||||
String appJar =
|
||||
ClassFileInstaller.writeJar("HumongousDuringDumpApp.jar", appClasses);
|
||||
|
||||
String gcLog = Boolean.getBoolean("test.cds.verbose.gc") ?
|
||||
"-Xlog:gc*=info,gc+region=trace,gc+alloc+region=debug" : "-showversion";
|
||||
|
||||
String extraArg = "-javaagent:" + agentJar;
|
||||
String extraOption = "-XX:+AllowArchivingWithJavaAgent";
|
||||
|
||||
OutputAnalyzer out =
|
||||
TestCommon.testDump(appJar, TestCommon.list("Hello"),
|
||||
"-XX:+UnlockDiagnosticVMOptions", extraOption,
|
||||
"-Xlog:gc+region+cds",
|
||||
"-Xlog:gc+region=trace",
|
||||
extraArg, "-Xmx64m", gcLog);
|
||||
out.shouldContain("(Unmovable) humongous regions have been found and may lead to fragmentation");
|
||||
out.shouldContain("All free regions should be at the top end of the heap, but we found holes.");
|
||||
out.shouldMatch("gc,region,cds. HeapRegion .* HUM. hole");
|
||||
String pattern = "gc,region,cds. HeapRegion .*hole";
|
||||
out.shouldMatch(pattern);
|
||||
out.shouldNotMatch(pattern + ".*unexpected");
|
||||
|
||||
TestCommon.run(
|
||||
"-cp", appJar,
|
||||
"-verbose",
|
||||
"-Xmx64m",
|
||||
"-XX:+PrintSharedSpaces",
|
||||
"-XX:+UnlockDiagnosticVMOptions", extraOption,
|
||||
gcLog,
|
||||
"Hello")
|
||||
.assertNormalExit();
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Copyright (c) 2018, 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.lang.instrument.ClassFileTransformer;
|
||||
import java.lang.instrument.Instrumentation;
|
||||
import java.lang.instrument.IllegalClassFormatException;
|
||||
import java.security.ProtectionDomain;
|
||||
|
||||
// This test is sensitive to -Xmx. It must be run with -xmx64m.
|
||||
// Running with a different -Xmx requires changing the parameters and careful re-testing.
|
||||
public class HumongousDuringDumpTransformer implements ClassFileTransformer {
|
||||
public byte[] transform(ClassLoader loader, String name, Class<?> classBeingRedefined,
|
||||
ProtectionDomain pd, byte[] buffer) throws IllegalClassFormatException {
|
||||
if (name.equals("Hello")) {
|
||||
try {
|
||||
makeHumongousRegions();
|
||||
} catch (Throwable t) {
|
||||
array = null;
|
||||
humon = null;
|
||||
System.out.println("Unexpected error: " + t);
|
||||
t.printStackTrace();
|
||||
}
|
||||
}
|
||||
array = null;
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Instrumentation savedInstrumentation;
|
||||
|
||||
public static void premain(String agentArguments, Instrumentation instrumentation) {
|
||||
long xmx = Runtime.getRuntime().maxMemory();
|
||||
if (xmx < 60 * 1024 * 1024 || xmx > 80 * 1024 * 1024) {
|
||||
System.out.println("Running with incorrect heap size: " + xmx);
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
System.out.println("ClassFileTransformer.premain() is called");
|
||||
instrumentation.addTransformer(new HumongousDuringDumpTransformer(), /*canRetransform=*/true);
|
||||
savedInstrumentation = instrumentation;
|
||||
}
|
||||
|
||||
public static Instrumentation getInstrumentation() {
|
||||
return savedInstrumentation;
|
||||
}
|
||||
|
||||
public static void agentmain(String args, Instrumentation inst) throws Exception {
|
||||
premain(args, inst);
|
||||
}
|
||||
|
||||
Object[] array;
|
||||
|
||||
static final int DUMMY_SIZE = 4096 - 16 - 8;
|
||||
static final int HUMON_SIZE = 4 * 1024 * 1024 - 16 - 8;
|
||||
static final int SKIP = 13;
|
||||
|
||||
byte humon[] = null;
|
||||
boolean first = true;
|
||||
|
||||
public synchronized void makeHumongousRegions() {
|
||||
if (!first) {
|
||||
return;
|
||||
}
|
||||
System.out.println("===============================================================================");
|
||||
first = false;
|
||||
|
||||
int total = 0;
|
||||
array = new Object[100000];
|
||||
System.out.println(array);
|
||||
|
||||
// (1) Allocate about 8MB of old objects.
|
||||
for (int n=0, i=0; total < 8 * 1024 * 1024; n++) {
|
||||
// Make enough allocations to cause a GC (for 64MB heap) to create
|
||||
// old regions.
|
||||
//
|
||||
// But don't completely fill up the heap. That would cause OOM and
|
||||
// may not be handled gracefully inside class transformation!
|
||||
Object x = new byte[DUMMY_SIZE];
|
||||
if ((n % SKIP) == 0) {
|
||||
array[i++] = x;
|
||||
total += DUMMY_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
System.gc();
|
||||
|
||||
// (2) Now allocate a humongous array. It will sit above the 8MB of old regions.
|
||||
humon = new byte[HUMON_SIZE];
|
||||
array = null;
|
||||
System.gc();
|
||||
}
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
Manifest-Version: 1.0
|
||||
Premain-Class: HumongousDuringDumpTransformer
|
||||
Agent-Class: HumongousDuringDumpTransformer
|
||||
Can-Retransform-Classes: true
|
||||
Can-Redefine-Classes: true
|
@ -57,6 +57,7 @@ public class InvalidFileFormat {
|
||||
test("OverflowPrefix.txt", "Num overflow. Corrupted at line 4");
|
||||
test("UnrecognizedPrefix.txt", "Unrecognized format. Corrupted at line 5");
|
||||
test("TruncatedString.txt", "Truncated. Corrupted at line 3");
|
||||
test("LengthOverflow.txt", "string length too large: 2147483647");
|
||||
}
|
||||
|
||||
private static void
|
||||
|
@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright (c) 2018, 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 Use a shared string allocated in a humongous G1 region.
|
||||
* @comment -- the following implies that G1 is used (by command-line or by default)
|
||||
* @requires vm.cds.archived.java.heap
|
||||
*
|
||||
* @library /test/hotspot/jtreg/runtime/appcds /test/lib
|
||||
* @modules jdk.jartool/sun.tools.jar
|
||||
* @build HelloString
|
||||
* @build sun.hotspot.WhiteBox
|
||||
* @run driver ClassFileInstaller sun.hotspot.WhiteBox sun.hotspot.WhiteBox$WhiteBoxPermission
|
||||
* @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. SharedStringsHumongous
|
||||
*/
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.PrintWriter;
|
||||
import sun.hotspot.WhiteBox;
|
||||
import jdk.test.lib.process.OutputAnalyzer;
|
||||
import jdk.test.lib.process.ProcessTools;
|
||||
import jdk.test.lib.Asserts;
|
||||
|
||||
public class SharedStringsHumongous {
|
||||
static String sharedArchiveConfigFile = System.getProperty("user.dir") + File.separator + "SharedStringsHumongous_gen.txt";
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
WhiteBox wb = WhiteBox.getWhiteBox();
|
||||
|
||||
try (FileOutputStream fos = new FileOutputStream(sharedArchiveConfigFile)) {
|
||||
PrintWriter out = new PrintWriter(new OutputStreamWriter(fos));
|
||||
out.println("VERSION: 1.0");
|
||||
out.println("@SECTION: String");
|
||||
out.println("31: shared_test_string_unique_14325");
|
||||
int region_size = wb.g1RegionSize();
|
||||
char body[] = new char[region_size + region_size / 2];
|
||||
for (int i = 0; i < body.length; i++) {
|
||||
body[i] = 'x';
|
||||
}
|
||||
Asserts.assertTrue(wb.g1IsHumongous(body));
|
||||
String prefix = "generated_string (followed by " + body.length + " 'x') ";
|
||||
|
||||
System.out.println("G1 region size: " + region_size);
|
||||
System.out.println("Using a humongous string: " + prefix);
|
||||
|
||||
String s = prefix + new String(body);
|
||||
out.println(s.length() + ": " + s);
|
||||
out.close();
|
||||
}
|
||||
|
||||
SharedStringsUtils.run(args, SharedStringsHumongous::test);
|
||||
}
|
||||
|
||||
public static void test(String[] args) throws Exception {
|
||||
String vmOptionsPrefix[] = SharedStringsUtils.getChildVMOptionsPrefix();
|
||||
String appJar = JarBuilder.build("SharedStringsHumongous", "HelloString");
|
||||
|
||||
OutputAnalyzer dumpOutput = TestCommon.dump(appJar, TestCommon.list("HelloString"),
|
||||
TestCommon.concat(vmOptionsPrefix,
|
||||
"-XX:SharedArchiveConfigFile=" + sharedArchiveConfigFile,
|
||||
"-Xlog:gc+region+cds",
|
||||
"-Xlog:gc+region=trace"));
|
||||
TestCommon.checkDump(dumpOutput, "extra interned string ignored; size too large");
|
||||
// Extra strings that are humongous are not kelp alive, so they should be GC'ed
|
||||
// before dumping the string table. That means the heap should contain no
|
||||
// humongous regions.
|
||||
dumpOutput.shouldNotMatch("gc,region,cds. HeapRegion 0x[0-9a-f]* HUM");
|
||||
|
||||
OutputAnalyzer execOutput = TestCommon.exec(appJar,
|
||||
TestCommon.concat(vmOptionsPrefix, "HelloString"));
|
||||
TestCommon.checkExec(execOutput);
|
||||
}
|
||||
}
|
@ -29,7 +29,7 @@
|
||||
* @library /test/hotspot/jtreg/runtime/appcds /test/lib
|
||||
* @modules jdk.jartool/sun.tools.jar
|
||||
* @build HelloString
|
||||
* @run driver SharedStringsStress
|
||||
* @run driver/timeout=500 SharedStringsStress
|
||||
*/
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
@ -39,34 +39,56 @@ import jdk.test.lib.process.OutputAnalyzer;
|
||||
import jdk.test.lib.process.ProcessTools;
|
||||
|
||||
public class SharedStringsStress {
|
||||
static String sharedArchiveConfigFile = System.getProperty("user.dir") + File.separator + "SharedStringsStress_gen.txt";
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
SharedStringsUtils.run(args, SharedStringsStress::test);
|
||||
}
|
||||
|
||||
public static void test(String[] args) throws Exception {
|
||||
String vmOptionsPrefix[] = SharedStringsUtils.getChildVMOptionsPrefix();
|
||||
|
||||
String appJar = JarBuilder.build("SharedStringsStress", "HelloString");
|
||||
|
||||
String sharedArchiveConfigFile = System.getProperty("user.dir") + File.separator + "SharedStringsStress_gen.txt";
|
||||
try (FileOutputStream fos = new FileOutputStream(sharedArchiveConfigFile)) {
|
||||
PrintWriter out = new PrintWriter(new OutputStreamWriter(fos));
|
||||
out.println("VERSION: 1.0");
|
||||
out.println("@SECTION: String");
|
||||
out.println("31: shared_test_string_unique_14325");
|
||||
for (int i=0; i<100000; i++) {
|
||||
for (int i=0; i<200000; i++) {
|
||||
String s = "generated_string " + i;
|
||||
out.println(s.length() + ": " + s);
|
||||
}
|
||||
out.close();
|
||||
}
|
||||
|
||||
OutputAnalyzer dumpOutput = TestCommon.dump(appJar, TestCommon.list("HelloString"),
|
||||
TestCommon.concat(vmOptionsPrefix,
|
||||
"-XX:SharedArchiveConfigFile=" + sharedArchiveConfigFile));
|
||||
TestCommon.checkDump(dumpOutput);
|
||||
OutputAnalyzer execOutput = TestCommon.exec(appJar,
|
||||
TestCommon.concat(vmOptionsPrefix, "HelloString"));
|
||||
TestCommon.checkExec(execOutput);
|
||||
SharedStringsUtils.run(args, SharedStringsStress::test);
|
||||
}
|
||||
|
||||
public static void test(String[] args) throws Exception {
|
||||
String vmOptionsPrefix[] = SharedStringsUtils.getChildVMOptionsPrefix();
|
||||
String appJar = JarBuilder.build("SharedStringsStress", "HelloString");
|
||||
|
||||
String test_cases[][] = {
|
||||
// default heap size
|
||||
{},
|
||||
|
||||
// Test for handling of heap fragmentation. With sharedArchiveConfigFile, we will dump about
|
||||
// 18MB of shared objects on 64 bit VM (smaller on 32-bit).
|
||||
//
|
||||
// During dump time, an extra copy of these objects are allocated,
|
||||
// so we need about 36MB, plus a few MB for other system data. So 64MB total heap
|
||||
// should be enough.
|
||||
//
|
||||
// The VM should executed a full GC to maximize contiguous free space and
|
||||
// avoid fragmentation.
|
||||
{"-Xmx64m"},
|
||||
};
|
||||
|
||||
for (String[] extra_opts: test_cases) {
|
||||
vmOptionsPrefix = TestCommon.concat(vmOptionsPrefix, extra_opts);
|
||||
|
||||
OutputAnalyzer dumpOutput = TestCommon.dump(appJar, TestCommon.list("HelloString"),
|
||||
TestCommon.concat(vmOptionsPrefix,
|
||||
"-XX:SharedArchiveConfigFile=" + sharedArchiveConfigFile,
|
||||
"-Xlog:gc+region+cds",
|
||||
"-Xlog:gc+region=trace"));
|
||||
TestCommon.checkDump(dumpOutput);
|
||||
OutputAnalyzer execOutput = TestCommon.exec(appJar,
|
||||
TestCommon.concat(vmOptionsPrefix, "HelloString"));
|
||||
TestCommon.checkExec(execOutput);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,10 @@
|
||||
VERSION: 1.0
|
||||
@SECTION: String
|
||||
2147483647: s
|
||||
5: cp819
|
||||
31: shared_test_string_intern_12345
|
||||
7: test123
|
||||
@SECTION: Symbol
|
||||
41 -1: (Ljava/util/Set<TE;>;Ljava/lang/Object;)V
|
||||
10 -1: linkMethod
|
||||
20 -1: isAlphaNumericString
|
@ -1,6 +1,6 @@
|
||||
VERSION: 1.0
|
||||
@SECTION: String
|
||||
2147483647: s
|
||||
40000: s
|
||||
5: cp819
|
||||
31: shared_test_string_intern_12345
|
||||
7: test123
|
||||
|
Loading…
x
Reference in New Issue
Block a user