Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/hotspot/share/cds/aotArtifactFinder.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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().
Expand Down
9 changes: 8 additions & 1 deletion src/hotspot/share/cds/archiveBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1504,7 +1504,14 @@ class ArchiveBuilder::CDSMapLogger : AllStatic {
st->print(PTR_FORMAT " ", p2i(requested_obj));
}
if (UseCompressedOops) {
st->print("(0x%08x) ", CompressedOops::narrow_oop_value(requested_obj));
uint32_t narrow_oop;
if (ArchiveHeapWriter::is_writing_deterministic_heap()) {
// Don't decode in case of deterministic heap.
narrow_oop = checked_cast<uint32_t>(cast_from_oop<intptr_t>(requested_obj));
} else {
narrow_oop = CompressedOops::narrow_oop_value(requested_obj);
}
st->print("(0x%08x) ", narrow_oop);
}
if (source_oop->is_array()) {
int array_len = arrayOop(source_oop)->length();
Expand Down
97 changes: 92 additions & 5 deletions src/hotspot/share/cds/archiveHeapWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@

GrowableArrayCHeap<u1, mtClassShared>* ArchiveHeapWriter::_buffer = nullptr;

// The following are offsets from buffer_bottom()
bool ArchiveHeapWriter::_is_writing_deterministic_heap = false;
size_t ArchiveHeapWriter::_buffer_used;

// Heap root segments
Expand Down Expand Up @@ -91,6 +91,45 @@ void ArchiveHeapWriter::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 ArchiveHeapWriter::narrow_oop_{mode, base, shift}(), see comments
// in ArchiveHeapWriter::set_requested_address_range(),
CompressedOops::Mode ArchiveHeapWriter::narrow_oop_mode() {
if (is_writing_deterministic_heap()) {
return CompressedOops::UnscaledNarrowOop;
} else {
return CompressedOops::mode();
}
}

address ArchiveHeapWriter::narrow_oop_base() {
if (is_writing_deterministic_heap()) {
return (address)0;
} else {
return CompressedOops::base();
}
}

int ArchiveHeapWriter::narrow_oop_shift() {
if (is_writing_deterministic_heap()) {
return 0;
} else {
return CompressedOops::shift();
}
}

Expand All @@ -103,7 +142,7 @@ void ArchiveHeapWriter::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);
}

Expand Down Expand Up @@ -465,14 +504,55 @@ size_t ArchiveHeapWriter::copy_one_source_obj_to_buffer(oop src_obj) {
return buffered_obj_offset;
}

void ArchiveHeapWriter::set_requested_address(ArchiveHeapInfo* 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 ArchiveHeapWriter::set_requested_address_range(ArchiveHeapInfo* 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);
MetaspaceShared::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);
Expand Down Expand Up @@ -550,7 +630,14 @@ template <typename T> void ArchiveHeapWriter::relocate_field_in_buffer(T* field_
assert(source_referent != nullptr, "must be");
}
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);
}
mark_oop_pointer<T>(field_addr_in_buffer, oopmap);
}
}
Expand Down
40 changes: 18 additions & 22 deletions src/hotspot/share/cds/archiveHeapWriter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,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"
Expand Down Expand Up @@ -87,26 +88,11 @@ class ArchiveHeapWriter : 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 ArchiveHeapWriter::set_requested_address_range() for more info.
// ----------------------------------------------------------------------

public:
Expand All @@ -117,13 +103,23 @@ class ArchiveHeapWriter : AllStatic {
// 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();

private:
class EmbeddedOopRelocator;
struct NativePointerInfo {
oop _src_obj;
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()).
Expand All @@ -133,8 +129,8 @@ class ArchiveHeapWriter : AllStatic {
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;
Expand Down Expand Up @@ -202,7 +198,7 @@ class ArchiveHeapWriter : AllStatic {
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(ArchiveHeapInfo* info);
static void set_requested_address_range(ArchiveHeapInfo* info);
static void relocate_embedded_oops(GrowableArrayCHeap<oop, mtClassShared>* roots, ArchiveHeapInfo* info);
static void compute_ptrmap(ArchiveHeapInfo *info);
static bool is_in_requested_range(oop o);
Expand Down
12 changes: 7 additions & 5 deletions src/hotspot/share/cds/filemap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -217,11 +217,13 @@ 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()) {
_narrow_oop_mode = CompressedOops::mode();
_narrow_oop_base = CompressedOops::base();
_narrow_oop_shift = CompressedOops::shift();
_narrow_oop_mode = ArchiveHeapWriter::narrow_oop_mode();
_narrow_oop_base = ArchiveHeapWriter::narrow_oop_base();
_narrow_oop_shift = ArchiveHeapWriter::narrow_oop_shift();
}
#endif
_compressed_oops = UseCompressedOops;
_compressed_class_ptrs = UseCompressedClassPointers;
if (UseCompressedClassPointers) {
Expand Down Expand Up @@ -901,7 +903,7 @@ void FileMapInfo::write_region(int region, char* base, size_t size,
assert(!CDSConfig::is_dumping_dynamic_archive(), "must be");
requested_base = (char*)ArchiveHeapWriter::requested_address();
if (UseCompressedOops) {
mapping_offset = (size_t)((address)requested_base - CompressedOops::base());
mapping_offset = (size_t)((address)requested_base - ArchiveHeapWriter::narrow_oop_base());
assert((mapping_offset >> CompressedOops::shift()) << CompressedOops::shift() == mapping_offset, "must be");
} else {
mapping_offset = 0; // not used with !UseCompressedOops
Expand Down Expand Up @@ -1605,7 +1607,7 @@ address FileMapInfo::heap_region_requested_address() {
// Runtime base = 0x4000 and shift is also 0. If we map this region at 0x5000, then
// the value P can remain 0x1200. The decoded address = (0x4000 + (0x1200 << 0)) = 0x5200,
// which is the runtime location of the referenced object.
return /*runtime*/ (address)((uintptr_t)CompressedOops::base() + r->mapping_offset());
return /*runtime*/ (address)((uintptr_t)ArchiveHeapWriter::narrow_oop_base() + r->mapping_offset());
} else {
// This was the hard-coded requested base address used at dump time. With uncompressed oops,
// the heap range is assigned by the OS so we will most likely have to relocate anyway, no matter
Expand Down
2 changes: 1 addition & 1 deletion src/hotspot/share/cds/heapShared.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ class HeapShared: AllStatic {
// Used by CDSHeapVerifier.
oop _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.
Expand Down
1 change: 0 additions & 1 deletion test/hotspot/jtreg/ProblemList.txt
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,6 @@ gc/shenandoah/TestEvilSyncBug.java#generational 8345501 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
Expand Down