8273508: Support archived heap objects in SerialGC

Reviewed-by: tschatzl, ccheung
This commit is contained in:
Ioi Lam 2021-09-28 06:23:47 +00:00
parent 3eca9c36a6
commit 6a573b888d
13 changed files with 211 additions and 33 deletions

View file

@ -1597,8 +1597,9 @@ MapArchiveResult FileMapInfo::map_regions(int regions[], int num_regions, char*
return MAP_ARCHIVE_SUCCESS; return MAP_ARCHIVE_SUCCESS;
} }
bool FileMapInfo::read_region(int i, char* base, size_t size) { bool FileMapInfo::read_region(int i, char* base, size_t size, bool do_commit) {
FileMapRegion* si = space_at(i); FileMapRegion* si = space_at(i);
if (do_commit) {
log_info(cds)("Commit %s region #%d at base " INTPTR_FORMAT " top " INTPTR_FORMAT " (%s)%s", log_info(cds)("Commit %s region #%d at base " INTPTR_FORMAT " top " INTPTR_FORMAT " (%s)%s",
is_static() ? "static " : "dynamic", i, p2i(base), p2i(base + size), is_static() ? "static " : "dynamic", i, p2i(base), p2i(base + size),
shared_region_name[i], si->allow_exec() ? " exec" : ""); shared_region_name[i], si->allow_exec() ? " exec" : "");
@ -1607,6 +1608,7 @@ bool FileMapInfo::read_region(int i, char* base, size_t size) {
i, shared_region_name[i]); i, shared_region_name[i]);
return false; return false;
} }
}
if (lseek(_fd, (long)si->file_offset(), SEEK_SET) != (int)si->file_offset() || if (lseek(_fd, (long)si->file_offset(), SEEK_SET) != (int)si->file_offset() ||
read_bytes(base, size) != size) { read_bytes(base, size) != size) {
return false; return false;
@ -1646,7 +1648,7 @@ MapArchiveResult FileMapInfo::map_region(int i, intx addr_delta, char* mapped_ba
// that covers all the FileMapRegions to ensure all regions can be mapped. However, Windows // that covers all the FileMapRegions to ensure all regions can be mapped. However, Windows
// can't mmap into a ReservedSpace, so we just os::read() the data. We're going to patch all the // can't mmap into a ReservedSpace, so we just os::read() the data. We're going to patch all the
// regions anyway, so there's no benefit for mmap anyway. // regions anyway, so there's no benefit for mmap anyway.
if (!read_region(i, requested_addr, size)) { if (!read_region(i, requested_addr, size, /* do_commit = */ true)) {
log_info(cds)("Failed to read %s shared space into reserved space at " INTPTR_FORMAT, log_info(cds)("Failed to read %s shared space into reserved space at " INTPTR_FORMAT,
shared_region_name[i], p2i(requested_addr)); shared_region_name[i], p2i(requested_addr));
return MAP_ARCHIVE_OTHER_FAILURE; // oom or I/O error. return MAP_ARCHIVE_OTHER_FAILURE; // oom or I/O error.
@ -1817,7 +1819,7 @@ void FileMapInfo::map_or_load_heap_regions() {
} else if (HeapShared::can_load()) { } else if (HeapShared::can_load()) {
success = HeapShared::load_heap_regions(this); success = HeapShared::load_heap_regions(this);
} else { } else {
log_info(cds)("Cannot use CDS heap data. UseG1GC or UseEpsilonGC are required."); log_info(cds)("Cannot use CDS heap data. UseEpsilonGC, UseG1GC or UseSerialGC are required.");
} }
} }

View file

@ -477,7 +477,7 @@ public:
int first_region_idx) NOT_CDS_JAVA_HEAP_RETURN; int first_region_idx) NOT_CDS_JAVA_HEAP_RETURN;
bool has_heap_regions() NOT_CDS_JAVA_HEAP_RETURN_(false); bool has_heap_regions() NOT_CDS_JAVA_HEAP_RETURN_(false);
MemRegion get_heap_regions_range_with_current_oop_encoding_mode() NOT_CDS_JAVA_HEAP_RETURN_(MemRegion()); MemRegion get_heap_regions_range_with_current_oop_encoding_mode() NOT_CDS_JAVA_HEAP_RETURN_(MemRegion());
bool read_region(int i, char* base, size_t size); bool read_region(int i, char* base, size_t size, bool do_commit);
char* map_bitmap_region(); char* map_bitmap_region();
void unmap_region(int i); void unmap_region(int i);
bool verify_region_checksum(int i); bool verify_region_checksum(int i);

