Skip to content

Commit 6a573b8

Browse files
committed
8273508: Support archived heap objects in SerialGC
Reviewed-by: tschatzl, ccheung
1 parent 3eca9c3 commit 6a573b8

13 files changed

+211
-33
lines changed

src/hotspot/share/cds/filemap.cpp

+12-10
Original file line numberDiff line numberDiff line change
@@ -1597,15 +1597,17 @@ MapArchiveResult FileMapInfo::map_regions(int regions[], int num_regions, char*
15971597
return MAP_ARCHIVE_SUCCESS;
15981598
}
15991599

1600-
bool FileMapInfo::read_region(int i, char* base, size_t size) {
1600+
bool FileMapInfo::read_region(int i, char* base, size_t size, bool do_commit) {
16011601
FileMapRegion* si = space_at(i);
1602-
log_info(cds)("Commit %s region #%d at base " INTPTR_FORMAT " top " INTPTR_FORMAT " (%s)%s",
1603-
is_static() ? "static " : "dynamic", i, p2i(base), p2i(base + size),
1604-
shared_region_name[i], si->allow_exec() ? " exec" : "");
1605-
if (!os::commit_memory(base, size, si->allow_exec())) {
1606-
log_error(cds)("Failed to commit %s region #%d (%s)", is_static() ? "static " : "dynamic",
1607-
i, shared_region_name[i]);
1608-
return false;
1602+
if (do_commit) {
1603+
log_info(cds)("Commit %s region #%d at base " INTPTR_FORMAT " top " INTPTR_FORMAT " (%s)%s",
1604+
is_static() ? "static " : "dynamic", i, p2i(base), p2i(base + size),
1605+
shared_region_name[i], si->allow_exec() ? " exec" : "");
1606+
if (!os::commit_memory(base, size, si->allow_exec())) {
1607+
log_error(cds)("Failed to commit %s region #%d (%s)", is_static() ? "static " : "dynamic",
1608+
i, shared_region_name[i]);
1609+
return false;
1610+
}
16091611
}
16101612
if (lseek(_fd, (long)si->file_offset(), SEEK_SET) != (int)si->file_offset() ||
16111613
read_bytes(base, size) != size) {
@@ -1646,7 +1648,7 @@ MapArchiveResult FileMapInfo::map_region(int i, intx addr_delta, char* mapped_ba
16461648
// that covers all the FileMapRegions to ensure all regions can be mapped. However, Windows
16471649
// can't mmap into a ReservedSpace, so we just os::read() the data. We're going to patch all the
16481650
// regions anyway, so there's no benefit for mmap anyway.
1649-
if (!read_region(i, requested_addr, size)) {
1651+
if (!read_region(i, requested_addr, size, /* do_commit = */ true)) {
16501652
log_info(cds)("Failed to read %s shared space into reserved space at " INTPTR_FORMAT,
16511653
shared_region_name[i], p2i(requested_addr));
16521654
return MAP_ARCHIVE_OTHER_FAILURE; // oom or I/O error.
@@ -1817,7 +1819,7 @@ void FileMapInfo::map_or_load_heap_regions() {
18171819
} else if (HeapShared::can_load()) {
18181820
success = HeapShared::load_heap_regions(this);
18191821
} else {
1820-
log_info(cds)("Cannot use CDS heap data. UseG1GC or UseEpsilonGC are required.");
1822+
log_info(cds)("Cannot use CDS heap data. UseEpsilonGC, UseG1GC or UseSerialGC are required.");
18211823
}
18221824
}
18231825

src/hotspot/share/cds/filemap.hpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -477,7 +477,7 @@ class FileMapInfo : public CHeapObj<mtInternal> {
477477
int first_region_idx) NOT_CDS_JAVA_HEAP_RETURN;
478478
bool has_heap_regions() NOT_CDS_JAVA_HEAP_RETURN_(false);
479479
MemRegion get_heap_regions_range_with_current_oop_encoding_mode() NOT_CDS_JAVA_HEAP_RETURN_(MemRegion());
480-
bool read_region(int i, char* base, size_t size);
480+
bool read_region(int i, char* base, size_t size, bool do_commit);
481481
char* map_bitmap_region();
482482
void unmap_region(int i);
483483
bool verify_region_checksum(int i);

src/hotspot/share/cds/heapShared.cpp

+38-17
Original file line numberDiff line numberDiff line change
@@ -1586,7 +1586,7 @@ class PatchLoadedRegionPointers: public BitMapClosure {
15861586
};
15871587

15881588
int HeapShared::init_loaded_regions(FileMapInfo* mapinfo, LoadedArchiveHeapRegion* loaded_regions,
1589-
uintptr_t* buffer_ret) {
1589+
MemRegion& archive_space) {
15901590
size_t total_bytes = 0;
15911591
int num_loaded_regions = 0;
15921592
for (int i = MetaspaceShared::first_archive_heap_region;
@@ -1604,12 +1604,16 @@ int HeapShared::init_loaded_regions(FileMapInfo* mapinfo, LoadedArchiveHeapRegio
16041604
}
16051605

16061606
assert(is_aligned(total_bytes, HeapWordSize), "must be");
1607-
uintptr_t buffer = (uintptr_t)
1608-
Universe::heap()->allocate_loaded_archive_space(total_bytes / HeapWordSize);
1609-
_loaded_heap_bottom = buffer;
1610-
_loaded_heap_top = buffer + total_bytes;
1607+
size_t word_size = total_bytes / HeapWordSize;
1608+
HeapWord* buffer = Universe::heap()->allocate_loaded_archive_space(word_size);
1609+
if (buffer == nullptr) {
1610+
return 0;
1611+
}
1612+
1613+
archive_space = MemRegion(buffer, word_size);
1614+
_loaded_heap_bottom = (uintptr_t)archive_space.start();
1615+
_loaded_heap_top = _loaded_heap_bottom + total_bytes;
16111616

1612-
*buffer_ret = buffer;
16131617
return num_loaded_regions;
16141618
}
16151619

@@ -1638,15 +1642,17 @@ bool HeapShared::load_regions(FileMapInfo* mapinfo, LoadedArchiveHeapRegion* loa
16381642
LoadedArchiveHeapRegion* ri = &loaded_regions[i];
16391643
FileMapRegion* r = mapinfo->space_at(ri->_region_index);
16401644

1641-
if (!mapinfo->read_region(ri->_region_index, (char*)load_address, r->used())) {
1645+
if (!mapinfo->read_region(ri->_region_index, (char*)load_address, r->used(), /* do_commit = */ false)) {
16421646
// There's no easy way to free the buffer, so we will fill it with zero later
16431647
// in fill_failed_loaded_region(), and it will eventually be GC'ed.
16441648
log_warning(cds)("Loading of heap region %d has failed. Archived objects are disabled", i);
16451649
_loading_failed = true;
16461650
return false;
16471651
}
1648-
log_info(cds)("Loaded heap region #%d at base " INTPTR_FORMAT " size = " SIZE_FORMAT_W(8) " bytes, delta = " INTX_FORMAT,
1649-
ri->_region_index, load_address, ri->_region_size, ri->_runtime_offset);
1652+
log_info(cds)("Loaded heap region #%d at base " INTPTR_FORMAT " top " INTPTR_FORMAT
1653+
" size " SIZE_FORMAT_W(6) " delta " INTX_FORMAT,
1654+
ri->_region_index, load_address, load_address + ri->_region_size,
1655+
ri->_region_size, ri->_runtime_offset);
16501656

16511657
uintptr_t oopmap = bitmap_base + r->oopmap_offset();
16521658
BitMapView bm((BitMap::bm_word_t*)oopmap, r->oopmap_size_in_bits());
@@ -1675,10 +1681,14 @@ bool HeapShared::load_heap_regions(FileMapInfo* mapinfo) {
16751681
LoadedArchiveHeapRegion loaded_regions[MetaspaceShared::max_num_heap_regions];
16761682
memset(loaded_regions, 0, sizeof(loaded_regions));
16771683

1678-
uintptr_t buffer;
1679-
int num_loaded_regions = init_loaded_regions(mapinfo, loaded_regions, &buffer);
1680-
sort_loaded_regions(loaded_regions, num_loaded_regions, buffer);
1681-
if (!load_regions(mapinfo, loaded_regions, num_loaded_regions, buffer)) {
1684+
MemRegion archive_space;
1685+
int num_loaded_regions = init_loaded_regions(mapinfo, loaded_regions, archive_space);
1686+
if (num_loaded_regions <= 0) {
1687+
return false;
1688+
}
1689+
sort_loaded_regions(loaded_regions, num_loaded_regions, (uintptr_t)archive_space.start());
1690+
if (!load_regions(mapinfo, loaded_regions, num_loaded_regions, (uintptr_t)archive_space.start())) {
1691+
assert(_loading_failed, "must be");
16821692
return false;
16831693
}
16841694

@@ -1711,7 +1721,15 @@ class VerifyLoadedHeapEmbeddedPointers: public BasicOopIterateClosure {
17111721
}
17121722
};
17131723

1714-
void HeapShared::verify_loaded_heap() {
1724+
void HeapShared::finish_initialization() {
1725+
if (is_loaded()) {
1726+
HeapWord* bottom = (HeapWord*)_loaded_heap_bottom;
1727+
HeapWord* top = (HeapWord*)_loaded_heap_top;
1728+
1729+
MemRegion archive_space = MemRegion(bottom, top);
1730+
Universe::heap()->complete_loaded_archive_space(archive_space);
1731+
}
1732+
17151733
if (VerifyArchivedFields <= 0 || !is_loaded()) {
17161734
return;
17171735
}
@@ -1739,9 +1757,12 @@ void HeapShared::verify_loaded_heap() {
17391757

17401758
void HeapShared::fill_failed_loaded_region() {
17411759
assert(_loading_failed, "must be");
1742-
HeapWord* bottom = (HeapWord*)_loaded_heap_bottom;
1743-
HeapWord* top = (HeapWord*)_loaded_heap_top;
1744-
Universe::heap()->fill_with_objects(bottom, top - bottom);
1760+
if (_loaded_heap_bottom != 0) {
1761+
assert(_loaded_heap_top != 0, "must be");
1762+
HeapWord* bottom = (HeapWord*)_loaded_heap_bottom;
1763+
HeapWord* top = (HeapWord*)_loaded_heap_top;
1764+
Universe::heap()->fill_with_objects(bottom, top - bottom);
1765+
}
17451766
}
17461767

17471768
#endif // INCLUDE_CDS_JAVA_HEAP

src/hotspot/share/cds/heapShared.hpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ class HeapShared: AllStatic {
170170

171171
// Can this VM load the objects from archived heap regions into the heap at start-up?
172172
static bool can_load() NOT_CDS_JAVA_HEAP_RETURN_(false);
173-
static void verify_loaded_heap() NOT_CDS_JAVA_HEAP_RETURN;
173+
static void finish_initialization() NOT_CDS_JAVA_HEAP_RETURN;
174174
static bool is_loaded() {
175175
CDS_JAVA_HEAP_ONLY(return _is_loaded;)
176176
NOT_CDS_JAVA_HEAP(return false;)
@@ -346,7 +346,7 @@ class HeapShared: AllStatic {
346346
static void init_archived_fields_for(Klass* k, const ArchivedKlassSubGraphInfoRecord* record);
347347

348348
static int init_loaded_regions(FileMapInfo* mapinfo, LoadedArchiveHeapRegion* loaded_regions,
349-
uintptr_t* buffer_ret);
349+
MemRegion& archive_space);
350350
static void sort_loaded_regions(LoadedArchiveHeapRegion* loaded_regions, int num_loaded_regions,
351351
uintptr_t buffer);
352352
static bool load_regions(FileMapInfo* mapinfo, LoadedArchiveHeapRegion* loaded_regions,

src/hotspot/share/cds/metaspaceShared.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -1434,7 +1434,7 @@ void MetaspaceShared::initialize_shared_spaces() {
14341434
// Finish up archived heap initialization. These must be
14351435
// done after ReadClosure.
14361436
static_mapinfo->patch_heap_embedded_pointers();
1437-
HeapShared::verify_loaded_heap();
1437+
HeapShared::finish_initialization();
14381438

14391439
// Close the mapinfo file
14401440
static_mapinfo->close();

src/hotspot/share/gc/serial/serialHeap.cpp

+11
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include "gc/shared/strongRootsScope.hpp"
3131
#include "gc/shared/suspendibleThreadSet.hpp"
3232
#include "memory/universe.hpp"
33+
#include "runtime/mutexLocker.hpp"
3334
#include "services/memoryManager.hpp"
3435

3536
SerialHeap* SerialHeap::heap() {
@@ -112,3 +113,13 @@ void SerialHeap::safepoint_synchronize_end() {
112113
SuspendibleThreadSet::desynchronize();
113114
}
114115
}
116+
117+
HeapWord* SerialHeap::allocate_loaded_archive_space(size_t word_size) {
118+
MutexLocker ml(Heap_lock);
119+
return old_gen()->allocate(word_size, false /* is_tlab */);
120+
}
121+
122+
void SerialHeap::complete_loaded_archive_space(MemRegion archive_space) {
123+
assert(old_gen()->used_region().contains(archive_space), "Archive space not contained in old gen");
124+
old_gen()->complete_loaded_archive_space(archive_space);
125+
}

src/hotspot/share/gc/serial/serialHeap.hpp

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -83,6 +83,11 @@ class SerialHeap : public GenCollectedHeap {
8383

8484
virtual void safepoint_synchronize_begin();
8585
virtual void safepoint_synchronize_end();
86+
87+
// Support for loading objects from CDS archive into the heap
88+
bool can_load_archived_objects() const { return true; }
89+
HeapWord* allocate_loaded_archive_space(size_t size);
90+
void complete_loaded_archive_space(MemRegion archive_space);
8691
};
8792

8893
#endif // SHARE_GC_SERIAL_SERIALHEAP_HPP

src/hotspot/share/gc/serial/tenuredGeneration.cpp

+12
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,18 @@ void TenuredGeneration::object_iterate(ObjectClosure* blk) {
219219
_the_space->object_iterate(blk);
220220
}
221221

222+
void TenuredGeneration::complete_loaded_archive_space(MemRegion archive_space) {
223+
// Create the BOT for the archive space.
224+
TenuredSpace* space = (TenuredSpace*)_the_space;
225+
space->initialize_threshold();
226+
HeapWord* start = archive_space.start();
227+
while (start < archive_space.end()) {
228+
size_t word_size = _the_space->block_size(start);
229+
space->alloc_block(start, start + word_size);
230+
start += word_size;
231+
}
232+
}
233+
222234
void TenuredGeneration::save_marks() {
223235
_the_space->set_saved_mark();
224236
}

src/hotspot/share/gc/serial/tenuredGeneration.hpp

+2
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ class TenuredGeneration: public CardGeneration {
7474
// Iteration
7575
void object_iterate(ObjectClosure* blk);
7676

77+
void complete_loaded_archive_space(MemRegion archive_space);
78+
7779
virtual inline HeapWord* allocate(size_t word_size, bool is_tlab);
7880
virtual inline HeapWord* par_allocate(size_t word_size, bool is_tlab);
7981

src/hotspot/share/gc/shared/collectedHeap.hpp

+1
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,7 @@ class CollectedHeap : public CHeapObj<mtInternal> {
485485
// (usually as a snapshot of the old generation).
486486
virtual bool can_load_archived_objects() const { return false; }
487487
virtual HeapWord* allocate_loaded_archive_space(size_t size) { return NULL; }
488+
virtual void complete_loaded_archive_space(MemRegion archive_space) { }
488489

489490
virtual bool is_oop(oop object) const;
490491
// Non product verification and debugging.

test/hotspot/jtreg/TEST.groups

+2-1
Original file line numberDiff line numberDiff line change
@@ -367,8 +367,9 @@ hotspot_appcds_dynamic = \
367367
-runtime/cds/appcds/SharedArchiveConsistency.java \
368368
-runtime/cds/appcds/StaticArchiveWithLambda.java \
369369
-runtime/cds/appcds/TestCombinedCompressedFlags.java \
370-
-runtime/cds/appcds/TestZGCWithCDS.java \
371370
-runtime/cds/appcds/TestEpsilonGCWithCDS.java \
371+
-runtime/cds/appcds/TestSerialGCWithCDS.java \
372+
-runtime/cds/appcds/TestZGCWithCDS.java \
372373
-runtime/cds/appcds/UnusedCPDuringDump.java \
373374
-runtime/cds/appcds/VerifierTest_1B.java
374375

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
/*
2+
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
/*
25+
* @test Loading CDS archived heap objects into SerialGC
26+
* @bug 8234679
27+
* @requires vm.cds
28+
* @requires vm.gc.Serial
29+
* @requires vm.gc.G1
30+
*
31+
* @comment don't run this test if any -XX::+Use???GC options are specified, since they will
32+
* interfere with the the test.
33+
* @requires vm.gc == null
34+
*
35+
* @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds
36+
* @compile test-classes/Hello.java
37+
* @run driver TestSerialGCWithCDS
38+
*/
39+
40+
import jdk.test.lib.Platform;
41+
import jdk.test.lib.process.OutputAnalyzer;
42+
43+
public class TestSerialGCWithCDS {
44+
public final static String HELLO = "Hello World";
45+
static String helloJar;
46+
47+
public static void main(String... args) throws Exception {
48+
helloJar = JarBuilder.build("hello", "Hello");
49+
50+
// Check if we can use SerialGC during dump time, or run time, or both.
51+
test(false, true);
52+
test(true, false);
53+
test(true, true);
54+
55+
// We usually have 2 heap regions. To increase test coverage, we can have 3 heap regions
56+
// by using "-Xmx256m -XX:ObjectAlignmentInBytes=64"
57+
if (Platform.is64bit()) test(false, true, true);
58+
}
59+
60+
final static String G1 = "-XX:+UseG1GC";
61+
final static String Serial = "-XX:+UseSerialGC";
62+
63+
static void test(boolean dumpWithSerial, boolean execWithSerial) throws Exception {
64+
test(dumpWithSerial, execWithSerial, false);
65+
}
66+
67+
static void test(boolean dumpWithSerial, boolean execWithSerial, boolean useSmallRegions) throws Exception {
68+
String dumpGC = dumpWithSerial ? Serial : G1;
69+
String execGC = execWithSerial ? Serial : G1;
70+
String small1 = useSmallRegions ? "-Xmx256m" : "-showversion";
71+
String small2 = useSmallRegions ? "-XX:ObjectAlignmentInBytes=64" : "-showversion";
72+
OutputAnalyzer out;
73+
74+
System.out.println("0. Dump with " + dumpGC);
75+
out = TestCommon.dump(helloJar,
76+
new String[] {"Hello"},
77+
dumpGC,
78+
small1,
79+
small2,
80+
"-Xlog:cds");
81+
out.shouldContain("Dumping shared data to file:");
82+
out.shouldHaveExitValue(0);
83+
84+
System.out.println("1. Exec with " + execGC);
85+
out = TestCommon.exec(helloJar,
86+
execGC,
87+
small1,
88+
small2,
89+
"-Xlog:cds",
90+
"Hello");
91+
out.shouldContain(HELLO);
92+
out.shouldHaveExitValue(0);
93+
94+
int n = 2;
95+
if (dumpWithSerial == false && execWithSerial == true) {
96+
// We dumped with G1, so we have an archived heap. At exec time, try to load them into
97+
// a small SerialGC heap that may be too small.
98+
String[] sizes = {
99+
"4m", // usually this will success load the archived heap
100+
"2m", // usually this will fail to loade th archived heap, but app can launch
101+
"1m" // usually this will cause VM launch to fail with "Too small maximum heap"
102+
};
103+
for (String sz : sizes) {
104+
String xmx = "-Xmx" + sz;
105+
System.out.println("=======\n" + n + ". Exec with " + execGC + " " + xmx);
106+
out = TestCommon.exec(helloJar,
107+
execGC,
108+
small1,
109+
small2,
110+
xmx,
111+
"-Xlog:cds",
112+
"Hello");
113+
if (out.getExitValue() == 0) {
114+
out.shouldContain(HELLO);
115+
} else {
116+
out.shouldContain("Too small maximum heap");
117+
}
118+
n++;
119+
}
120+
}
121+
}
122+
}

0 commit comments

Comments
 (0)