mirror of
https://github.com/openjdk/jdk.git
synced 2026-01-28 03:58:21 +00:00
8363986: Heap region in CDS archive is not at deterministic address
Reviewed-by: kvn, asmehra
This commit is contained in:
parent
902aa4dcd2
commit
02ff38f2d7
@ -39,7 +39,7 @@ class TypeArrayKlass;
|
||||
// It also decides what Klasses must be cached in aot-initialized state.
|
||||
//
|
||||
// ArchiveBuilder uses [1] as roots to scan for all MetaspaceObjs that need to be cached.
|
||||
// ArchiveHeapWriter uses [2] to create an image of the archived heap.
|
||||
// HeapShared uses [2] to create an image of the archived heap.
|
||||
//
|
||||
// [1] is stored in _all_cached_classes in aotArtifactFinder.cpp.
|
||||
// [2] is stored in HeapShared::archived_object_cache().
|
||||
|
||||
@ -796,7 +796,7 @@ void AOTMapLogger::dumptime_log_mapped_heap_region(ArchiveMappedHeapInfo* heap_i
|
||||
address buffer_start = address(r.start()); // start of the current oop inside the buffer
|
||||
address buffer_end = address(r.end());
|
||||
|
||||
address requested_base = UseCompressedOops ? (address)CompressedOops::base() : (address)AOTMappedHeapWriter::NOCOOPS_REQUESTED_BASE;
|
||||
address requested_base = UseCompressedOops ? AOTMappedHeapWriter::narrow_oop_base() : (address)AOTMappedHeapWriter::NOCOOPS_REQUESTED_BASE;
|
||||
address requested_start = UseCompressedOops ? AOTMappedHeapWriter::buffered_addr_to_requested_addr(buffer_start) : requested_base;
|
||||
|
||||
log_region_range("heap", buffer_start, buffer_end, requested_start);
|
||||
|
||||
@ -55,7 +55,7 @@
|
||||
|
||||
GrowableArrayCHeap<u1, mtClassShared>* AOTMappedHeapWriter::_buffer = nullptr;
|
||||
|
||||
// The following are offsets from buffer_bottom()
|
||||
bool AOTMappedHeapWriter::_is_writing_deterministic_heap = false;
|
||||
size_t AOTMappedHeapWriter::_buffer_used;
|
||||
|
||||
// Heap root segments
|
||||
@ -74,7 +74,7 @@ AOTMappedHeapWriter::_buffer_offset_to_source_obj_table = nullptr;
|
||||
DumpedInternedStrings *AOTMappedHeapWriter::_dumped_interned_strings = nullptr;
|
||||
|
||||
typedef HashTable<
|
||||
size_t, // offset of a filler from ArchiveHeapWriter::buffer_bottom()
|
||||
size_t, // offset of a filler from AOTMappedHeapWriter::buffer_bottom()
|
||||
size_t, // size of this filler (in bytes)
|
||||
127, // prime number
|
||||
AnyObj::C_HEAP,
|
||||
@ -96,6 +96,45 @@ void AOTMappedHeapWriter::init() {
|
||||
_source_objs = new GrowableArrayCHeap<oop, mtClassShared>(10000);
|
||||
|
||||
guarantee(MIN_GC_REGION_ALIGNMENT <= G1HeapRegion::min_region_size_in_words() * HeapWordSize, "must be");
|
||||
|
||||
if (CDSConfig::old_cds_flags_used()) {
|
||||
// With the old CDS workflow, we can guatantee determninistic output: given
|
||||
// the same classlist file, we can generate the same static CDS archive.
|
||||
// To ensure determinism, we always use the same compressed oop encoding
|
||||
// (zero-based, no shift). See set_requested_address_range().
|
||||
_is_writing_deterministic_heap = true;
|
||||
} else {
|
||||
// Determninistic output is not supported by the new AOT workflow, so
|
||||
// we don't force the (zero-based, no shift) encoding. This way, it is more
|
||||
// likely that we can avoid oop relocation in the production run.
|
||||
_is_writing_deterministic_heap = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// For AOTMappedHeapWriter::narrow_oop_{mode, base, shift}(), see comments
|
||||
// in AOTMappedHeapWriter::set_requested_address_range(),
|
||||
CompressedOops::Mode AOTMappedHeapWriter::narrow_oop_mode() {
|
||||
if (is_writing_deterministic_heap()) {
|
||||
return CompressedOops::UnscaledNarrowOop;
|
||||
} else {
|
||||
return CompressedOops::mode();
|
||||
}
|
||||
}
|
||||
|
||||
address AOTMappedHeapWriter::narrow_oop_base() {
|
||||
if (is_writing_deterministic_heap()) {
|
||||
return (address)0;
|
||||
} else {
|
||||
return CompressedOops::base();
|
||||
}
|
||||
}
|
||||
|
||||
int AOTMappedHeapWriter::narrow_oop_shift() {
|
||||
if (is_writing_deterministic_heap()) {
|
||||
return 0;
|
||||
} else {
|
||||
return CompressedOops::shift();
|
||||
}
|
||||
}
|
||||
|
||||
@ -116,7 +155,7 @@ void AOTMappedHeapWriter::write(GrowableArrayCHeap<oop, mtClassShared>* roots,
|
||||
assert(CDSConfig::is_dumping_heap(), "sanity");
|
||||
allocate_buffer();
|
||||
copy_source_objs_to_buffer(roots);
|
||||
set_requested_address(heap_info);
|
||||
set_requested_address_range(heap_info);
|
||||
relocate_embedded_oops(roots, heap_info);
|
||||
}
|
||||
|
||||
@ -536,14 +575,55 @@ size_t AOTMappedHeapWriter::copy_one_source_obj_to_buffer(oop src_obj) {
|
||||
return buffered_obj_offset;
|
||||
}
|
||||
|
||||
void AOTMappedHeapWriter::set_requested_address(ArchiveMappedHeapInfo* info) {
|
||||
// Set the range [_requested_bottom, _requested_top), the requested address range of all
|
||||
// the archived heap objects in the production run.
|
||||
//
|
||||
// (1) UseCompressedOops == true && !is_writing_deterministic_heap()
|
||||
//
|
||||
// The archived objects are stored using the COOPS encoding of the assembly phase.
|
||||
// We pick a range within the heap used by the assembly phase.
|
||||
//
|
||||
// In the production run, if different COOPS encodings are used:
|
||||
// - The heap contents needs to be relocated.
|
||||
//
|
||||
// (2) UseCompressedOops == true && is_writing_deterministic_heap()
|
||||
//
|
||||
// We always use zero-based, zero-shift encoding. _requested_top is aligned to 0x10000000.
|
||||
//
|
||||
// (3) UseCompressedOops == false:
|
||||
//
|
||||
// In the production run, the heap range is usually picked (randomly) by the OS, so we
|
||||
// will almost always need to perform relocation, regardless of how we pick the requested
|
||||
// address range.
|
||||
//
|
||||
// So we just hard code it to NOCOOPS_REQUESTED_BASE.
|
||||
//
|
||||
void AOTMappedHeapWriter::set_requested_address_range(ArchiveMappedHeapInfo* info) {
|
||||
assert(!info->is_used(), "only set once");
|
||||
|
||||
size_t heap_region_byte_size = _buffer_used;
|
||||
assert(heap_region_byte_size > 0, "must archived at least one object!");
|
||||
|
||||
if (UseCompressedOops) {
|
||||
if (UseG1GC) {
|
||||
if (is_writing_deterministic_heap()) {
|
||||
// Pick a heap range so that requested addresses can be encoded with zero-base/no shift.
|
||||
// We align the requested bottom to at least 1 MB: if the production run uses G1 with a small
|
||||
// heap (e.g., -Xmx256m), it's likely that we can map the archived objects at the
|
||||
// requested location to avoid relocation.
|
||||
//
|
||||
// For other collectors or larger heaps, relocation is unavoidable, but is usually
|
||||
// quite cheap. If you really want to avoid relocation, use the AOT workflow instead.
|
||||
address heap_end = (address)0x100000000;
|
||||
size_t alignment = MAX2(MIN_GC_REGION_ALIGNMENT, 1024 * 1024);
|
||||
if (align_up(heap_region_byte_size, alignment) >= (size_t)heap_end) {
|
||||
log_error(aot, heap)("cached heap space is too large: %zu bytes", heap_region_byte_size);
|
||||
AOTMetaspace::unrecoverable_writing_error();
|
||||
}
|
||||
_requested_bottom = align_down(heap_end - heap_region_byte_size, alignment);
|
||||
} else if (UseG1GC) {
|
||||
// For G1, pick the range at the top of the current heap. If the exact same heap sizes
|
||||
// are used in the production run, it's likely that we can map the archived objects
|
||||
// at the requested location to avoid relocation.
|
||||
address heap_end = (address)G1CollectedHeap::heap()->reserved().end();
|
||||
log_info(aot, heap)("Heap end = %p", heap_end);
|
||||
_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) {
|
||||
|
||||
template <typename T> void AOTMappedHeapWriter::relocate_field_in_buffer(T* field_addr_in_buffer, oop source_referent, CHeapBitMap* oopmap) {
|
||||
oop request_referent = source_obj_to_requested_obj(source_referent);
|
||||
store_requested_oop_in_buffer<T>(field_addr_in_buffer, request_referent);
|
||||
if (UseCompressedOops && is_writing_deterministic_heap()) {
|
||||
// We use zero-based, 0-shift encoding, so the narrowOop is just the lower
|
||||
// 32 bits of request_referent
|
||||
intptr_t addr = cast_from_oop<intptr_t>(request_referent);
|
||||
*((narrowOop*)field_addr_in_buffer) = checked_cast<narrowOop>(addr);
|
||||
} else {
|
||||
store_requested_oop_in_buffer<T>(field_addr_in_buffer, request_referent);
|
||||
}
|
||||
if (request_referent != nullptr) {
|
||||
mark_oop_pointer<T>(field_addr_in_buffer, oopmap);
|
||||
}
|
||||
@ -918,9 +1005,9 @@ AOTMapLogger::OopDataIterator* AOTMappedHeapWriter::oop_iterator(ArchiveMappedHe
|
||||
address buffer_start = address(r.start());
|
||||
address buffer_end = address(r.end());
|
||||
|
||||
address requested_base = UseCompressedOops ? (address)CompressedOops::base() : (address)AOTMappedHeapWriter::NOCOOPS_REQUESTED_BASE;
|
||||
address requested_start = UseCompressedOops ? buffered_addr_to_requested_addr(buffer_start) : requested_base;
|
||||
int requested_shift = CompressedOops::shift();
|
||||
address requested_base = UseCompressedOops ? AOTMappedHeapWriter::narrow_oop_base() : (address)AOTMappedHeapWriter::NOCOOPS_REQUESTED_BASE;
|
||||
address requested_start = UseCompressedOops ? AOTMappedHeapWriter::buffered_addr_to_requested_addr(buffer_start) : requested_base;
|
||||
int requested_shift = AOTMappedHeapWriter::narrow_oop_shift();
|
||||
intptr_t buffer_to_requested_delta = requested_start - buffer_start;
|
||||
uint64_t buffer_start_narrow_oop = 0xdeadbeed;
|
||||
if (UseCompressedOops) {
|
||||
|
||||
@ -29,6 +29,7 @@
|
||||
#include "cds/heapShared.hpp"
|
||||
#include "memory/allocation.hpp"
|
||||
#include "memory/allStatic.hpp"
|
||||
#include "oops/compressedOops.hpp"
|
||||
#include "oops/oopHandle.hpp"
|
||||
#include "utilities/bitMap.hpp"
|
||||
#include "utilities/exceptions.hpp"
|
||||
@ -71,7 +72,7 @@ class AOTMappedHeapWriter : AllStatic {
|
||||
// These are entered into HeapShared::archived_object_cache().
|
||||
//
|
||||
// - "buffered objects" are copies of the "source objects", and are stored in into
|
||||
// ArchiveHeapWriter::_buffer, which is a GrowableArray that sits outside of
|
||||
// AOTMappedHeapWriter::_buffer, which is a GrowableArray that sits outside of
|
||||
// the valid heap range. Therefore we avoid using the addresses of these copies
|
||||
// as oops. They are usually called "buffered_addr" in the code (of the type "address").
|
||||
//
|
||||
@ -81,26 +82,11 @@ class AOTMappedHeapWriter : AllStatic {
|
||||
// - Each archived object has a "requested address" -- at run time, if the object
|
||||
// can be mapped at this address, we can avoid relocation.
|
||||
//
|
||||
// The requested address is implemented differently depending on UseCompressedOops:
|
||||
// The requested address of an archived object is essentially its buffered_addr + delta,
|
||||
// where delta is (_requested_bottom - buffer_bottom());
|
||||
//
|
||||
// UseCompressedOops == true:
|
||||
// The archived objects are stored assuming that the runtime COOPS compression
|
||||
// scheme is exactly the same as in dump time (or else a more expensive runtime relocation
|
||||
// would be needed.)
|
||||
//
|
||||
// At dump time, we assume that the runtime heap range is exactly the same as
|
||||
// in dump time. The requested addresses of the archived objects are chosen such that
|
||||
// they would occupy the top end of a G1 heap (TBD when dumping is supported by other
|
||||
// collectors. See JDK-8298614).
|
||||
//
|
||||
// UseCompressedOops == false:
|
||||
// At runtime, the heap range is usually picked (randomly) by the OS, so we will almost always
|
||||
// need to perform relocation. Hence, the goal of the "requested address" is to ensure that
|
||||
// the contents of the archived objects are deterministic. I.e., the oop fields of archived
|
||||
// objects will always point to deterministic addresses.
|
||||
//
|
||||
// For G1, the archived heap is written such that the lowest archived object is placed
|
||||
// at NOCOOPS_REQUESTED_BASE. (TBD after JDK-8298614).
|
||||
// The requested addresses of all archived objects are within [_requested_bottom, _requested_top).
|
||||
// See AOTMappedHeapWriter::set_requested_address_range() for more info.
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
public:
|
||||
@ -111,6 +97,15 @@ public:
|
||||
// Shenandoah heap region size can never be smaller than 256K.
|
||||
static constexpr int MIN_GC_REGION_ALIGNMENT = 256 * K;
|
||||
|
||||
// The heap contents are required to be deterministic when dumping "old" CDS archives, in order
|
||||
// to support reproducible lib/server/classes*.jsa when building the JDK.
|
||||
static bool is_writing_deterministic_heap() { return _is_writing_deterministic_heap; }
|
||||
|
||||
// The oop encoding used by the archived heap objects.
|
||||
static CompressedOops::Mode narrow_oop_mode();
|
||||
static address narrow_oop_base();
|
||||
static int narrow_oop_shift();
|
||||
|
||||
static const int INITIAL_TABLE_SIZE = 15889; // prime number
|
||||
static const int MAX_TABLE_SIZE = 1000000;
|
||||
|
||||
@ -121,6 +116,7 @@ private:
|
||||
int _field_offset;
|
||||
};
|
||||
|
||||
static bool _is_writing_deterministic_heap;
|
||||
static GrowableArrayCHeap<u1, mtClassShared>* _buffer;
|
||||
|
||||
// The number of bytes that have written into _buffer (may be smaller than _buffer->length()).
|
||||
@ -130,15 +126,15 @@ private:
|
||||
static HeapRootSegments _heap_root_segments;
|
||||
|
||||
// The address range of the requested location of the archived heap objects.
|
||||
static address _requested_bottom;
|
||||
static address _requested_top;
|
||||
static address _requested_bottom; // The requested address of the lowest archived heap object
|
||||
static address _requested_top; // The exclusive end of the highest archived heap object
|
||||
|
||||
static GrowableArrayCHeap<NativePointerInfo, mtClassShared>* _native_pointers;
|
||||
static GrowableArrayCHeap<oop, mtClassShared>* _source_objs;
|
||||
static DumpedInternedStrings *_dumped_interned_strings;
|
||||
|
||||
// We sort _source_objs_order to minimize the number of bits in ptrmap and oopmap.
|
||||
// See comments near the body of ArchiveHeapWriter::compare_objs_by_oop_fields().
|
||||
// See comments near the body of AOTMappedHeapWriter::compare_objs_by_oop_fields().
|
||||
// The objects will be written in the order of:
|
||||
//_source_objs->at(_source_objs_order->at(0)._index)
|
||||
// source_objs->at(_source_objs_order->at(1)._index)
|
||||
@ -200,7 +196,7 @@ private:
|
||||
static int filler_array_length(size_t fill_bytes);
|
||||
static HeapWord* init_filler_array_at_buffer_top(int array_length, size_t fill_bytes);
|
||||
|
||||
static void set_requested_address(ArchiveMappedHeapInfo* info);
|
||||
static void set_requested_address_range(ArchiveMappedHeapInfo* info);
|
||||
static void mark_native_pointers(oop orig_obj);
|
||||
static void relocate_embedded_oops(GrowableArrayCHeap<oop, mtClassShared>* roots, ArchiveMappedHeapInfo* info);
|
||||
static void compute_ptrmap(ArchiveMappedHeapInfo *info);
|
||||
|
||||
@ -216,12 +216,14 @@ void FileMapHeader::populate(FileMapInfo *info, size_t core_region_alignment,
|
||||
_obj_alignment = ObjectAlignmentInBytes;
|
||||
_compact_strings = CompactStrings;
|
||||
_compact_headers = UseCompactObjectHeaders;
|
||||
#if INCLUDE_CDS_JAVA_HEAP
|
||||
if (CDSConfig::is_dumping_heap()) {
|
||||
_object_streaming_mode = HeapShared::is_writing_streaming_mode();
|
||||
_narrow_oop_mode = CompressedOops::mode();
|
||||
_narrow_oop_base = CompressedOops::base();
|
||||
_narrow_oop_shift = CompressedOops::shift();
|
||||
_narrow_oop_mode = AOTMappedHeapWriter::narrow_oop_mode();
|
||||
_narrow_oop_base = AOTMappedHeapWriter::narrow_oop_base();
|
||||
_narrow_oop_shift = AOTMappedHeapWriter::narrow_oop_shift();
|
||||
}
|
||||
#endif
|
||||
_compressed_oops = UseCompressedOops;
|
||||
_compressed_class_ptrs = UseCompressedClassPointers;
|
||||
if (UseCompressedClassPointers) {
|
||||
@ -911,7 +913,7 @@ void FileMapInfo::write_region(int region, char* base, size_t size,
|
||||
if (HeapShared::is_writing_mapping_mode()) {
|
||||
requested_base = (char*)AOTMappedHeapWriter::requested_address();
|
||||
if (UseCompressedOops) {
|
||||
mapping_offset = (size_t)((address)requested_base - CompressedOops::base());
|
||||
mapping_offset = (size_t)((address)requested_base - AOTMappedHeapWriter::narrow_oop_base());
|
||||
assert((mapping_offset >> CompressedOops::shift()) << CompressedOops::shift() == mapping_offset, "must be");
|
||||
}
|
||||
} else {
|
||||
|
||||
@ -332,7 +332,7 @@ public:
|
||||
// Used by CDSHeapVerifier.
|
||||
OopHandle _orig_referrer;
|
||||
|
||||
// The location of this object inside ArchiveHeapWriter::_buffer
|
||||
// The location of this object inside {AOTMappedHeapWriter, AOTStreamedHeapWriter}::_buffer
|
||||
size_t _buffer_offset;
|
||||
|
||||
// One or more fields in this object are pointing to non-null oops.
|
||||
|
||||
@ -100,7 +100,6 @@ gc/shenandoah/TestSieveObjects.java#no-tlab-genshen 8361099 generic-all
|
||||
|
||||
# :hotspot_runtime
|
||||
|
||||
runtime/cds/DeterministicDump.java 8363986 macosx-x64,macosx-aarch64
|
||||
runtime/jni/terminatedThread/TestTerminatedThread.java 8317789 aix-ppc64
|
||||
runtime/Monitor/SyncOnValueBasedClassTest.java 8340995 linux-s390x
|
||||
runtime/os/TestTracePageSizes.java#no-options 8267460 linux-aarch64
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user