View file

@ -1586,7 +1586,7 @@ class PatchLoadedRegionPointers: public BitMapClosure {
}; };
int HeapShared::init_loaded_regions(FileMapInfo* mapinfo, LoadedArchiveHeapRegion* loaded_regions, int HeapShared::init_loaded_regions(FileMapInfo* mapinfo, LoadedArchiveHeapRegion* loaded_regions,
uintptr_t* buffer_ret) { MemRegion& archive_space) {
size_t total_bytes = 0; size_t total_bytes = 0;
int num_loaded_regions = 0; int num_loaded_regions = 0;
for (int i = MetaspaceShared::first_archive_heap_region; for (int i = MetaspaceShared::first_archive_heap_region;
@ -1604,12 +1604,16 @@ int HeapShared::init_loaded_regions(FileMapInfo* mapinfo, LoadedArchiveHeapRegio
} }
assert(is_aligned(total_bytes, HeapWordSize), "must be"); assert(is_aligned(total_bytes, HeapWordSize), "must be");
uintptr_t buffer = (uintptr_t) size_t word_size = total_bytes / HeapWordSize;
Universe::heap()->allocate_loaded_archive_space(total_bytes / HeapWordSize); HeapWord* buffer = Universe::heap()->allocate_loaded_archive_space(word_size);
_loaded_heap_bottom = buffer; if (buffer == nullptr) {
_loaded_heap_top = buffer + total_bytes; return 0;
}
archive_space = MemRegion(buffer, word_size);
_loaded_heap_bottom = (uintptr_t)archive_space.start();
_loaded_heap_top = _loaded_heap_bottom + total_bytes;
*buffer_ret = buffer;
return num_loaded_regions; return num_loaded_regions;
} }
@ -1638,15 +1642,17 @@ bool HeapShared::load_regions(FileMapInfo* mapinfo, LoadedArchiveHeapRegion* loa
LoadedArchiveHeapRegion* ri = &loaded_regions[i]; LoadedArchiveHeapRegion* ri = &loaded_regions[i];
FileMapRegion* r = mapinfo->space_at(ri->_region_index); FileMapRegion* r = mapinfo->space_at(ri->_region_index);
if (!mapinfo->read_region(ri->_region_index, (char*)load_address, r->used())) { if (!mapinfo->read_region(ri->_region_index, (char*)load_address, r->used(), /* do_commit = */ false)) {
// There's no easy way to free the buffer, so we will fill it with zero later // There's no easy way to free the buffer, so we will fill it with zero later
// in fill_failed_loaded_region(), and it will eventually be GC'ed. // in fill_failed_loaded_region(), and it will eventually be GC'ed.
log_warning(cds)("Loading of heap region %d has failed. Archived objects are disabled", i); log_warning(cds)("Loading of heap region %d has failed. Archived objects are disabled", i);
_loading_failed = true; _loading_failed = true;
return false; return false;
} }
log_info(cds)("Loaded heap region #%d at base " INTPTR_FORMAT " size = " SIZE_FORMAT_W(8) " bytes, delta = " INTX_FORMAT, log_info(cds)("Loaded heap region #%d at base " INTPTR_FORMAT " top " INTPTR_FORMAT
ri->_region_index, load_address, ri->_region_size, ri->_runtime_offset); " size " SIZE_FORMAT_W(6) " delta " INTX_FORMAT,
ri->_region_index, load_address, load_address + ri->_region_size,
ri->_region_size, ri->_runtime_offset);
uintptr_t oopmap = bitmap_base + r->oopmap_offset(); uintptr_t oopmap = bitmap_base + r->oopmap_offset();
BitMapView bm((BitMap::bm_word_t*)oopmap, r->oopmap_size_in_bits()); BitMapView bm((BitMap::bm_word_t*)oopmap, r->oopmap_size_in_bits());
@ -1675,10 +1681,14 @@ bool HeapShared::load_heap_regions(FileMapInfo* mapinfo) {
LoadedArchiveHeapRegion loaded_regions[MetaspaceShared::max_num_heap_regions]; LoadedArchiveHeapRegion loaded_regions[MetaspaceShared::max_num_heap_regions];
memset(loaded_regions, 0, sizeof(loaded_regions)); memset(loaded_regions, 0, sizeof(loaded_regions));
uintptr_t buffer; MemRegion archive_space;
int num_loaded_regions = init_loaded_regions(mapinfo, loaded_regions, &buffer); int num_loaded_regions = init_loaded_regions(mapinfo, loaded_regions, archive_space);
sort_loaded_regions(loaded_regions, num_loaded_regions, buffer); if (num_loaded_regions <= 0) {
if (!load_regions(mapinfo, loaded_regions, num_loaded_regions, buffer)) { return false;
}
sort_loaded_regions(loaded_regions, num_loaded_regions, (uintptr_t)archive_space.start());
if (!load_regions(mapinfo, loaded_regions, num_loaded_regions, (uintptr_t)archive_space.start())) {
assert(_loading_failed, "must be");
return false; return false;
} }
@ -1711,7 +1721,15 @@ class VerifyLoadedHeapEmbeddedPointers: public BasicOopIterateClosure {
} }
}; };
void HeapShared::verify_loaded_heap() { void HeapShared::finish_initialization() {
if (is_loaded()) {
HeapWord* bottom = (HeapWord*)_loaded_heap_bottom;
HeapWord* top = (HeapWord*)_loaded_heap_top;
MemRegion archive_space = MemRegion(bottom, top);
Universe::heap()->complete_loaded_archive_space(archive_space);
}
if (VerifyArchivedFields <= 0 || !is_loaded()) { if (VerifyArchivedFields <= 0 || !is_loaded()) {
return; return;
} }
@ -1739,9 +1757,12 @@ void HeapShared::verify_loaded_heap() {
void HeapShared::fill_failed_loaded_region() { void HeapShared::fill_failed_loaded_region() {
assert(_loading_failed, "must be"); assert(_loading_failed, "must be");
if (_loaded_heap_bottom != 0) {
assert(_loaded_heap_top != 0, "must be");
HeapWord* bottom = (HeapWord*)_loaded_heap_bottom; HeapWord* bottom = (HeapWord*)_loaded_heap_bottom;
HeapWord* top = (HeapWord*)_loaded_heap_top; HeapWord* top = (HeapWord*)_loaded_heap_top;
Universe::heap()->fill_with_objects(bottom, top - bottom); Universe::heap()->fill_with_objects(bottom, top - bottom);
} }
}
#endif // INCLUDE_CDS_JAVA_HEAP #endif // INCLUDE_CDS_JAVA_HEAP

View file

@ -170,7 +170,7 @@ public:
// Can this VM load the objects from archived heap regions into the heap at start-up? // Can this VM load the objects from archived heap regions into the heap at start-up?
static bool can_load() NOT_CDS_JAVA_HEAP_RETURN_(false); static bool can_load() NOT_CDS_JAVA_HEAP_RETURN_(false);
static void verify_loaded_heap() NOT_CDS_JAVA_HEAP_RETURN; static void finish_initialization() NOT_CDS_JAVA_HEAP_RETURN;
static bool is_loaded() { static bool is_loaded() {
CDS_JAVA_HEAP_ONLY(return _is_loaded;) CDS_JAVA_HEAP_ONLY(return _is_loaded;)
NOT_CDS_JAVA_HEAP(return false;) NOT_CDS_JAVA_HEAP(return false;)
@ -346,7 +346,7 @@ private:
static void init_archived_fields_for(Klass* k, const ArchivedKlassSubGraphInfoRecord* record); static void init_archived_fields_for(Klass* k, const ArchivedKlassSubGraphInfoRecord* record);
static int init_loaded_regions(FileMapInfo* mapinfo, LoadedArchiveHeapRegion* loaded_regions, static int init_loaded_regions(FileMapInfo* mapinfo, LoadedArchiveHeapRegion* loaded_regions,
uintptr_t* buffer_ret); MemRegion& archive_space);
static void sort_loaded_regions(LoadedArchiveHeapRegion* loaded_regions, int num_loaded_regions, static void sort_loaded_regions(LoadedArchiveHeapRegion* loaded_regions, int num_loaded_regions,
uintptr_t buffer); uintptr_t buffer);
static bool load_regions(FileMapInfo* mapinfo, LoadedArchiveHeapRegion* loaded_regions, static bool load_regions(FileMapInfo* mapinfo, LoadedArchiveHeapRegion* loaded_regions,

View file

@ -1434,7 +1434,7 @@ void MetaspaceShared::initialize_shared_spaces() {
// Finish up archived heap initialization. These must be // Finish up archived heap initialization. These must be
// done after ReadClosure. // done after ReadClosure.
static_mapinfo->patch_heap_embedded_pointers(); static_mapinfo->patch_heap_embedded_pointers();
HeapShared::verify_loaded_heap(); HeapShared::finish_initialization();
// Close the mapinfo file // Close the mapinfo file
static_mapinfo->close(); static_mapinfo->close();

View file

@ -30,6 +30,7 @@
#include "gc/shared/strongRootsScope.hpp" #include "gc/shared/strongRootsScope.hpp"
#include "gc/shared/suspendibleThreadSet.hpp" #include "gc/shared/suspendibleThreadSet.hpp"
#include "memory/universe.hpp" #include "memory/universe.hpp"
#include "runtime/mutexLocker.hpp"
#include "services/memoryManager.hpp" #include "services/memoryManager.hpp"
SerialHeap* SerialHeap::heap() { SerialHeap* SerialHeap::heap() {
@ -112,3 +113,13 @@ void SerialHeap::safepoint_synchronize_end() {
SuspendibleThreadSet::desynchronize(); SuspendibleThreadSet::desynchronize();
} }
} }
HeapWord* SerialHeap::allocate_loaded_archive_space(size_t word_size) {
MutexLocker ml(Heap_lock);
return old_gen()->allocate(word_size, false /* is_tlab */);
}
void SerialHeap::complete_loaded_archive_space(MemRegion archive_space) {
assert(old_gen()->used_region().contains(archive_space), "Archive space not contained in old gen");
old_gen()->complete_loaded_archive_space(archive_space);
}

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -83,6 +83,11 @@ public:
virtual void safepoint_synchronize_begin(); virtual void safepoint_synchronize_begin();
virtual void safepoint_synchronize_end(); virtual void safepoint_synchronize_end();
// Support for loading objects from CDS archive into the heap
bool can_load_archived_objects() const { return true; }
HeapWord* allocate_loaded_archive_space(size_t size);
void complete_loaded_archive_space(MemRegion archive_space);
}; };
#endif // SHARE_GC_SERIAL_SERIALHEAP_HPP #endif // SHARE_GC_SERIAL_SERIALHEAP_HPP

View file

@ -219,6 +219,18 @@ void TenuredGeneration::object_iterate(ObjectClosure* blk) {
_the_space->object_iterate(blk); _the_space->object_iterate(blk);
} }
void TenuredGeneration::complete_loaded_archive_space(MemRegion archive_space) {
// Create the BOT for the archive space.
TenuredSpace* space = (TenuredSpace*)_the_space;
space->initialize_threshold();
HeapWord* start = archive_space.start();
while (start < archive_space.end()) {
size_t word_size = _the_space->block_size(start);
space->alloc_block(start, start + word_size);
start += word_size;
}
}
void TenuredGeneration::save_marks() { void TenuredGeneration::save_marks() {
_the_space->set_saved_mark(); _the_space->set_saved_mark();
} }

View file

@ -74,6 +74,8 @@ class TenuredGeneration: public CardGeneration {
// Iteration // Iteration
void object_iterate(ObjectClosure* blk); void object_iterate(ObjectClosure* blk);
void complete_loaded_archive_space(MemRegion archive_space);
virtual inline HeapWord* allocate(size_t word_size, bool is_tlab); virtual inline HeapWord* allocate(size_t word_size, bool is_tlab);
virtual inline HeapWord* par_allocate(size_t word_size, bool is_tlab); virtual inline HeapWord* par_allocate(size_t word_size, bool is_tlab);

View file

@ -485,6 +485,7 @@ class CollectedHeap : public CHeapObj<mtInternal> {
// (usually as a snapshot of the old generation). // (usually as a snapshot of the old generation).
virtual bool can_load_archived_objects() const { return false; } virtual bool can_load_archived_objects() const { return false; }
virtual HeapWord* allocate_loaded_archive_space(size_t size) { return NULL; } virtual HeapWord* allocate_loaded_archive_space(size_t size) { return NULL; }
virtual void complete_loaded_archive_space(MemRegion archive_space) { }
virtual bool is_oop(oop object) const; virtual bool is_oop(oop object) const;
// Non product verification and debugging. // Non product verification and debugging.

View file

@ -367,8 +367,9 @@ hotspot_appcds_dynamic = \
-runtime/cds/appcds/SharedArchiveConsistency.java \ -runtime/cds/appcds/SharedArchiveConsistency.java \
-runtime/cds/appcds/StaticArchiveWithLambda.java \ -runtime/cds/appcds/StaticArchiveWithLambda.java \
-runtime/cds/appcds/TestCombinedCompressedFlags.java \ -runtime/cds/appcds/TestCombinedCompressedFlags.java \
-runtime/cds/appcds/TestZGCWithCDS.java \
-runtime/cds/appcds/TestEpsilonGCWithCDS.java \ -runtime/cds/appcds/TestEpsilonGCWithCDS.java \
-runtime/cds/appcds/TestSerialGCWithCDS.java \
-runtime/cds/appcds/TestZGCWithCDS.java \
-runtime/cds/appcds/UnusedCPDuringDump.java \ -runtime/cds/appcds/UnusedCPDuringDump.java \
-runtime/cds/appcds/VerifierTest_1B.java -runtime/cds/appcds/VerifierTest_1B.java

View file

@ -0,0 +1,122 @@
/*
* 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 Loading CDS archived heap objects into SerialGC
* @bug 8234679
* @requires vm.cds
* @requires vm.gc.Serial
* @requires vm.gc.G1
*
* @comment don't run this test if any -XX::+Use???GC options are specified, since they will
* interfere with the the test.
* @requires vm.gc == null
*
* @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds
* @compile test-classes/Hello.java
* @run driver TestSerialGCWithCDS
*/
import jdk.test.lib.Platform;
import jdk.test.lib.process.OutputAnalyzer;
public class TestSerialGCWithCDS {
public final static String HELLO = "Hello World";
static String helloJar;
public static void main(String... args) throws Exception {
helloJar = JarBuilder.build("hello", "Hello");
// Check if we can use SerialGC during dump time, or run time, or both.
test(false, true);
test(true, false);
test(true, true);
// We usually have 2 heap regions. To increase test coverage, we can have 3 heap regions
// by using "-Xmx256m -XX:ObjectAlignmentInBytes=64"
if (Platform.is64bit()) test(false, true, true);
}
final static String G1 = "-XX:+UseG1GC";
final static String Serial = "-XX:+UseSerialGC";
static void test(boolean dumpWithSerial, boolean execWithSerial) throws Exception {
test(dumpWithSerial, execWithSerial, false);
}
static void test(boolean dumpWithSerial, boolean execWithSerial, boolean useSmallRegions) throws Exception {
String dumpGC = dumpWithSerial ? Serial : G1;
String execGC = execWithSerial ? Serial : G1;
String small1 = useSmallRegions ? "-Xmx256m" : "-showversion";
String small2 = useSmallRegions ? "-XX:ObjectAlignmentInBytes=64" : "-showversion";
OutputAnalyzer out;
System.out.println("0. Dump with " + dumpGC);
out = TestCommon.dump(helloJar,
new String[] {"Hello"},
dumpGC,
small1,
small2,
"-Xlog:cds");
out.shouldContain("Dumping shared data to file:");
out.shouldHaveExitValue(0);
System.out.println("1. Exec with " + execGC);
out = TestCommon.exec(helloJar,
execGC,
small1,
small2,
"-Xlog:cds",
"Hello");
out.shouldContain(HELLO);
out.shouldHaveExitValue(0);
int n = 2;
if (dumpWithSerial == false && execWithSerial == true) {
// We dumped with G1, so we have an archived heap. At exec time, try to load them into
// a small SerialGC heap that may be too small.
String[] sizes = {
"4m", // usually this will success load the archived heap
"2m", // usually this will fail to loade th archived heap, but app can launch
"1m" // usually this will cause VM launch to fail with "Too small maximum heap"
};
for (String sz : sizes) {
String xmx = "-Xmx" + sz;
System.out.println("=======\n" + n + ". Exec with " + execGC + " " + xmx);
out = TestCommon.exec(helloJar,
execGC,
small1,
small2,
xmx,
"-Xlog:cds",
"Hello");
if (out.getExitValue() == 0) {
out.shouldContain(HELLO);
} else {
out.shouldContain("Too small maximum heap");
}
n++;
}
}
}
}

View file

@ -28,6 +28,7 @@
* @bug 8214455 * @bug 8214455
* @requires vm.cds.write.archived.java.heap * @requires vm.cds.write.archived.java.heap
* @requires (sun.arch.data.model == "64" & os.maxMemory > 4g) * @requires (sun.arch.data.model == "64" & os.maxMemory > 4g)
* @requires vm.gc == "null"
* @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds
* @build HeapFragmentationApp * @build HeapFragmentationApp
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar HeapFragmentationApp.jar HeapFragmentationApp * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar HeapFragmentationApp.jar HeapFragmentationApp