Skip to content

Commit dc00eb8

Browse files
committed
8338912: CDS: Segmented roots array
Reviewed-by: ccheung, iklam
1 parent 74add0e commit dc00eb8

10 files changed

+221
-103
lines changed

src/hotspot/share/cds/archiveBuilder.cpp

+11-8
Original file line numberDiff line numberDiff line change
@@ -1104,6 +1104,17 @@ class ArchiveBuilder::CDSMapLogger : AllStatic {
11041104

11051105
LogStreamHandle(Info, cds, map) st;
11061106

1107+
HeapRootSegments segments = heap_info->heap_root_segments();
1108+
assert(segments.base_offset() == 0, "Sanity");
1109+
1110+
for (size_t seg_idx = 0; seg_idx < segments.count(); seg_idx++) {
1111+
address requested_start = ArchiveHeapWriter::buffered_addr_to_requested_addr(start);
1112+
st.print_cr(PTR_FORMAT ": Heap roots segment [%d]",
1113+
p2i(requested_start), segments.size_in_elems(seg_idx));
1114+
start += segments.size_in_bytes(seg_idx);
1115+
}
1116+
log_heap_roots();
1117+
11071118
while (start < end) {
11081119
size_t byte_size;
11091120
oop source_oop = ArchiveHeapWriter::buffered_addr_to_source_obj(start);
@@ -1114,12 +1125,6 @@ class ArchiveBuilder::CDSMapLogger : AllStatic {
11141125
// This is a regular oop that got archived.
11151126
print_oop_with_requested_addr_cr(&st, source_oop, false);
11161127
byte_size = source_oop->size() * BytesPerWord;
1117-
} else if (start == ArchiveHeapWriter::buffered_heap_roots_addr()) {
1118-
// HeapShared::roots() is copied specially, so it doesn't exist in
1119-
// ArchiveHeapWriter::BufferOffsetToSourceObjectTable.
1120-
// See ArchiveHeapWriter::copy_roots_to_buffer().
1121-
st.print_cr("HeapShared::roots[%d]", HeapShared::pending_roots()->length());
1122-
byte_size = ArchiveHeapWriter::heap_roots_word_size() * BytesPerWord;
11231128
} else if ((byte_size = ArchiveHeapWriter::get_filler_size_at(start)) > 0) {
11241129
// We have a filler oop, which also does not exist in BufferOffsetToSourceObjectTable.
11251130
st.print_cr("filler " SIZE_FORMAT " bytes", byte_size);
@@ -1132,8 +1137,6 @@ class ArchiveBuilder::CDSMapLogger : AllStatic {
11321137

11331138
if (source_oop != nullptr) {
11341139
log_oop_details(heap_info, source_oop, /*buffered_addr=*/start);
1135-
} else if (start == ArchiveHeapWriter::buffered_heap_roots_addr()) {
1136-
log_heap_roots();
11371140
}
11381141
start = oop_end;
11391142
}

src/hotspot/share/cds/archiveHeapLoader.cpp

+12-2
Original file line numberDiff line numberDiff line change
@@ -374,8 +374,18 @@ void ArchiveHeapLoader::finish_initialization() {
374374
if (is_in_use()) {
375375
patch_native_pointers();
376376
intptr_t bottom = is_loaded() ? _loaded_heap_bottom : _mapped_heap_bottom;
377-
intptr_t roots_oop = bottom + FileMapInfo::current_info()->heap_roots_offset();
378-
HeapShared::init_roots(cast_to_oop(roots_oop));
377+
378+
// The heap roots are stored in one or more segments that are laid out consecutively.
379+
// The byte size of each segment (except for the last one) is max_size.
380+
HeapRootSegments segments = FileMapInfo::current_info()->heap_root_segments();
381+
int max_size = segments.max_size_in_bytes();
382+
HeapShared::init_root_segment_sizes(max_size);
383+
intptr_t first_segment_addr = bottom + segments.base_offset();
384+
for (size_t c = 0; c < segments.count(); c++) {
385+
oop segment_oop = cast_to_oop(first_segment_addr + (c * max_size));
386+
assert(segment_oop->is_objArray(), "Must be");
387+
HeapShared::add_root_segment((objArrayOop)segment_oop);
388+
}
379389
}
380390
}
381391

src/hotspot/share/cds/archiveHeapWriter.cpp

+81-55
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,9 @@ GrowableArrayCHeap<u1, mtClassShared>* ArchiveHeapWriter::_buffer = nullptr;
5252

5353
// The following are offsets from buffer_bottom()
5454
size_t ArchiveHeapWriter::_buffer_used;
55-
size_t ArchiveHeapWriter::_heap_roots_offset;
5655

57-
size_t ArchiveHeapWriter::_heap_roots_word_size;
56+
// Heap root segments
57+
HeapRootSegments ArchiveHeapWriter::_heap_root_segments;
5858

5959
address ArchiveHeapWriter::_requested_bottom;
6060
address ArchiveHeapWriter::_requested_top;
@@ -164,10 +164,6 @@ address ArchiveHeapWriter::buffered_addr_to_requested_addr(address buffered_addr
164164
return _requested_bottom + buffered_address_to_offset(buffered_addr);
165165
}
166166

167-
oop ArchiveHeapWriter::heap_roots_requested_address() {
168-
return cast_to_oop(_requested_bottom + _heap_roots_offset);
169-
}
170-
171167
address ArchiveHeapWriter::requested_address() {
172168
assert(_buffer != nullptr, "must be initialized");
173169
return _requested_bottom;
@@ -186,47 +182,71 @@ void ArchiveHeapWriter::ensure_buffer_space(size_t min_bytes) {
186182
_buffer->at_grow(to_array_index(min_bytes));
187183
}
188184

189-
void ArchiveHeapWriter::copy_roots_to_buffer(GrowableArrayCHeap<oop, mtClassShared>* roots) {
190-
Klass* k = Universe::objectArrayKlass(); // already relocated to point to archived klass
191-
int length = roots->length();
192-
_heap_roots_word_size = objArrayOopDesc::object_size(length);
193-
size_t byte_size = _heap_roots_word_size * HeapWordSize;
194-
if (byte_size >= MIN_GC_REGION_ALIGNMENT) {
195-
log_error(cds, heap)("roots array is too large. Please reduce the number of classes");
196-
vm_exit(1);
197-
}
185+
objArrayOop ArchiveHeapWriter::allocate_root_segment(size_t offset, int element_count) {
186+
HeapWord* mem = offset_to_buffered_address<HeapWord *>(offset);
187+
memset(mem, 0, objArrayOopDesc::object_size(element_count));
198188

199-
maybe_fill_gc_region_gap(byte_size);
200-
201-
size_t new_used = _buffer_used + byte_size;
202-
ensure_buffer_space(new_used);
189+
// The initialization code is copied from MemAllocator::finish and ObjArrayAllocator::initialize.
190+
oopDesc::set_mark(mem, markWord::prototype());
191+
oopDesc::release_set_klass(mem, Universe::objectArrayKlass());
192+
arrayOopDesc::set_length(mem, element_count);
193+
return objArrayOop(cast_to_oop(mem));
194+
}
203195

204-
HeapWord* mem = offset_to_buffered_address<HeapWord*>(_buffer_used);
205-
memset(mem, 0, byte_size);
206-
{
207-
// This is copied from MemAllocator::finish
208-
oopDesc::set_mark(mem, markWord::prototype());
209-
oopDesc::release_set_klass(mem, k);
210-
}
211-
{
212-
// This is copied from ObjArrayAllocator::initialize
213-
arrayOopDesc::set_length(mem, length);
196+
void ArchiveHeapWriter::root_segment_at_put(objArrayOop segment, int index, oop root) {
197+
// Do not use arrayOop->obj_at_put(i, o) as arrayOop is outside the real heap!
198+
if (UseCompressedOops) {
199+
*segment->obj_at_addr<narrowOop>(index) = CompressedOops::encode(root);
200+
} else {
201+
*segment->obj_at_addr<oop>(index) = root;
214202
}
203+
}
215204

216-
objArrayOop arrayOop = objArrayOop(cast_to_oop(mem));
217-
for (int i = 0; i < length; i++) {
218-
// Do not use arrayOop->obj_at_put(i, o) as arrayOop is outside of the real heap!
219-
oop o = roots->at(i);
220-
if (UseCompressedOops) {
221-
* arrayOop->obj_at_addr<narrowOop>(i) = CompressedOops::encode(o);
222-
} else {
223-
* arrayOop->obj_at_addr<oop>(i) = o;
205+
void ArchiveHeapWriter::copy_roots_to_buffer(GrowableArrayCHeap<oop, mtClassShared>* roots) {
206+
// Depending on the number of classes we are archiving, a single roots array may be
207+
// larger than MIN_GC_REGION_ALIGNMENT. Roots are allocated first in the buffer, which
208+
// allows us to chop the large array into a series of "segments". Current layout
209+
// starts with zero or more segments exactly fitting MIN_GC_REGION_ALIGNMENT, and end
210+
// with a single segment that may be smaller than MIN_GC_REGION_ALIGNMENT.
211+
// This is simple and efficient. We do not need filler objects anywhere between the segments,
212+
// or immediately after the last segment. This allows starting the object dump immediately
213+
// after the roots.
214+
215+
assert((_buffer_used % MIN_GC_REGION_ALIGNMENT) == 0,
216+
"Pre-condition: Roots start at aligned boundary: " SIZE_FORMAT, _buffer_used);
217+
218+
int max_elem_count = ((MIN_GC_REGION_ALIGNMENT - arrayOopDesc::header_size_in_bytes()) / heapOopSize);
219+
assert(objArrayOopDesc::object_size(max_elem_count)*HeapWordSize == MIN_GC_REGION_ALIGNMENT,
220+
"Should match exactly");
221+
222+
HeapRootSegments segments(_buffer_used,
223+
roots->length(),
224+
MIN_GC_REGION_ALIGNMENT,
225+
max_elem_count);
226+
227+
for (size_t seg_idx = 0; seg_idx < segments.count(); seg_idx++) {
228+
int size_elems = segments.size_in_elems(seg_idx);
229+
size_t size_bytes = segments.size_in_bytes(seg_idx);
230+
231+
size_t oop_offset = _buffer_used;
232+
_buffer_used = oop_offset + size_bytes;
233+
ensure_buffer_space(_buffer_used);
234+
235+
assert((oop_offset % MIN_GC_REGION_ALIGNMENT) == 0,
236+
"Roots segment " SIZE_FORMAT " start is not aligned: " SIZE_FORMAT,
237+
segments.count(), oop_offset);
238+
239+
int root_index = 0;
240+
objArrayOop seg_oop = allocate_root_segment(oop_offset, size_elems);
241+
for (int i = 0; i < size_elems; i++) {
242+
root_segment_at_put(seg_oop, i, roots->at(root_index++));
224243
}
244+
245+
log_info(cds, heap)("archived obj root segment [%d] = " SIZE_FORMAT " bytes, obj = " PTR_FORMAT,
246+
size_elems, size_bytes, p2i(seg_oop));
225247
}
226-
log_info(cds, heap)("archived obj roots[%d] = " SIZE_FORMAT " bytes, klass = %p, obj = %p", length, byte_size, k, mem);
227248

228-
_heap_roots_offset = _buffer_used;
229-
_buffer_used = new_used;
249+
_heap_root_segments = segments;
230250
}
231251

232252
static int oop_sorting_rank(oop o) {
@@ -282,6 +302,10 @@ void ArchiveHeapWriter::sort_source_objs() {
282302
}
283303

284304
void ArchiveHeapWriter::copy_source_objs_to_buffer(GrowableArrayCHeap<oop, mtClassShared>* roots) {
305+
// There could be multiple root segments, which we want to be aligned by region.
306+
// Putting them ahead of objects makes sure we waste no space.
307+
copy_roots_to_buffer(roots);
308+
285309
sort_source_objs();
286310
for (int i = 0; i < _source_objs_order->length(); i++) {
287311
int src_obj_index = _source_objs_order->at(i)._index;
@@ -295,8 +319,6 @@ void ArchiveHeapWriter::copy_source_objs_to_buffer(GrowableArrayCHeap<oop, mtCla
295319
_buffer_offset_to_source_obj_table->maybe_grow();
296320
}
297321

298-
copy_roots_to_buffer(roots);
299-
300322
log_info(cds)("Size of heap region = " SIZE_FORMAT " bytes, %d objects, %d roots, %d native ptrs",
301323
_buffer_used, _source_objs->length() + 1, roots->length(), _num_native_ptrs);
302324
}
@@ -455,7 +477,7 @@ void ArchiveHeapWriter::set_requested_address(ArchiveHeapInfo* info) {
455477

456478
info->set_buffer_region(MemRegion(offset_to_buffered_address<HeapWord*>(0),
457479
offset_to_buffered_address<HeapWord*>(_buffer_used)));
458-
info->set_heap_roots_offset(_heap_roots_offset);
480+
info->set_heap_root_segments(_heap_root_segments);
459481
}
460482

461483
// Oop relocation
@@ -543,12 +565,6 @@ void ArchiveHeapWriter::update_header_for_requested_obj(oop requested_obj, oop s
543565
}
544566
}
545567

546-
// Relocate an element in the buffered copy of HeapShared::roots()
547-
template <typename T> void ArchiveHeapWriter::relocate_root_at(oop requested_roots, int index, CHeapBitMap* oopmap) {
548-
size_t offset = (size_t)((objArrayOop)requested_roots)->obj_at_offset<T>(index);
549-
relocate_field_in_buffer<T>((T*)(buffered_heap_roots_addr() + offset), oopmap);
550-
}
551-
552568
class ArchiveHeapWriter::EmbeddedOopRelocator: public BasicOopIterateClosure {
553569
oop _src_obj;
554570
address _buffered_obj;
@@ -600,14 +616,24 @@ void ArchiveHeapWriter::relocate_embedded_oops(GrowableArrayCHeap<oop, mtClassSh
600616

601617
// Relocate HeapShared::roots(), which is created in copy_roots_to_buffer() and
602618
// doesn't have a corresponding src_obj, so we can't use EmbeddedOopRelocator on it.
603-
oop requested_roots = requested_obj_from_buffer_offset(_heap_roots_offset);
604-
update_header_for_requested_obj(requested_roots, nullptr, Universe::objectArrayKlass());
605-
int length = roots != nullptr ? roots->length() : 0;
606-
for (int i = 0; i < length; i++) {
619+
for (size_t seg_idx = 0; seg_idx < _heap_root_segments.count(); seg_idx++) {
620+
size_t seg_offset = _heap_root_segments.segment_offset(seg_idx);
621+
622+
objArrayOop requested_obj = (objArrayOop)requested_obj_from_buffer_offset(seg_offset);
623+
update_header_for_requested_obj(requested_obj, nullptr, Universe::objectArrayKlass());
624+
address buffered_obj = offset_to_buffered_address<address>(seg_offset);
625+
int length = _heap_root_segments.size_in_elems(seg_idx);
626+
607627
if (UseCompressedOops) {
608-
relocate_root_at<narrowOop>(requested_roots, i, heap_info->oopmap());
628+
for (int i = 0; i < length; i++) {
629+
narrowOop* addr = (narrowOop*)(buffered_obj + objArrayOopDesc::obj_at_offset<narrowOop>(i));
630+
relocate_field_in_buffer<narrowOop>(addr, heap_info->oopmap());
631+
}
609632
} else {
610-
relocate_root_at<oop>(requested_roots, i, heap_info->oopmap());
633+
for (int i = 0; i < length; i++) {
634+
oop* addr = (oop*)(buffered_obj + objArrayOopDesc::obj_at_offset<oop>(i));
635+
relocate_field_in_buffer<oop>(addr, heap_info->oopmap());
636+
}
611637
}
612638
}
613639

src/hotspot/share/cds/archiveHeapWriter.hpp

+7-15
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,7 @@ class ArchiveHeapInfo {
4141
MemRegion _buffer_region; // Contains the archived objects to be written into the CDS archive.
4242
CHeapBitMap _oopmap;
4343
CHeapBitMap _ptrmap;
44-
size_t _heap_roots_offset; // Offset of the HeapShared::roots() object, from the bottom
45-
// of the archived heap objects, in bytes.
44+
HeapRootSegments _heap_root_segments;
4645

4746
public:
4847
ArchiveHeapInfo() : _buffer_region(), _oopmap(128, mtClassShared), _ptrmap(128, mtClassShared) {}
@@ -57,8 +56,8 @@ class ArchiveHeapInfo {
5756
CHeapBitMap* oopmap() { return &_oopmap; }
5857
CHeapBitMap* ptrmap() { return &_ptrmap; }
5958

60-
void set_heap_roots_offset(size_t n) { _heap_roots_offset = n; }
61-
size_t heap_roots_offset() const { return _heap_roots_offset; }
59+
void set_heap_root_segments(HeapRootSegments segments) { _heap_root_segments = segments; };
60+
HeapRootSegments heap_root_segments() { return _heap_root_segments; }
6261
};
6362

6463
#if INCLUDE_CDS_JAVA_HEAP
@@ -130,9 +129,8 @@ class ArchiveHeapWriter : AllStatic {
130129
// The number of bytes that have written into _buffer (may be smaller than _buffer->length()).
131130
static size_t _buffer_used;
132131

133-
// The bottom of the copy of Heap::roots() inside this->_buffer.
134-
static size_t _heap_roots_offset;
135-
static size_t _heap_roots_word_size;
132+
// The heap root segments information.
133+
static HeapRootSegments _heap_root_segments;
136134

137135
// The address range of the requested location of the archived heap objects.
138136
static address _requested_bottom;
@@ -193,6 +191,8 @@ class ArchiveHeapWriter : AllStatic {
193191
return buffered_addr - buffer_bottom();
194192
}
195193

194+
static void root_segment_at_put(objArrayOop segment, int index, oop root);
195+
static objArrayOop allocate_root_segment(size_t offset, int element_count);
196196
static void copy_roots_to_buffer(GrowableArrayCHeap<oop, mtClassShared>* roots);
197197
static void copy_source_objs_to_buffer(GrowableArrayCHeap<oop, mtClassShared>* roots);
198198
static size_t copy_one_source_obj_to_buffer(oop src_obj);
@@ -219,7 +219,6 @@ class ArchiveHeapWriter : AllStatic {
219219
template <typename T> static T* requested_addr_to_buffered_addr(T* p);
220220
template <typename T> static void relocate_field_in_buffer(T* field_addr_in_buffer, CHeapBitMap* oopmap);
221221
template <typename T> static void mark_oop_pointer(T* buffered_addr, CHeapBitMap* oopmap);
222-
template <typename T> static void relocate_root_at(oop requested_roots, int index, CHeapBitMap* oopmap);
223222

224223
static void update_header_for_requested_obj(oop requested_obj, oop src_obj, Klass* src_klass);
225224

@@ -234,13 +233,6 @@ class ArchiveHeapWriter : AllStatic {
234233
static bool is_string_too_large_to_archive(oop string);
235234
static void write(GrowableArrayCHeap<oop, mtClassShared>*, ArchiveHeapInfo* heap_info);
236235
static address requested_address(); // requested address of the lowest achived heap object
237-
static oop heap_roots_requested_address(); // requested address of HeapShared::roots()
238-
static address buffered_heap_roots_addr() {
239-
return offset_to_buffered_address<address>(_heap_roots_offset);
240-
}
241-
static size_t heap_roots_word_size() {
242-
return _heap_roots_word_size;
243-
}
244236
static size_t get_filler_size_at(address buffered_addr);
245237

246238
static void mark_native_pointer(oop src_obj, int offset);

src/hotspot/share/cds/archiveUtils.cpp

+21
Original file line numberDiff line numberDiff line change
@@ -369,3 +369,24 @@ void ArchiveUtils::log_to_classlist(BootstrapInfo* bootstrap_specifier, TRAPS) {
369369
}
370370
}
371371
}
372+
373+
size_t HeapRootSegments::size_in_bytes(size_t seg_idx) {
374+
assert(seg_idx < _count, "In range");
375+
return objArrayOopDesc::object_size(size_in_elems(seg_idx)) * HeapWordSize;
376+
}
377+
378+
int HeapRootSegments::size_in_elems(size_t seg_idx) {
379+
assert(seg_idx < _count, "In range");
380+
if (seg_idx != _count - 1) {
381+
return _max_size_in_elems;
382+
} else {
383+
// Last slice, leftover
384+
return _roots_count % _max_size_in_elems;
385+
}
386+
}
387+
388+
size_t HeapRootSegments::segment_offset(size_t seg_idx) {
389+
assert(seg_idx < _count, "In range");
390+
return _base_offset + seg_idx * _max_size_in_bytes;
391+
}
392+

src/hotspot/share/cds/archiveUtils.hpp

+41
Original file line numberDiff line numberDiff line change
@@ -250,4 +250,45 @@ class ArchiveUtils {
250250
static void log_to_classlist(BootstrapInfo* bootstrap_specifier, TRAPS) NOT_CDS_RETURN;
251251
};
252252

253+
class HeapRootSegments {
254+
private:
255+
size_t _base_offset;
256+
size_t _count;
257+
int _roots_count;
258+
int _max_size_in_bytes;
259+
int _max_size_in_elems;
260+
261+
public:
262+
size_t base_offset() { return _base_offset; }
263+
size_t count() { return _count; }
264+
int roots_count() { return _roots_count; }
265+
int max_size_in_bytes() { return _max_size_in_bytes; }
266+
int max_size_in_elems() { return _max_size_in_elems; }
267+
268+
size_t size_in_bytes(size_t seg_idx);
269+
int size_in_elems(size_t seg_idx);
270+
size_t segment_offset(size_t seg_idx);
271+
272+
// Trivial copy assignments are allowed to copy the entire object representation.
273+
// We also inline this class into archive header. Therefore, it is important to make
274+
// sure any gaps in object representation are initialized to zeroes. This is why
275+
// constructors memset before doing field assignments.
276+
HeapRootSegments() {
277+
memset(this, 0, sizeof(*this));
278+
}
279+
HeapRootSegments(size_t base_offset, int roots_count, int max_size_in_bytes, int max_size_in_elems) {
280+
assert(is_power_of_2(max_size_in_bytes), "must be");
281+
memset(this, 0, sizeof(*this));
282+
_base_offset = base_offset;
283+
_count = (roots_count + max_size_in_elems - 1) / max_size_in_elems;
284+
_roots_count = roots_count;
285+
_max_size_in_bytes = max_size_in_bytes;
286+
_max_size_in_elems = max_size_in_elems;
287+
}
288+
289+
// This class is trivially copyable and assignable.
290+
HeapRootSegments(const HeapRootSegments&) = default;
291+
HeapRootSegments& operator=(const HeapRootSegments&) = default;
292+
};
293+
253294
#endif // SHARE_CDS_ARCHIVEUTILS_HPP

0 commit comments

Comments
 (0)