Skip to content

Commit ac533a7

Browse files
committedNov 19, 2024
Fixed CDSAccess::get_archived_object_permanent_index() to only keep alive archived oops that have a permanent index (previously every archived oop was kept alive)
1 parent cd8c4f4 commit ac533a7

9 files changed

+160
-97
lines changed
 

‎src/hotspot/share/cds/archiveBuilder.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -1534,7 +1534,7 @@ void ArchiveBuilder::write_archive(FileMapInfo* mapinfo, ArchiveHeapInfo* heap_i
15341534

15351535
write_region(mapinfo, MetaspaceShared::rw, &_rw_region, /*read_only=*/false,/*allow_exec=*/false);
15361536
write_region(mapinfo, MetaspaceShared::ro, &_ro_region, /*read_only=*/true, /*allow_exec=*/false);
1537-
write_region(mapinfo, MetaspaceShared::cc, &_cc_region, /*read_only=*/false,/*allow_exec=*/true);
1537+
write_region(mapinfo, MetaspaceShared::cc, &_cc_region, /*read_only=*/false,/*allow_exec=*/false);
15381538

15391539
// Split pointer map into read-write and read-only bitmaps
15401540
ArchivePtrMarker::initialize_rw_ro_cc_maps(&_rw_ptrmap, &_ro_ptrmap, &_cc_ptrmap);

‎src/hotspot/share/cds/archiveHeapLoader.cpp

+16
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include "cds/metaspaceShared.hpp"
3030
#include "classfile/classLoaderDataShared.hpp"
3131
#include "classfile/systemDictionaryShared.hpp"
32+
#include "classfile/vmClasses.hpp"
3233
#include "gc/shared/collectedHeap.hpp"
3334
#include "logging/log.hpp"
3435
#include "memory/iterator.inline.hpp"
@@ -428,6 +429,21 @@ void ArchiveHeapLoader::fill_failed_loaded_heap() {
428429
}
429430
}
430431

