Skip to content

Commit 02ff38f

Browse files
committed
8363986: Heap region in CDS archive is not at deterministic address
Reviewed-by: kvn, asmehra
1 parent 902aa4d commit 02ff38f

File tree

7 files changed

+125
-41
lines changed

7 files changed

+125
-41
lines changed

src/hotspot/share/cds/aotArtifactFinder.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ class TypeArrayKlass;
3939
// It also decides what Klasses must be cached in aot-initialized state.
4040
//
4141
// ArchiveBuilder uses [1] as roots to scan for all MetaspaceObjs that need to be cached.
42-
// ArchiveHeapWriter uses [2] to create an image of the archived heap.
42+
// HeapShared uses [2] to create an image of the archived heap.
4343
//
4444
// [1] is stored in _all_cached_classes in aotArtifactFinder.cpp.
4545
// [2] is stored in HeapShared::archived_object_cache().

src/hotspot/share/cds/aotMapLogger.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -796,7 +796,7 @@ void AOTMapLogger::dumptime_log_mapped_heap_region(ArchiveMappedHeapInfo* heap_i
796796
address buffer_start = address(r.start()); // start of the current oop inside the buffer
797797
address buffer_end = address(r.end());
798798

799-
address requested_base = UseCompressedOops ? (address)CompressedOops::base() : (address)AOTMappedHeapWriter::NOCOOPS_REQUESTED_BASE;
799+
address requested_base = UseCompressedOops ? AOTMappedHeapWriter::narrow_oop_base() : (address)AOTMappedHeapWriter::NOCOOPS_REQUESTED_BASE;
800800
address requested_start = UseCompressedOops ? AOTMappedHeapWriter::buffered_addr_to_requested_addr(buffer_start) : requested_base;
801801

802802
log_region_range("heap", buffer_start, buffer_end, requested_start);

src/hotspot/share/cds/aotMappedHeapWriter.cpp

Lines changed: 96 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@
5555

5656
GrowableArrayCHeap<u1, mtClassShared>* AOTMappedHeapWriter::_buffer = nullptr;
5757

58-
// The following are offsets from buffer_bottom()
58+
bool AOTMappedHeapWriter::_is_writing_deterministic_heap = false;
5959
size_t AOTMappedHeapWriter::_buffer_used;
6060

6161
// Heap root segments
@@ -74,7 +74,7 @@ AOTMappedHeapWriter::_buffer_offset_to_source_obj_table = nullptr;
7474
DumpedInternedStrings *AOTMappedHeapWriter::_dumped_interned_strings = nullptr;
7575

7676
typedef HashTable<
77-
size_t, // offset of a filler from ArchiveHeapWriter::buffer_bottom()
77+
size_t, // offset of a filler from AOTMappedHeapWriter::buffer_bottom()
7878
size_t, // size of this filler (in bytes)
7979
127, // prime number
8080
AnyObj::C_HEAP,
@@ -96,6 +96,45 @@ void AOTMappedHeapWriter::init() {
9696
_source_objs = new GrowableArrayCHeap<oop, mtClassShared>(10000);
9797

9898
guarantee(MIN_GC_REGION_ALIGNMENT <= G1HeapRegion::min_region_size_in_words() * HeapWordSize, "must be");
99+
100+
if (CDSConfig::old_cds_flags_used()) {
101+
// With the old CDS workflow, we can guatantee determninistic output: given
102+
// the same classlist file, we can generate the same static CDS archive.
103+
// To ensure determinism, we always use the same compressed oop encoding
104+
// (zero-based, no shift). See set_requested_address_range().
105+
_is_writing_deterministic_heap = true;
106+
} else {
107+
// Determninistic output is not supported by the new AOT workflow, so
108+
// we don't force the (zero-based, no shift) encoding. This way, it is more
109+
// likely that we can avoid oop relocation in the production run.
110+
_is_writing_deterministic_heap = false;
111+
}
112+
}
113+
}
114+
115+
// For AOTMappedHeapWriter::narrow_oop_{mode, base, shift}(), see comments
116+
// in AOTMappedHeapWriter::set_requested_address_range(),
117+
CompressedOops::Mode AOTMappedHeapWriter::narrow_oop_mode() {
118+
if (is_writing_deterministic_heap()) {
119+
return CompressedOops::UnscaledNarrowOop;
120+
} else {
121+
return CompressedOops::mode();
122+
}
123+
}
124+
125+
address AOTMappedHeapWriter::narrow_oop_base() {
126+
if (is_writing_deterministic_heap()) {
127+
return (address)0;
128+
} else {
129+
return CompressedOops::base();
130+
}
131+
}
132+
133+
int AOTMappedHeapWriter::narrow_oop_shift() {
134+
if (is_writing_deterministic_heap()) {
135+
return 0;
136+
} else {
137+
return CompressedOops::shift();
99138
}
100139
}
101140

