8214455: Relocate CDS archived regions to the top of the G1 heap

Reviewed-by: tschatzl, ccheung
This commit is contained in:
Ioi Lam 2021-04-08 00:41:28 +00:00
parent 88eb2919f5
commit 78d1164ce2
8 changed files with 147 additions and 8 deletions
src/hotspot/share/memory
test
hotspot/jtreg/runtime/cds/appcds
jdk/java/io/BufferedInputStream

@ -814,8 +814,8 @@ class RelocateBufferToRequested : public BitMapClosure {
address top = _builder->buffer_top();
address new_bottom = bottom + _buffer_to_requested_delta;
address new_top = top + _buffer_to_requested_delta;
log_debug(cds)("Relocating archive from [" INTPTR_FORMAT " - " INTPTR_FORMAT " ] to "
"[" INTPTR_FORMAT " - " INTPTR_FORMAT " ]",
log_debug(cds)("Relocating archive from [" INTPTR_FORMAT " - " INTPTR_FORMAT "] to "
"[" INTPTR_FORMAT " - " INTPTR_FORMAT "]",
p2i(bottom), p2i(top),
p2i(new_bottom), p2i(new_top));
}

@ -215,6 +215,7 @@ void FileMapHeader::populate(FileMapInfo* mapinfo, size_t core_region_alignment)
_narrow_oop_mode = CompressedOops::mode();
_narrow_oop_base = CompressedOops::base();
_narrow_oop_shift = CompressedOops::shift();
_heap_begin = CompressedOops::begin();
_heap_end = CompressedOops::end();
}
_compressed_oops = UseCompressedOops;
@ -1811,6 +1812,8 @@ void FileMapInfo::map_heap_regions_impl() {
p2i(narrow_klass_base()), narrow_klass_shift());
log_info(cds)(" narrow_oop_mode = %d, narrow_oop_base = " PTR_FORMAT ", narrow_oop_shift = %d",
narrow_oop_mode(), p2i(narrow_oop_base()), narrow_oop_shift());
log_info(cds)(" heap range = [" PTR_FORMAT " - " PTR_FORMAT "]",
p2i(header()->heap_begin()), p2i(header()->heap_end()));
log_info(cds)("The current max heap size = " SIZE_FORMAT "M, HeapRegion::GrainBytes = " SIZE_FORMAT,
MaxHeapSize/M, HeapRegion::GrainBytes);
@ -1818,6 +1821,8 @@ void FileMapInfo::map_heap_regions_impl() {
p2i(CompressedKlassPointers::base()), CompressedKlassPointers::shift());
log_info(cds)(" narrow_oop_mode = %d, narrow_oop_base = " PTR_FORMAT ", narrow_oop_shift = %d",
CompressedOops::mode(), p2i(CompressedOops::base()), CompressedOops::shift());
log_info(cds)(" heap range = [" PTR_FORMAT " - " PTR_FORMAT "]",
p2i(CompressedOops::begin()), p2i(CompressedOops::end()));
if (narrow_klass_base() != CompressedKlassPointers::base() ||
narrow_klass_shift() != CompressedKlassPointers::shift()) {
@ -1828,15 +1833,18 @@ void FileMapInfo::map_heap_regions_impl() {
if (narrow_oop_mode() != CompressedOops::mode() ||
narrow_oop_base() != CompressedOops::base() ||
narrow_oop_shift() != CompressedOops::shift()) {
log_info(cds)("CDS heap data need to be relocated because the archive was created with an incompatible oop encoding mode.");
log_info(cds)("CDS heap data needs to be relocated because the archive was created with an incompatible oop encoding mode.");
_heap_pointers_need_patching = true;
} else {
MemRegion range = get_heap_regions_range_with_current_oop_encoding_mode();
if (!CompressedOops::is_in(range)) {
log_info(cds)("CDS heap data need to be relocated because");
log_info(cds)("CDS heap data needs to be relocated because");
log_info(cds)("the desired range " PTR_FORMAT " - " PTR_FORMAT, p2i(range.start()), p2i(range.end()));
log_info(cds)("is outside of the heap " PTR_FORMAT " - " PTR_FORMAT, p2i(CompressedOops::begin()), p2i(CompressedOops::end()));
_heap_pointers_need_patching = true;
} else if (header()->heap_end() != CompressedOops::end()) {
log_info(cds)("CDS heap data needs to be relocated to the end of the runtime heap to reduce fragmentation");
_heap_pointers_need_patching = true;
}
}
@ -1852,7 +1860,7 @@ void FileMapInfo::map_heap_regions_impl() {
// that they are now near the top of the runtime time. This can be done by
// the simple math of adding the delta as shown above.
address dumptime_heap_end = header()->heap_end();
address runtime_heap_end = (address)CompressedOops::end();
address runtime_heap_end = CompressedOops::end();
delta = runtime_heap_end - dumptime_heap_end;
}
@ -1868,7 +1876,7 @@ void FileMapInfo::map_heap_regions_impl() {
// open regions.
size_t align = size_t(relocated_closed_heap_region_bottom) % HeapRegion::GrainBytes;
delta -= align;
log_info(cds)("CDS heap data need to be relocated lower by a further " SIZE_FORMAT
log_info(cds)("CDS heap data needs to be relocated lower by a further " SIZE_FORMAT
" bytes to " INTX_FORMAT " to be aligned with HeapRegion::GrainBytes",
align, delta);
HeapShared::init_narrow_oop_decoding(narrow_oop_base() + delta, narrow_oop_shift());
@ -1996,6 +2004,7 @@ void FileMapInfo::patch_archived_heap_embedded_pointers() {
return;
}
log_info(cds)("patching heap embedded pointers");
patch_archived_heap_embedded_pointers(closed_archive_heap_ranges,
num_closed_archive_heap_ranges,
MetaspaceShared::first_closed_archive_heap_region);

@ -199,6 +199,7 @@ class FileMapHeader: private CDSFileMapHeaderBase {
bool _compressed_class_ptrs; // save the flag UseCompressedClassPointers
size_t _cloned_vtables_offset; // The address of the first cloned vtable
size_t _serialized_data_offset; // Data accessed using {ReadClosure,WriteClosure}::serialize()
address _heap_begin; // heap begin at dump time.
address _heap_end; // heap end at dump time.
bool _base_archive_is_default; // indicates if the base archive is the system default one
@ -262,6 +263,7 @@ public:
address narrow_klass_base() const { return (address)mapped_base_address(); }
char* cloned_vtables() const { return from_mapped_offset(_cloned_vtables_offset); }
char* serialized_data() const { return from_mapped_offset(_serialized_data_offset); }
address heap_begin() const { return _heap_begin; }
address heap_end() const { return _heap_end; }
bool base_archive_is_default() const { return _base_archive_is_default; }
const char* jvm_ident() const { return _jvm_ident; }

@ -346,6 +346,8 @@ void HeapShared::archive_java_heap_objects(GrowableArray<MemRegion>* closed,
// Cache for recording where the archived objects are copied to
create_archived_object_cache();
log_info(cds)("Heap range = [" PTR_FORMAT " - " PTR_FORMAT "]",
p2i(CompressedOops::begin()), p2i(CompressedOops::end()));
log_info(cds)("Dumping objects to closed archive heap region ...");
copy_closed_archive_heap_objects(closed);

@ -452,6 +452,12 @@ public class TestCommon extends CDSTestUtils {
return new Result(opts, runWithArchive(opts));
}
public static Result runWithoutCDS(String... suffix) throws Exception {
AppCDSOptions opts = (new AppCDSOptions());
opts.addSuffix(suffix).setXShareMode("off");;
return new Result(opts, runWithArchive(opts));
}
public static Result runWithRelativePath(String jarDir, String... suffix) throws Exception {
AppCDSOptions opts = (new AppCDSOptions());
opts.setAppJarDir(jarDir);

@ -0,0 +1,33 @@
/*
* 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.
*
*/
public class HeapFragmentationApp {
public static void main(String args[]) throws Exception {
int BUF_SIZE = Integer.parseInt(args[0]);
System.out.println("allocating byte[" + BUF_SIZE + "]");
byte[] array = new byte[BUF_SIZE];
System.out.println("array = " + array);
System.out.println("array.length = " + array.length);
}
}

@ -0,0 +1,86 @@
/*
* 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
* @summary Relocate CDS archived regions to the top of the G1 heap
* @bug 8214455
* @requires vm.cds.archived.java.heap
* @requires (sun.arch.data.model == "64" & os.maxMemory > 4g)
* @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds
* @build HeapFragmentationApp
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar HeapFragmentationApp.jar HeapFragmentationApp
* @run driver HeapFragmentationTest
*/
import jdk.test.lib.process.OutputAnalyzer;
import jdk.test.lib.helpers.ClassFileInstaller;
public class HeapFragmentationTest {
public static void main(String[] args) throws Exception {
String mainClass = "HeapFragmentationApp";
String appJar = ClassFileInstaller.getJarPath(mainClass + ".jar");
String appClasses[] = TestCommon.list(mainClass);
// We run with a 1400m heap, so we should be able to allocate a 1000m buffer, regardless
// of the heap size chosen at dump time.
String dumpTimeHeapSize = "-Xmx800m";
String runTimeHeapSize = "-Xmx1400m";
String BUFF_SIZE = Integer.toString(1000 * 1024 * 1024);
String successOutput = "array.length = " + BUFF_SIZE;
// On Linux/x64, this would (usually, if not affected by ASLR) force the low end of
// the heap to be at 0x600000000. Thus, the heap ranges would be
//
// dump time: [0x600000000 ... 0x600000000 + 800m]
// [AHR] <- archived heap regions are dumped at here
//
// run time: [0x600000000 ............... 0x600000000 + 1400m]
// [AHR] <- archived heap regions are moved to here
// |<---- max allocatable size ----->|
String heapBase = "-XX:HeapBaseMinAddress=0x600000000";
TestCommon.dump(appJar, appClasses,
heapBase,
dumpTimeHeapSize, "-Xlog:cds=debug");
String execArgs[] = TestCommon.concat("-cp", appJar,
"-showversion",
heapBase,
"-Xlog:cds=debug",
"-Xlog:gc+heap+exit",
"-Xlog:gc,gc+heap,gc+ergo+heap",
"-XX:+CrashOnOutOfMemoryError",
"-XX:+IgnoreUnrecognizedVMOptions",
"-XX:+G1ExitOnExpansionFailure");
// First, make sure the test runs without CDS
TestCommon.runWithoutCDS(TestCommon.concat(execArgs, runTimeHeapSize, mainClass, BUFF_SIZE))
.assertNormalExit(successOutput);
// Run with CDS. The archived heap regions should be relocated to avoid fragmentation.
TestCommon.run(TestCommon.concat(execArgs, runTimeHeapSize, mainClass, BUFF_SIZE))
.assertNormalExit(successOutput);
}
}

@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013, 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
@ -23,11 +23,12 @@
/* @test
* @bug 7129312
* @requires (sun.arch.data.model == "64" & os.maxMemory > 4g)
* @requires (sun.arch.data.model == "64" & os.maxMemory > 4g)
* @summary BufferedInputStream calculates negative array size with large
* streams and mark
* @run main/othervm -Xmx4G -Xlog:gc,gc+heap,gc+ergo+heap -XX:+CrashOnOutOfMemoryError
-XX:+IgnoreUnrecognizedVMOptions -XX:+G1ExitOnExpansionFailure
-Xlog:cds=debug
LargeCopyWithMark
*/