432+
oop ArchiveHeapLoader::oop_from_offset(int offset) {
433+
// Once GC starts, the offsets saved in CachedCodeDirectoryInternal::_permanent_oop_offsets
434+
// will become invalid. I don't know what function can check if GCs are allowed, but surely
435+
// GCs can't happen before the Object class is loaded.
436+
assert(CDSConfig::is_using_archive(), "sanity");
437+
assert(vmClasses::Object_klass()->class_loader_data() == nullptr,
438+
"can be called only very early during VM start-up");
439+
if (is_loaded()) {
440+
return cast_to_oop(_loaded_heap_bottom + offset);
441+
} else {
442+
assert(is_mapped(), "must be");
443+
return cast_to_oop(_mapped_heap_bottom + offset);
444+
}
445+
}
446+
431447
class PatchNativePointers: public BitMapClosure {
432448
Metadata** _start;
433449

‎src/hotspot/share/cds/archiveHeapLoader.hpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2018, 2024, 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
@@ -153,6 +153,7 @@ class ArchiveHeapLoader : AllStatic {
153153
static void assert_in_loaded_heap(uintptr_t o) {
154154
assert(is_in_loaded_heap(o), "must be");
155155
}
156+
static oop oop_from_offset(int offset);
156157
#endif // INCLUDE_CDS_JAVA_HEAP
157158

158159
};

‎src/hotspot/share/cds/archiveHeapWriter.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,8 @@ void ArchiveHeapWriter::copy_source_objs_to_buffer(GrowableArrayCHeap<oop, mtCla
322322
assert(info != nullptr, "must be");
323323
size_t buffer_offset = copy_one_source_obj_to_buffer(src_obj);
324324
info->set_buffer_offset(buffer_offset);
325+
assert(buffer_offset <= 0x7fffffff, "sanity");
326+
HeapShared::add_to_permanent_oop_table(src_obj, (int)buffer_offset);
325327

326328
_buffer_offset_to_source_obj_table->put_when_absent(buffer_offset, src_obj);
327329
_buffer_offset_to_source_obj_table->maybe_grow();

‎src/hotspot/share/cds/filemap.cpp

+18-6
Original file line numberDiff line numberDiff line change
@@ -1981,12 +1981,24 @@ char* FileMapInfo::map_bitmap_region() {
19811981

19821982
bool FileMapInfo::map_cached_code_region(ReservedSpace rs) {
19831983
FileMapRegion* r = region_at(MetaspaceShared::cc);
1984-
assert(r->used() > 0 && rs.size(), "must be");
1984+
assert(r->used() > 0 && r->used_aligned() == rs.size(), "must be");
19851985

1986-
bool read_only = false, allow_exec = true;
19871986
char* requested_base = rs.base();
1988-
char* mapped_base = map_memory(_fd, _full_path, r->file_offset(),
1989-
requested_base, r->used_aligned(), read_only, allow_exec, mtClassShared);
1987+
assert(requested_base != nullptr, "should be inside code cache");
1988+
1989+
char* mapped_base;
1990+
if (MetaspaceShared::use_windows_memory_mapping()) {
1991+
if (!read_region(MetaspaceShared::cc, requested_base, r->used_aligned(), /* do_commit = */ true)) {
1992+
log_info(cds)("Failed to read cc shared space into reserved space at " INTPTR_FORMAT,
1993+
p2i(requested_base));
1994+
return false;
1995+
}
1996+
mapped_base = requested_base;
1997+
} else {
1998+
bool read_only = false, allow_exec = false;
1999+
mapped_base = map_memory(_fd, _full_path, r->file_offset(),
2000+
requested_base, r->used_aligned(), read_only, allow_exec, mtClassShared);
2001+
}
19902002
if (mapped_base == nullptr) {
19912003
log_info(cds)("failed to map cached code region");
19922004
return false;
@@ -1995,8 +2007,8 @@ bool FileMapInfo::map_cached_code_region(ReservedSpace rs) {
19952007
r->set_mapped_from_file(true);
19962008
r->set_mapped_base(mapped_base);
19972009
relocate_pointers_in_cached_code_region();
1998-
log_info(cds)("Mapped static region #%d at base " INTPTR_FORMAT " top " INTPTR_FORMAT " (%s)",
1999-
MetaspaceShared::bm, p2i(r->mapped_base()), p2i(r->mapped_end()),
2010+
log_info(cds)("Mapped static region #%d at base " INTPTR_FORMAT " top " INTPTR_FORMAT " (%s)",
2011+
MetaspaceShared::cc, p2i(r->mapped_base()), p2i(r->mapped_end()),
20002012
shared_region_name[MetaspaceShared::cc]);
20012013
return true;
20022014
}

‎src/hotspot/share/cds/heapShared.cpp

+96-76
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include "cds/archiveHeapLoader.hpp"
2929
#include "cds/archiveHeapWriter.hpp"
3030
#include "cds/archiveUtils.hpp"
31+
#include "cds/cdsAccess.hpp"
3132
#include "cds/cdsConfig.hpp"
3233
#include "cds/cdsEnumKlass.hpp"
3334
#include "cds/cdsHeapVerifier.hpp"
@@ -248,12 +249,6 @@ int HeapShared::append_root(oop obj) {
248249
// No GC should happen since we aren't scanning _pending_roots.
249250
assert(Thread::current() == (Thread*)VMThread::vm_thread(), "should be in vm thread");
250251

251-
if (UsePermanentHeapObjects && obj != nullptr) {
252-
int n = get_archived_object_permanent_index_locked(obj);
253-
assert(n >= 0, "must have been added");
254-
return n;
255-
}
256-
257252
if (_pending_roots == nullptr) {
258253
_pending_roots = new GrowableArrayCHeap<OopHandle, mtClassShared>(500);
259254
}
@@ -322,77 +317,53 @@ oop HeapShared::orig_to_scratch_object(oop orig_obj) {
322317
return nullptr;
323318
}
324319

325-
class ArchivedObjectPermanentIndexTable: public ResourceHashtable<OopHandle, int,
320+
// Permanent oops are used to support AOT-compiled methods, which may have in-line references
321+
// to Strings and MH oops.
322+
//
323+
// At runtime, these oops are stored in _runtime_permanent_oops (which keeps them alive forever)
324+
// and are accssed vis CDSAccess::get_archived_object(int).
325+
struct PermanentOopInfo {
326+
int _index; // Gets assigned only if HeapShared::get_archived_object_permanent_index() has been called on the object
327+
int _heap_offset; // Offset of the object from the bottom of the archived heap.
328+
PermanentOopInfo(int index, int heap_offset) : _index(index), _heap_offset(heap_offset) {}
329+
};
330+
331+
class PermanentOopTable: public ResourceHashtable<OopHandle, PermanentOopInfo,
326332
36137, // prime number
327333
AnyObj::C_HEAP,
328334
mtClassShared,
329335
oop_handle_hash,
330336
oop_handle_equals> {};
331337

332-
static ArchivedObjectPermanentIndexTable* _permanent_index_table = nullptr;
333-
334-
void HeapShared::add_to_permanent_index_table(oop obj) {
335-
assert_locked_or_safepoint(ArchivedObjectTables_lock);
336-
337-
assert(CDSConfig::is_dumping_heap(), "must be");
338-
if (UsePermanentHeapObjects) {
339-
if (_permanent_index_table == nullptr) {
340-
_permanent_index_table = new (mtClass)ArchivedObjectPermanentIndexTable();
341-
}
342-
if (_pending_roots == nullptr) {
343-
_pending_roots = new GrowableArrayCHeap<OopHandle, mtClassShared>(500);
344-
}
345-
346-
if (_orig_to_scratch_object_table != nullptr) {
347-
OopHandle orig(&obj);
348-
OopHandle* v = _orig_to_scratch_object_table->get(orig);
349-
if (v != nullptr) {
350-
obj = v->resolve();
351-
}
352-
}
338+
static int _dumptime_permanent_oop_count = 0;
339+
static PermanentOopTable* _dumptime_permanent_oop_table = nullptr;
340+
static GrowableArrayCHeap<OopHandle, mtClassShared>* _runtime_permanent_oops = nullptr;
353341

354-
OopHandle oh(Universe::vm_global(), obj);
355-
int index = _pending_roots->append(oh);
356-
_permanent_index_table->put_when_absent(oh, index);
342+
// ArchiveHeapWriter adds each archived heap object to _dumptime_permanent_oop_table,
343+
// so we can remember their offset (from the bottom of the archived heap).
344+
void HeapShared::add_to_permanent_oop_table(oop obj, int offset) {
345+
assert_at_safepoint();
346+
if (_dumptime_permanent_oop_table == nullptr) {
347+
_dumptime_permanent_oop_table = new (mtClass)PermanentOopTable();
357348
}
349+
350+
PermanentOopInfo info(-1, offset);
351+
OopHandle oh(Universe::vm_global(), obj);
352+
_dumptime_permanent_oop_table->put_when_absent(oh, info);
358353
}
359354

355+
// A permanent index is assigned to an archived object ONLY when
356+
// the AOT compiler calls this function.
360357
int HeapShared::get_archived_object_permanent_index(oop obj) {
361358
MutexLocker ml(ArchivedObjectTables_lock, Mutex::_no_safepoint_check_flag);
362-
return get_archived_object_permanent_index_locked(obj);
363-
}
364359

365-
// Can be called in the "old" workflow.
366-
int HeapShared::get_archived_object_permanent_index_locked(oop obj) {
367-
assert_locked_or_safepoint(ArchivedObjectTables_lock);
368-
if (!UsePermanentHeapObjects) {
369-
return -1;
360+
if (!CDSConfig::is_dumping_heap()) {
361+
return -1; // Called by the Leyden old workflow
370362
}
371-
if (!CDSConfig::is_dumping_heap() && !ArchiveHeapLoader::is_in_use()) {
363+
if (_dumptime_permanent_oop_table == nullptr) {
372364
return -1;
373365
}
374366

375-
if (_permanent_index_table == nullptr) {
376-
if (!ArchiveHeapLoader::is_in_use()) {
377-
// Called in the old workflow
378-
return -1;
379-
} else {
380-
_permanent_index_table = new (mtClass)ArchivedObjectPermanentIndexTable();
381-
int count = 0;
382-
for (int segment_idx = 0; segment_idx < _root_segments->length(); segment_idx++) {
383-
objArrayOop segment = (objArrayOop)_root_segments->at(segment_idx).resolve();
384-
for (int i = 0; i < segment->length(); i++, count++) {
385-
oop root = segment->obj_at(i);
386-
if (root != nullptr) {
387-
OopHandle tmp(Universe::vm_global(), root);
388-
int index = count;
389-
_permanent_index_table->put_when_absent(tmp, index);
390-
}
391-
}
392-
}
393-
}
394-
}
395-
396367
if (_orig_to_scratch_object_table != nullptr) {
397368
OopHandle orig(&obj);
398369
OopHandle* v = _orig_to_scratch_object_table->get(orig);
@@ -402,28 +373,78 @@ int HeapShared::get_archived_object_permanent_index_locked(oop obj) {
402373
}
403374

404375
OopHandle tmp(&obj);
405-
int* v = _permanent_index_table->get(tmp);
406-
if (v != nullptr) {
407-
int n = *v;
408-
return n;
376+
PermanentOopInfo* info = _dumptime_permanent_oop_table->get(tmp);
377+
if (info == nullptr) {
378+
return -1;
379+
} else {
380+
if (info->_index < 0) {
381+
info->_index = _dumptime_permanent_oop_count++;
382+
}
383+
return info->_index;
409384
}
410-
411-
return -1;
412385
}
413386

414387
oop HeapShared::get_archived_object(int permanent_index) {
415-
if (ArchiveHeapLoader::is_in_use()) {
416-
return get_root(permanent_index);
417-
} else {
418-
assert(CDSConfig::is_dumping_heap(), "must be");
419-
if (_pending_roots != nullptr) {
420-
return _pending_roots->at(permanent_index).resolve();
421-
} else {
422-
return nullptr;
423-
}
388+
assert(permanent_index >= 0, "sanity");
389+
assert(ArchiveHeapLoader::is_in_use(), "sanity");
390+
assert(_runtime_permanent_oops != nullptr, "sanity");
391+
392+
oop obj = _runtime_permanent_oops->at(permanent_index).resolve();
393+
log_info(cds)("GET perm obj %d = %p", permanent_index, cast_from_oop<void*>(obj));
394+
if (obj != nullptr) {
395+
log_info(cds)("GET perm obj %d class = %p", permanent_index, obj->klass());
396+
log_info(cds)("GET perm obj %d class = %s", permanent_index, obj->klass()->external_name());
397+
}
398+
return obj;
399+
}
400+
401+
// Remember all archived heap objects that have a permanent index.
402+
// table[i] = offset of oop whose permanent index is i.
403+
void CachedCodeDirectoryInternal::dumptime_init_internal() {
404+
const int count = _dumptime_permanent_oop_count;
405+
int* table = (int*)CDSAccess::allocate_from_code_cache(count * sizeof(int));
406+
for (int i = 0; i < count; i++) {
407+
table[count] = -1;
408+
}
409+
_dumptime_permanent_oop_table->iterate([&](OopHandle o, PermanentOopInfo& info) {
410+
int index = info._index;
411+
if (index >= 0) {
412+
assert(index < count, "sanity");
413+
table[index] = info._heap_offset;
414+
}
415+
return true; // continue
416+
});
417+
418+
for (int i = 0; i < count; i++) {
419+
assert(table[i] >= 0, "must be");
424420
}
421+
422+
log_info(cds)("Dumped %d permanent oops", count);
423+
424+
_permanent_oop_count = count;
425+
CDSAccess::set_pointer(&_permanent_oop_offsets, table);
425426
}
426427

428+
// This is called during the bootstrap of the production run, before any GC can happen.
429+
// Record each permanent oop in a OopHandle for GC safety.
430+
void CachedCodeDirectoryInternal::runtime_init_internal() {
431+
int count = _permanent_oop_count;
432+
int* table = _permanent_oop_offsets;
433+
_runtime_permanent_oops = new GrowableArrayCHeap<OopHandle, mtClassShared>();
434+
for (int i = 0; i < count; i++) {
435+
oop obj = ArchiveHeapLoader::oop_from_offset(table[i]);
436+
OopHandle oh(Universe::vm_global(), obj);
437+
_runtime_permanent_oops->append(oh);
438+
439+
ResourceMark rm;
440+
log_info(cds)("perm obj %d = %p", i, cast_from_oop<void*>(obj));
441+
if (obj != nullptr) {
442+
log_info(cds)("perm obj %d class = %p", i, obj->klass());
443+
log_info(cds)("perm obj %d class = %s", i, obj->klass()->external_name());
444+
}
445+
}
446+
};
447+
427448
void HeapShared::get_segment_indexes(int idx, int& seg_idx, int& int_idx) {
428449
assert(_root_segment_max_size_elems > 0, "sanity");
429450

@@ -482,7 +503,6 @@ bool HeapShared::archive_object(oop obj) {
482503
debug_trace();
483504
return false;
484505
} else {
485-
add_to_permanent_index_table(obj);
486506
count_allocation(obj->size());
487507
ArchiveHeapWriter::add_source_obj(obj);
488508
CachedOopInfo info = make_cached_oop_info(obj);

‎src/hotspot/share/cds/heapShared.hpp

+9-2
Original file line numberDiff line numberDiff line change
@@ -370,7 +370,6 @@ class HeapShared: AllStatic {
370370
static bool can_mirror_be_used_in_subgraph(oop orig_java_mirror);
371371
static void archive_java_mirrors();
372372
static void archive_strings();
373-
static int get_archived_object_permanent_index_locked(oop obj);
374373

375374
public:
376375
static void exit_on_error();
@@ -460,7 +459,7 @@ class HeapShared: AllStatic {
460459
static void initialize_test_class_from_archive(TRAPS) NOT_CDS_JAVA_HEAP_RETURN;
461460
#endif
462461

463-
static void add_to_permanent_index_table(oop obj);
462+
static void add_to_permanent_oop_table(oop obj, int offset);
464463

465464
// AOT-compile time only: get a stable index for an archived object.
466465
// Returns 0 if obj is not archived.
@@ -477,6 +476,14 @@ class HeapShared: AllStatic {
477476
static bool is_archivable_hidden_klass(InstanceKlass* ik) NOT_CDS_JAVA_HEAP_RETURN_(false);
478477
};
479478

479+
class CachedCodeDirectoryInternal {
480+
int _permanent_oop_count;
481+
int* _permanent_oop_offsets; // offset of each permanent object from the bottom of the archived heap
482+
public:
483+
void dumptime_init_internal();
484+
void runtime_init_internal();
485+
};
486+
480487
#if INCLUDE_CDS_JAVA_HEAP
481488
class DumpedInternedStrings :
482489
public ResizeableResourceHashtable<oop, bool,

‎src/hotspot/share/cds/metaspaceShared.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -1332,7 +1332,7 @@ void MetaspaceShared::open_static_archive() {
13321332
delete(mapinfo);
13331333
} else {
13341334
FileMapRegion* r = mapinfo->region_at(MetaspaceShared::cc);
1335-
CDSAccess::set_cached_code_size(r->used());
1335+
CDSAccess::set_cached_code_size(r->used_aligned());
13361336
}
13371337
}
13381338

‎src/hotspot/share/code/SCCache.cpp

+15-10
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "asm/macroAssembler.hpp"
2727
#include "cds/cdsAccess.hpp"
2828
#include "cds/cdsConfig.hpp"
29+
#include "cds/heapShared.hpp"
2930
#include "cds/metaspaceShared.hpp"
3031
#include "ci/ciConstant.hpp"
3132
#include "ci/ciEnv.hpp"
@@ -418,7 +419,7 @@ bool SCCache::open_cache(const char* cache_path) {
418419
return true;
419420
}
420421

421-
class CachedCodeDirectory {
422+
class CachedCodeDirectory : public CachedCodeDirectoryInternal {
422423
public:
423424
int _some_number;
424425
InstanceKlass* _some_klass;
@@ -479,19 +480,25 @@ void SCCache::new_workflow_start_writing_cache() {
479480
}
480481

481482
void SCCache::new_workflow_end_writing_cache() {
482-
483+
_cached_code_directory->dumptime_init_internal();
483484
}
484485

485486
void SCCache::new_workflow_load_cache() {
486487
void* ptr = CodeCache::map_cached_code();
487488
if (ptr != nullptr) {
489+
ResourceMark rm;
490+
_cached_code_directory = (CachedCodeDirectory*)ptr;
491+
492+
// CDS uses this to implement CDSAccess::get_archived_object(k)
493+
_cached_code_directory->runtime_init_internal();
494+
488495
// At this point:
489496
// - CodeCache::initialize_heaps() has finished.
490497
// - CDS archive is fully mapped ("metadata", "heap" and "cached_code" regions are mapped)
491498
// - All pointers in the mapped CDS regions are relocated.
492499
// - CDSAccess::get_archived_object() works.
493-
ResourceMark rm;
494-
_cached_code_directory = (CachedCodeDirectory*)ptr;
500+
501+
// Data used by AOT compiler
495502
InstanceKlass* k = _cached_code_directory->_some_klass;
496503
log_info(scc)("new workflow: cached code mapped at %p", ptr);
497504
log_info(scc)("_cached_code_directory->_some_klass = %p (%s)", k, k->external_name());
@@ -2481,7 +2488,6 @@ jobject SCCReader::read_oop(JavaThread* thread, const methodHandle& comp_method)
24812488
code_offset += sizeof(int);
24822489
set_read_position(code_offset);
24832490
obj = CDSAccess::get_archived_object(k);
2484-
assert(k == CDSAccess::get_archived_object_permanent_index(obj), "sanity");
24852491
} else if (kind == DataKind::String) {
24862492
code_offset = read_position();
24872493
int length = *(int*)addr(code_offset);
@@ -2510,7 +2516,6 @@ jobject SCCReader::read_oop(JavaThread* thread, const methodHandle& comp_method)
25102516
code_offset += sizeof(int);
25112517
set_read_position(code_offset);
25122518
obj = CDSAccess::get_archived_object(k);
2513-
assert(k == CDSAccess::get_archived_object_permanent_index(obj), "sanity");
25142519
} else {
25152520
set_lookup_failed();
25162521
log_info(scc)("%d (L%d): Unknown oop's kind: %d",
@@ -2676,8 +2681,8 @@ bool SCCache::write_oop(jobject& jo) {
26762681
}
26772682
}
26782683
} else if (java_lang_String::is_instance(obj)) { // herere
2679-
int k = CDSAccess::get_archived_object_permanent_index(obj); // k >= 1 means obj is a "permanent heap object"
2680-
if (k > 0) {
2684+
int k = CDSAccess::get_archived_object_permanent_index(obj); // k >= 0 means obj is a "permanent heap object"
2685+
if (k >= 0) {
26812686
kind = DataKind::String_Shared;
26822687
n = write_bytes(&kind, sizeof(int));
26832688
if (n != sizeof(int)) {
@@ -2726,8 +2731,8 @@ bool SCCache::write_oop(jobject& jo) {
27262731
return false;
27272732
}
27282733
} else { // herere
2729-
int k = CDSAccess::get_archived_object_permanent_index(obj); // k >= 1 means obj is a "permanent heap object"
2730-
if (k > 0) {
2734+
int k = CDSAccess::get_archived_object_permanent_index(obj); // k >= 0 means obj is a "permanent heap object"
2735+
if (k >= 0) {
27312736
kind = DataKind::MH_Oop_Shared;
27322737
n = write_bytes(&kind, sizeof(int));
27332738
if (n != sizeof(int)) {

0 commit comments

Comments
 (0)
Please sign in to comment.