@@ -116,7 +155,7 @@ void AOTMappedHeapWriter::write(GrowableArrayCHeap<oop, mtClassShared>* roots,
116155
assert(CDSConfig::is_dumping_heap(), "sanity");
117156
allocate_buffer();
118157
copy_source_objs_to_buffer(roots);
119-
set_requested_address(heap_info);
158+
set_requested_address_range(heap_info);
120159
relocate_embedded_oops(roots, heap_info);
121160
}
122161

@@ -536,14 +575,55 @@ size_t AOTMappedHeapWriter::copy_one_source_obj_to_buffer(oop src_obj) {
536575
return buffered_obj_offset;
537576
}
538577

539-
void AOTMappedHeapWriter::set_requested_address(ArchiveMappedHeapInfo* info) {
578+
// Set the range [_requested_bottom, _requested_top), the requested address range of all
579+
// the archived heap objects in the production run.
580+
//
581+
// (1) UseCompressedOops == true && !is_writing_deterministic_heap()
582+
//
583+
// The archived objects are stored using the COOPS encoding of the assembly phase.
584+
// We pick a range within the heap used by the assembly phase.
585+
//
586+
// In the production run, if different COOPS encodings are used:
587+
// - The heap contents needs to be relocated.
588+
//
589+
// (2) UseCompressedOops == true && is_writing_deterministic_heap()
590+
//
591+
// We always use zero-based, zero-shift encoding. _requested_top is aligned to 0x10000000.
592+
//
593+
// (3) UseCompressedOops == false:
594+
//
595+
// In the production run, the heap range is usually picked (randomly) by the OS, so we
596+
// will almost always need to perform relocation, regardless of how we pick the requested
597+
// address range.
598+
//
599+
// So we just hard code it to NOCOOPS_REQUESTED_BASE.
600+
//
601+
void AOTMappedHeapWriter::set_requested_address_range(ArchiveMappedHeapInfo* info) {
540602
assert(!info->is_used(), "only set once");
541603

542604
size_t heap_region_byte_size = _buffer_used;
543605
assert(heap_region_byte_size > 0, "must archived at least one object!");
544606

545607
if (UseCompressedOops) {
546-
if (UseG1GC) {
608+
if (is_writing_deterministic_heap()) {
609+
// Pick a heap range so that requested addresses can be encoded with zero-base/no shift.
610+
// We align the requested bottom to at least 1 MB: if the production run uses G1 with a small
611+
// heap (e.g., -Xmx256m), it's likely that we can map the archived objects at the
612+
// requested location to avoid relocation.
613+
//
614+
// For other collectors or larger heaps, relocation is unavoidable, but is usually
615+
// quite cheap. If you really want to avoid relocation, use the AOT workflow instead.
616+
address heap_end = (address)0x100000000;
617+
size_t alignment = MAX2(MIN_GC_REGION_ALIGNMENT, 1024 * 1024);
618+
if (align_up(heap_region_byte_size, alignment) >= (size_t)heap_end) {
619+
log_error(aot, heap)("cached heap space is too large: %zu bytes", heap_region_byte_size);
620+
AOTMetaspace::unrecoverable_writing_error();
621+
}
622+
_requested_bottom = align_down(heap_end - heap_region_byte_size, alignment);
623+
} else if (UseG1GC) {
624+
// For G1, pick the range at the top of the current heap. If the exact same heap sizes
625+
// are used in the production run, it's likely that we can map the archived objects
626+
// at the requested location to avoid relocation.
547627
address heap_end = (address)G1CollectedHeap::heap()->reserved().end();
548628
log_info(aot, heap)("Heap end = %p", heap_end);
549629
_requested_bottom = align_down(heap_end - heap_region_byte_size, G1HeapRegion::GrainBytes);
@@ -612,7 +692,14 @@ oop AOTMappedHeapWriter::load_oop_from_buffer(narrowOop* buffered_addr) {
612692

613693
template <typename T> void AOTMappedHeapWriter::relocate_field_in_buffer(T* field_addr_in_buffer, oop source_referent, CHeapBitMap* oopmap) {
614694
oop request_referent = source_obj_to_requested_obj(source_referent);
615-
store_requested_oop_in_buffer<T>(field_addr_in_buffer, request_referent);
695+
if (UseCompressedOops && is_writing_deterministic_heap()) {
696+
// We use zero-based, 0-shift encoding, so the narrowOop is just the lower
697+
// 32 bits of request_referent
698+
intptr_t addr = cast_from_oop<intptr_t>(request_referent);
699+
*((narrowOop*)field_addr_in_buffer) = checked_cast<narrowOop>(addr);
700+
} else {
701+
store_requested_oop_in_buffer<T>(field_addr_in_buffer, request_referent);
702+
}
616703
if (request_referent != nullptr) {
617704
mark_oop_pointer<T>(field_addr_in_buffer, oopmap);
618705
}
@@ -918,9 +1005,9 @@ AOTMapLogger::OopDataIterator* AOTMappedHeapWriter::oop_iterator(ArchiveMappedHe
9181005
address buffer_start = address(r.start());
9191006
address buffer_end = address(r.end());
9201007

921-
address requested_base = UseCompressedOops ? (address)CompressedOops::base() : (address)AOTMappedHeapWriter::NOCOOPS_REQUESTED_BASE;
922-
address requested_start = UseCompressedOops ? buffered_addr_to_requested_addr(buffer_start) : requested_base;
923-
int requested_shift = CompressedOops::shift();
1008+
address requested_base = UseCompressedOops ? AOTMappedHeapWriter::narrow_oop_base() : (address)AOTMappedHeapWriter::NOCOOPS_REQUESTED_BASE;
1009+
address requested_start = UseCompressedOops ? AOTMappedHeapWriter::buffered_addr_to_requested_addr(buffer_start) : requested_base;
1010+
int requested_shift = AOTMappedHeapWriter::narrow_oop_shift();
9241011
intptr_t buffer_to_requested_delta = requested_start - buffer_start;
9251012
uint64_t buffer_start_narrow_oop = 0xdeadbeed;
9261013
if (UseCompressedOops) {

src/hotspot/share/cds/aotMappedHeapWriter.hpp

Lines changed: 20 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include "cds/heapShared.hpp"
3030
#include "memory/allocation.hpp"
3131
#include "memory/allStatic.hpp"
32+
#include "oops/compressedOops.hpp"
3233
#include "oops/oopHandle.hpp"
3334
#include "utilities/bitMap.hpp"
3435
#include "utilities/exceptions.hpp"
@@ -71,7 +72,7 @@ class AOTMappedHeapWriter : AllStatic {
7172
// These are entered into HeapShared::archived_object_cache().
7273
//
7374
// - "buffered objects" are copies of the "source objects", and are stored in into
74-
// ArchiveHeapWriter::_buffer, which is a GrowableArray that sits outside of
75+
// AOTMappedHeapWriter::_buffer, which is a GrowableArray that sits outside of
7576
// the valid heap range. Therefore we avoid using the addresses of these copies
7677
// as oops. They are usually called "buffered_addr" in the code (of the type "address").
7778
//
@@ -81,26 +82,11 @@ class AOTMappedHeapWriter : AllStatic {
8182
// - Each archived object has a "requested address" -- at run time, if the object
8283
// can be mapped at this address, we can avoid relocation.
8384
//
84-
// The requested address is implemented differently depending on UseCompressedOops:
85+
// The requested address of an archived object is essentially its buffered_addr + delta,
86+
// where delta is (_requested_bottom - buffer_bottom());
8587
//
86-
// UseCompressedOops == true:
87-
// The archived objects are stored assuming that the runtime COOPS compression
88-
// scheme is exactly the same as in dump time (or else a more expensive runtime relocation
89-
// would be needed.)
90-
//
91-
// At dump time, we assume that the runtime heap range is exactly the same as
92-
// in dump time. The requested addresses of the archived objects are chosen such that
93-
// they would occupy the top end of a G1 heap (TBD when dumping is supported by other
94-
// collectors. See JDK-8298614).
95-
//
96-
// UseCompressedOops == false:
97-
// At runtime, the heap range is usually picked (randomly) by the OS, so we will almost always
98-
// need to perform relocation. Hence, the goal of the "requested address" is to ensure that
99-
// the contents of the archived objects are deterministic. I.e., the oop fields of archived
100-
// objects will always point to deterministic addresses.
101-
//
102-
// For G1, the archived heap is written such that the lowest archived object is placed
103-
// at NOCOOPS_REQUESTED_BASE. (TBD after JDK-8298614).
88+
// The requested addresses of all archived objects are within [_requested_bottom, _requested_top).
89+
// See AOTMappedHeapWriter::set_requested_address_range() for more info.
10490
// ----------------------------------------------------------------------
10591

10692
public:
@@ -111,6 +97,15 @@ class AOTMappedHeapWriter : AllStatic {
11197
// Shenandoah heap region size can never be smaller than 256K.
11298
static constexpr int MIN_GC_REGION_ALIGNMENT = 256 * K;
11399

100+
// The heap contents are required to be deterministic when dumping "old" CDS archives, in order
101+
// to support reproducible lib/server/classes*.jsa when building the JDK.
102+
static bool is_writing_deterministic_heap() { return _is_writing_deterministic_heap; }
103+
104+
// The oop encoding used by the archived heap objects.
105+
static CompressedOops::Mode narrow_oop_mode();
106+
static address narrow_oop_base();
107+
static int narrow_oop_shift();
108+
114109
static const int INITIAL_TABLE_SIZE = 15889; // prime number
115110
static const int MAX_TABLE_SIZE = 1000000;
116111

@@ -121,6 +116,7 @@ class AOTMappedHeapWriter : AllStatic {
121116
int _field_offset;
122117
};
123118

119+
static bool _is_writing_deterministic_heap;
124120
static GrowableArrayCHeap<u1, mtClassShared>* _buffer;
125121

126122
// The number of bytes that have written into _buffer (may be smaller than _buffer->length()).
@@ -130,15 +126,15 @@ class AOTMappedHeapWriter : AllStatic {
130126
static HeapRootSegments _heap_root_segments;
131127

132128
// The address range of the requested location of the archived heap objects.
133-
static address _requested_bottom;
134-
static address _requested_top;
129+
static address _requested_bottom; // The requested address of the lowest archived heap object
130+
static address _requested_top; // The exclusive end of the highest archived heap object
135131

136132
static GrowableArrayCHeap<NativePointerInfo, mtClassShared>* _native_pointers;
137133
static GrowableArrayCHeap<oop, mtClassShared>* _source_objs;
138134
static DumpedInternedStrings *_dumped_interned_strings;
139135

140136
// We sort _source_objs_order to minimize the number of bits in ptrmap and oopmap.
141-
// See comments near the body of ArchiveHeapWriter::compare_objs_by_oop_fields().
137+
// See comments near the body of AOTMappedHeapWriter::compare_objs_by_oop_fields().
142138
// The objects will be written in the order of:
143139
//_source_objs->at(_source_objs_order->at(0)._index)
144140
// source_objs->at(_source_objs_order->at(1)._index)
@@ -200,7 +196,7 @@ class AOTMappedHeapWriter : AllStatic {
200196
static int filler_array_length(size_t fill_bytes);
201197
static HeapWord* init_filler_array_at_buffer_top(int array_length, size_t fill_bytes);
202198

203-
static void set_requested_address(ArchiveMappedHeapInfo* info);
199+
static void set_requested_address_range(ArchiveMappedHeapInfo* info);
204200
static void mark_native_pointers(oop orig_obj);
205201
static void relocate_embedded_oops(GrowableArrayCHeap<oop, mtClassShared>* roots, ArchiveMappedHeapInfo* info);
206202
static void compute_ptrmap(ArchiveMappedHeapInfo *info);

src/hotspot/share/cds/filemap.cpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -216,12 +216,14 @@ void FileMapHeader::populate(FileMapInfo *info, size_t core_region_alignment,
216216
_obj_alignment = ObjectAlignmentInBytes;
217217
_compact_strings = CompactStrings;
218218
_compact_headers = UseCompactObjectHeaders;
219+
#if INCLUDE_CDS_JAVA_HEAP
219220
if (CDSConfig::is_dumping_heap()) {
220221
_object_streaming_mode = HeapShared::is_writing_streaming_mode();
221-
_narrow_oop_mode = CompressedOops::mode();
222-
_narrow_oop_base = CompressedOops::base();
223-
_narrow_oop_shift = CompressedOops::shift();
222+
_narrow_oop_mode = AOTMappedHeapWriter::narrow_oop_mode();
223+
_narrow_oop_base = AOTMappedHeapWriter::narrow_oop_base();
224+
_narrow_oop_shift = AOTMappedHeapWriter::narrow_oop_shift();
224225
}
226+
#endif
225227
_compressed_oops = UseCompressedOops;
226228
_compressed_class_ptrs = UseCompressedClassPointers;
227229
if (UseCompressedClassPointers) {
@@ -911,7 +913,7 @@ void FileMapInfo::write_region(int region, char* base, size_t size,
911913
if (HeapShared::is_writing_mapping_mode()) {
912914
requested_base = (char*)AOTMappedHeapWriter::requested_address();
913915
if (UseCompressedOops) {
914-
mapping_offset = (size_t)((address)requested_base - CompressedOops::base());
916+
mapping_offset = (size_t)((address)requested_base - AOTMappedHeapWriter::narrow_oop_base());
915917
assert((mapping_offset >> CompressedOops::shift()) << CompressedOops::shift() == mapping_offset, "must be");
916918
}
917919
} else {

src/hotspot/share/cds/heapShared.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -332,7 +332,7 @@ class HeapShared: AllStatic {
332332
// Used by CDSHeapVerifier.
333333
OopHandle _orig_referrer;
334334

335-
// The location of this object inside ArchiveHeapWriter::_buffer
335+
// The location of this object inside {AOTMappedHeapWriter, AOTStreamedHeapWriter}::_buffer
336336
size_t _buffer_offset;
337337

338338
// One or more fields in this object are pointing to non-null oops.

test/hotspot/jtreg/ProblemList.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,6 @@ gc/shenandoah/TestSieveObjects.java#no-tlab-genshen 8361099 generic-all
100100

101101
# :hotspot_runtime
102102

103-
runtime/cds/DeterministicDump.java 8363986 macosx-x64,macosx-aarch64
104103
runtime/jni/terminatedThread/TestTerminatedThread.java 8317789 aix-ppc64
105104
runtime/Monitor/SyncOnValueBasedClassTest.java 8340995 linux-s390x
106105
runtime/os/TestTracePageSizes.java#no-options 8267460 linux-aarch64

0 commit comments

Comments
 (0)