mirror of
https://github.com/openjdk/jdk.git
synced 2025-09-21 19:44:41 +02:00
8212995: Placing the Integer.IntegerCache and cached Integer objects in the closed archive heap region
Support shareable archive object subgraphs in closed archive heap regions. Reviewed-by: iklam, ccheung
This commit is contained in:
parent
e3a2417b63
commit
80ad2c7e71
9 changed files with 284 additions and 174 deletions
|
@ -37,6 +37,7 @@
|
|||
#include "memory/metaspaceClosure.hpp"
|
||||
#include "memory/resourceArea.hpp"
|
||||
#include "oops/compressedOops.inline.hpp"
|
||||
#include "oops/fieldStreams.hpp"
|
||||
#include "oops/oop.inline.hpp"
|
||||
#include "runtime/fieldDescriptor.inline.hpp"
|
||||
#include "runtime/safepointVerifiers.hpp"
|
||||
|
@ -47,12 +48,40 @@
|
|||
|
||||
#if INCLUDE_CDS_JAVA_HEAP
|
||||
|
||||
bool HeapShared::_closed_archive_heap_region_mapped = false;
|
||||
bool HeapShared::_open_archive_heap_region_mapped = false;
|
||||
bool HeapShared::_archive_heap_region_fixed = false;
|
||||
|
||||
address HeapShared::_narrow_oop_base;
|
||||
int HeapShared::_narrow_oop_shift;
|
||||
|
||||
//
|
||||
// If you add new entries to the following tables, you should know what you're doing!
|
||||
//
|
||||
|
||||
// Entry fields for shareable subgraphs archived in the closed archive heap
|
||||
// region. Warning: Objects in the subgraphs should not have reference fields
|
||||
// assigned at runtime.
|
||||
static ArchivableStaticFieldInfo closed_archive_subgraph_entry_fields[] = {
|
||||
{"java/lang/Integer$IntegerCache", "archivedCache"},
|
||||
};
|
||||
// Entry fields for subgraphs archived in the open archive heap region.
|
||||
static ArchivableStaticFieldInfo open_archive_subgraph_entry_fields[] = {
|
||||
{"jdk/internal/module/ArchivedModuleGraph", "archivedSystemModules"},
|
||||
{"jdk/internal/module/ArchivedModuleGraph", "archivedModuleFinder"},
|
||||
{"jdk/internal/module/ArchivedModuleGraph", "archivedMainModule"},
|
||||
{"jdk/internal/module/ArchivedModuleGraph", "archivedConfiguration"},
|
||||
{"java/util/ImmutableCollections$ListN", "EMPTY_LIST"},
|
||||
{"java/util/ImmutableCollections$MapN", "EMPTY_MAP"},
|
||||
{"java/util/ImmutableCollections$SetN", "EMPTY_SET"},
|
||||
{"java/lang/module/Configuration", "EMPTY_CONFIGURATION"},
|
||||
};
|
||||
|
||||
const static int num_closed_archive_subgraph_entry_fields =
|
||||
sizeof(closed_archive_subgraph_entry_fields) / sizeof(ArchivableStaticFieldInfo);
|
||||
const static int num_open_archive_subgraph_entry_fields =
|
||||
sizeof(open_archive_subgraph_entry_fields) / sizeof(ArchivableStaticFieldInfo);
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Java heap object archiving support
|
||||
|
@ -189,6 +218,10 @@ void HeapShared::copy_closed_archive_heap_objects(
|
|||
// Archive interned string objects
|
||||
StringTable::write_to_archive();
|
||||
|
||||
archive_object_subgraphs(closed_archive_subgraph_entry_fields,
|
||||
num_closed_archive_subgraph_entry_fields,
|
||||
true /* is_closed_archive */, THREAD);
|
||||
|
||||
G1CollectedHeap::heap()->end_archive_alloc_range(closed_archive,
|
||||
os::vm_allocation_granularity());
|
||||
}
|
||||
|
@ -204,7 +237,10 @@ void HeapShared::copy_open_archive_heap_objects(
|
|||
|
||||
archive_klass_objects(THREAD);
|
||||
|
||||
archive_object_subgraphs(THREAD);
|
||||
archive_object_subgraphs(open_archive_subgraph_entry_fields,
|
||||
num_open_archive_subgraph_entry_fields,
|
||||
false /* is_closed_archive */,
|
||||
THREAD);
|
||||
|
||||
G1CollectedHeap::heap()->end_archive_alloc_range(open_archive,
|
||||
os::vm_allocation_granularity());
|
||||
|
@ -237,7 +273,8 @@ KlassSubGraphInfo* HeapShared::get_subgraph_info(Klass* k) {
|
|||
}
|
||||
|
||||
// Add an entry field to the current KlassSubGraphInfo.
|
||||
void KlassSubGraphInfo::add_subgraph_entry_field(int static_field_offset, oop v) {
|
||||
void KlassSubGraphInfo::add_subgraph_entry_field(
|
||||
int static_field_offset, oop v, bool is_closed_archive) {
|
||||
assert(DumpSharedSpaces, "dump time only");
|
||||
if (_subgraph_entry_fields == NULL) {
|
||||
_subgraph_entry_fields =
|
||||
|
@ -245,6 +282,7 @@ void KlassSubGraphInfo::add_subgraph_entry_field(int static_field_offset, oop v)
|
|||
}
|
||||
_subgraph_entry_fields->append((juint)static_field_offset);
|
||||
_subgraph_entry_fields->append(CompressedOops::encode(v));
|
||||
_subgraph_entry_fields->append(is_closed_archive ? 1 : 0);
|
||||
}
|
||||
|
||||
// Add the Klass* for an object in the current KlassSubGraphInfo's subgraphs.
|
||||
|
@ -315,7 +353,7 @@ void ArchivedKlassSubGraphInfoRecord::init(KlassSubGraphInfo* info) {
|
|||
GrowableArray<juint>* entry_fields = info->subgraph_entry_fields();
|
||||
if (entry_fields != NULL) {
|
||||
int num_entry_fields = entry_fields->length();
|
||||
assert(num_entry_fields % 2 == 0, "sanity");
|
||||
assert(num_entry_fields % 3 == 0, "sanity");
|
||||
_entry_field_records =
|
||||
MetaspaceShared::new_ro_array<juint>(num_entry_fields);
|
||||
for (int i = 0 ; i < num_entry_fields; i++) {
|
||||
|
@ -365,8 +403,8 @@ struct CopyKlassSubGraphInfoToArchive : StackObj {
|
|||
// Build the records of archived subgraph infos, which include:
|
||||
// - Entry points to all subgraphs from the containing class mirror. The entry
|
||||
// points are static fields in the mirror. For each entry point, the field
|
||||
// offset and value are recorded in the sub-graph info. The value are stored
|
||||
// back to the corresponding field at runtime.
|
||||
// offset, value and is_closed_archive flag are recorded in the sub-graph
|
||||
// info. The value is stored back to the corresponding field at runtime.
|
||||
// - A list of klasses that need to be loaded/initialized before archived
|
||||
// java object sub-graph can be accessed at runtime.
|
||||
void HeapShared::write_subgraph_info_table() {
|
||||
|
@ -448,15 +486,25 @@ void HeapShared::initialize_from_archived_subgraph(Klass* k) {
|
|||
Array<juint>* entry_field_records = record->entry_field_records();
|
||||
if (entry_field_records != NULL) {
|
||||
int efr_len = entry_field_records->length();
|
||||
assert(efr_len % 2 == 0, "sanity");
|
||||
assert(efr_len % 3 == 0, "sanity");
|
||||
for (i = 0; i < efr_len;) {
|
||||
int field_offset = entry_field_records->at(i);
|
||||
// The object refereced by the field becomes 'known' by GC from this
|
||||
// point. All objects in the subgraph reachable from the object are
|
||||
// also 'known' by GC.
|
||||
oop v = materialize_archived_object(entry_field_records->at(i+1));
|
||||
narrowOop nv = entry_field_records->at(i+1);
|
||||
int is_closed_archive = entry_field_records->at(i+2);
|
||||
oop v;
|
||||
if (is_closed_archive == 0) {
|
||||
// It's an archived object in the open archive heap regions, not shared.
|
||||
// The object refereced by the field becomes 'known' by GC from this
|
||||
// point. All objects in the subgraph reachable from the object are
|
||||
// also 'known' by GC.
|
||||
v = materialize_archived_object(nv);
|
||||
} else {
|
||||
// Shared object in the closed archive heap regions. Decode directly.
|
||||
assert(!CompressedOops::is_null(nv), "shared object is null");
|
||||
v = HeapShared::decode_from_archive(nv);
|
||||
}
|
||||
m->obj_field_put(field_offset, v);
|
||||
i += 2;
|
||||
i += 3;
|
||||
|
||||
log_debug(cds, heap)(" " PTR_FORMAT " init field @ %2d = " PTR_FORMAT, p2i(k), field_offset, p2i(v));
|
||||
}
|
||||
|
@ -469,16 +517,20 @@ void HeapShared::initialize_from_archived_subgraph(Klass* k) {
|
|||
|
||||
class WalkOopAndArchiveClosure: public BasicOopIterateClosure {
|
||||
int _level;
|
||||
bool _is_closed_archive;
|
||||
bool _record_klasses_only;
|
||||
KlassSubGraphInfo* _subgraph_info;
|
||||
oop _orig_referencing_obj;
|
||||
oop _archived_referencing_obj;
|
||||
Thread* _thread;
|
||||
public:
|
||||
WalkOopAndArchiveClosure(int level, bool record_klasses_only,
|
||||
WalkOopAndArchiveClosure(int level,
|
||||
bool is_closed_archive,
|
||||
bool record_klasses_only,
|
||||
KlassSubGraphInfo* subgraph_info,
|
||||
oop orig, oop archived, TRAPS) :
|
||||
_level(level), _record_klasses_only(record_klasses_only),
|
||||
_level(level), _is_closed_archive(is_closed_archive),
|
||||
_record_klasses_only(record_klasses_only),
|
||||
_subgraph_info(subgraph_info),
|
||||
_orig_referencing_obj(orig), _archived_referencing_obj(archived),
|
||||
_thread(THREAD) {}
|
||||
|
@ -506,7 +558,8 @@ class WalkOopAndArchiveClosure: public BasicOopIterateClosure {
|
|||
obj->print_on(&out);
|
||||
}
|
||||
|
||||
oop archived = HeapShared::archive_reachable_objects_from(_level + 1, _subgraph_info, obj, THREAD);
|
||||
oop archived = HeapShared::archive_reachable_objects_from(
|
||||
_level + 1, _subgraph_info, obj, _is_closed_archive, THREAD);
|
||||
assert(archived != NULL, "VM should have exited with unarchivable objects for _level > 1");
|
||||
assert(HeapShared::is_archived_object(archived), "must be");
|
||||
|
||||
|
@ -520,11 +573,32 @@ class WalkOopAndArchiveClosure: public BasicOopIterateClosure {
|
|||
}
|
||||
};
|
||||
|
||||
void HeapShared::check_closed_archive_heap_region_object(InstanceKlass* k,
|
||||
Thread* THREAD) {
|
||||
// Check fields in the object
|
||||
for (JavaFieldStream fs(k); !fs.done(); fs.next()) {
|
||||
if (!fs.access_flags().is_static()) {
|
||||
BasicType ft = fs.field_descriptor().field_type();
|
||||
if (!fs.access_flags().is_final() && (ft == T_ARRAY || T_OBJECT)) {
|
||||
ResourceMark rm(THREAD);
|
||||
log_warning(cds, heap)(
|
||||
"Please check reference field in %s instance in closed archive heap region: %s %s",
|
||||
k->external_name(), (fs.name())->as_C_string(),
|
||||
(fs.signature())->as_C_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// (1) If orig_obj has not been archived yet, archive it.
|
||||
// (2) If orig_obj has not been seen yet (since start_recording_subgraph() was called),
|
||||
// trace all objects that are reachable from it, and make sure these objects are archived.
|
||||
// (3) Record the klasses of all orig_obj and all reachable objects.
|
||||
oop HeapShared::archive_reachable_objects_from(int level, KlassSubGraphInfo* subgraph_info, oop orig_obj, TRAPS) {
|
||||
oop HeapShared::archive_reachable_objects_from(int level,
|
||||
KlassSubGraphInfo* subgraph_info,
|
||||
oop orig_obj,
|
||||
bool is_closed_archive,
|
||||
TRAPS) {
|
||||
assert(orig_obj != NULL, "must be");
|
||||
assert(!is_archived_object(orig_obj), "sanity");
|
||||
|
||||
|
@ -578,8 +652,12 @@ oop HeapShared::archive_reachable_objects_from(int level, KlassSubGraphInfo* sub
|
|||
Klass *relocated_k = archived_obj->klass();
|
||||
subgraph_info->add_subgraph_object_klass(orig_k, relocated_k);
|
||||
|
||||
WalkOopAndArchiveClosure walker(level, record_klasses_only, subgraph_info, orig_obj, archived_obj, THREAD);
|
||||
WalkOopAndArchiveClosure walker(level, is_closed_archive, record_klasses_only,
|
||||
subgraph_info, orig_obj, archived_obj, THREAD);
|
||||
orig_obj->oop_iterate(&walker);
|
||||
if (is_closed_archive && orig_k->is_instance_klass()) {
|
||||
check_closed_archive_heap_region_object(InstanceKlass::cast(orig_k), THREAD);
|
||||
}
|
||||
return archived_obj;
|
||||
}
|
||||
|
||||
|
@ -621,15 +699,12 @@ void HeapShared::archive_reachable_objects_from_static_field(InstanceKlass *k,
|
|||
const char* klass_name,
|
||||
int field_offset,
|
||||
const char* field_name,
|
||||
bool is_closed_archive,
|
||||
TRAPS) {
|
||||
assert(DumpSharedSpaces, "dump time only");
|
||||
assert(k->is_shared_boot_class(), "must be boot class");
|
||||
|
||||
oop m = k->java_mirror();
|
||||
oop archived_m = find_archived_heap_object(m);
|
||||
if (CompressedOops::is_null(archived_m)) {
|
||||
return;
|
||||
}
|
||||
|
||||
KlassSubGraphInfo* subgraph_info = get_subgraph_info(k);
|
||||
oop f = m->obj_field(field_offset);
|
||||
|
@ -643,7 +718,8 @@ void HeapShared::archive_reachable_objects_from_static_field(InstanceKlass *k,
|
|||
f->print_on(&out);
|
||||
}
|
||||
|
||||
oop af = archive_reachable_objects_from(1, subgraph_info, f, CHECK);
|
||||
oop af = archive_reachable_objects_from(1, subgraph_info, f,
|
||||
is_closed_archive, CHECK);
|
||||
|
||||
if (af == NULL) {
|
||||
log_error(cds, heap)("Archiving failed %s::%s (some reachable objects cannot be archived)",
|
||||
|
@ -652,13 +728,13 @@ void HeapShared::archive_reachable_objects_from_static_field(InstanceKlass *k,
|
|||
// Note: the field value is not preserved in the archived mirror.
|
||||
// Record the field as a new subGraph entry point. The recorded
|
||||
// information is restored from the archive at runtime.
|
||||
subgraph_info->add_subgraph_entry_field(field_offset, af);
|
||||
subgraph_info->add_subgraph_entry_field(field_offset, af, is_closed_archive);
|
||||
log_info(cds, heap)("Archived field %s::%s => " PTR_FORMAT, klass_name, field_name, p2i(af));
|
||||
}
|
||||
} else {
|
||||
// The field contains null, we still need to record the entry point,
|
||||
// so it can be restored at runtime.
|
||||
subgraph_info->add_subgraph_entry_field(field_offset, NULL);
|
||||
subgraph_info->add_subgraph_entry_field(field_offset, NULL, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -687,10 +763,6 @@ void HeapShared::verify_subgraph_from_static_field(InstanceKlass* k, int field_o
|
|||
assert(k->is_shared_boot_class(), "must be boot class");
|
||||
|
||||
oop m = k->java_mirror();
|
||||
oop archived_m = find_archived_heap_object(m);
|
||||
if (CompressedOops::is_null(archived_m)) {
|
||||
return;
|
||||
}
|
||||
oop f = m->obj_field(field_offset);
|
||||
if (!CompressedOops::is_null(f)) {
|
||||
verify_subgraph_from(f);
|
||||
|
@ -783,30 +855,6 @@ void HeapShared::done_recording_subgraph(InstanceKlass *k, const char* class_nam
|
|||
_num_total_recorded_klasses += num_new_recorded_klasses;
|
||||
}
|
||||
|
||||
struct ArchivableStaticFieldInfo {
|
||||
const char* klass_name;
|
||||
const char* field_name;
|
||||
InstanceKlass* klass;
|
||||
int offset;
|
||||
BasicType type;
|
||||
};
|
||||
|
||||
// If you add new entries to this table, you should know what you're doing!
|
||||
static ArchivableStaticFieldInfo archivable_static_fields[] = {
|
||||
{"jdk/internal/module/ArchivedModuleGraph", "archivedSystemModules"},
|
||||
{"jdk/internal/module/ArchivedModuleGraph", "archivedModuleFinder"},
|
||||
{"jdk/internal/module/ArchivedModuleGraph", "archivedMainModule"},
|
||||
{"jdk/internal/module/ArchivedModuleGraph", "archivedConfiguration"},
|
||||
{"java/util/ImmutableCollections$ListN", "EMPTY_LIST"},
|
||||
{"java/util/ImmutableCollections$MapN", "EMPTY_MAP"},
|
||||
{"java/util/ImmutableCollections$SetN", "EMPTY_SET"},
|
||||
{"java/lang/Integer$IntegerCache", "archivedCache"},
|
||||
{"java/lang/module/Configuration", "EMPTY_CONFIGURATION"},
|
||||
};
|
||||
|
||||
const static int num_archivable_static_fields =
|
||||
sizeof(archivable_static_fields) / sizeof(ArchivableStaticFieldInfo);
|
||||
|
||||
class ArchivableStaticFieldFinder: public FieldClosure {
|
||||
InstanceKlass* _ik;
|
||||
Symbol* _field_name;
|
||||
|
@ -828,11 +876,10 @@ public:
|
|||
int offset() { return _offset; }
|
||||
};
|
||||
|
||||
void HeapShared::init_archivable_static_fields(Thread* THREAD) {
|
||||
_dump_time_subgraph_info_table = new (ResourceObj::C_HEAP, mtClass)DumpTimeKlassSubGraphInfoTable();
|
||||
|
||||
for (int i = 0; i < num_archivable_static_fields; i++) {
|
||||
ArchivableStaticFieldInfo* info = &archivable_static_fields[i];
|
||||
void HeapShared::init_subgraph_entry_fields(ArchivableStaticFieldInfo fields[],
|
||||
int num, Thread* THREAD) {
|
||||
for (int i = 0; i < num; i++) {
|
||||
ArchivableStaticFieldInfo* info = &fields[i];
|
||||
TempNewSymbol klass_name = SymbolTable::new_symbol(info->klass_name, THREAD);
|
||||
TempNewSymbol field_name = SymbolTable::new_symbol(info->field_name, THREAD);
|
||||
|
||||
|
@ -849,7 +896,26 @@ void HeapShared::init_archivable_static_fields(Thread* THREAD) {
|
|||
}
|
||||
}
|
||||
|
||||
void HeapShared::archive_object_subgraphs(Thread* THREAD) {
|
||||
void HeapShared::init_subgraph_entry_fields(Thread* THREAD) {
|
||||
_dump_time_subgraph_info_table = new (ResourceObj::C_HEAP, mtClass)DumpTimeKlassSubGraphInfoTable();
|
||||
|
||||
init_subgraph_entry_fields(closed_archive_subgraph_entry_fields,
|
||||
num_closed_archive_subgraph_entry_fields,
|
||||
THREAD);
|
||||
init_subgraph_entry_fields(open_archive_subgraph_entry_fields,
|
||||
num_open_archive_subgraph_entry_fields,
|
||||
THREAD);
|
||||
}
|
||||
|
||||
void HeapShared::archive_object_subgraphs(ArchivableStaticFieldInfo fields[],
|
||||
int num, bool is_closed_archive,
|
||||
Thread* THREAD) {
|
||||
_num_total_subgraph_recordings = 0;
|
||||
_num_total_walked_objs = 0;
|
||||
_num_total_archived_objs = 0;
|
||||
_num_total_recorded_klasses = 0;
|
||||
_num_total_verifications = 0;
|
||||
|
||||
// For each class X that has one or more archived fields:
|
||||
// [1] Dump the subgraph of each archived field
|
||||
// [2] Create a list of all the class of the objects that can be reached
|
||||
|
@ -857,38 +923,40 @@ void HeapShared::archive_object_subgraphs(Thread* THREAD) {
|
|||
// At runtime, these classes are initialized before X's archived fields
|
||||
// are restored by HeapShared::initialize_from_archived_subgraph().
|
||||
int i;
|
||||
for (i = 0; i < num_archivable_static_fields; ) {
|
||||
ArchivableStaticFieldInfo* info = &archivable_static_fields[i];
|
||||
for (i = 0; i < num; ) {
|
||||
ArchivableStaticFieldInfo* info = &fields[i];
|
||||
const char* klass_name = info->klass_name;
|
||||
start_recording_subgraph(info->klass, klass_name);
|
||||
|
||||
// If you have specified consecutive fields of the same klass in
|
||||
// archivable_static_fields[], these will be archived in the same
|
||||
// fields[], these will be archived in the same
|
||||
// {start_recording_subgraph ... done_recording_subgraph} pass to
|
||||
// save time.
|
||||
for (; i < num_archivable_static_fields; i++) {
|
||||
ArchivableStaticFieldInfo* f = &archivable_static_fields[i];
|
||||
for (; i < num; i++) {
|
||||
ArchivableStaticFieldInfo* f = &fields[i];
|
||||
if (f->klass_name != klass_name) {
|
||||
break;
|
||||
}
|
||||
archive_reachable_objects_from_static_field(f->klass, f->klass_name,
|
||||
f->offset, f->field_name, CHECK);
|
||||
f->offset, f->field_name,
|
||||
is_closed_archive, CHECK);
|
||||
}
|
||||
done_recording_subgraph(info->klass, klass_name);
|
||||
}
|
||||
|
||||
log_info(cds, heap)("Performed subgraph records = %d times", _num_total_subgraph_recordings);
|
||||
log_info(cds, heap)("Walked %d objects", _num_total_walked_objs);
|
||||
log_info(cds, heap)("Archived %d objects", _num_total_archived_objs);
|
||||
log_info(cds, heap)("Recorded %d klasses", _num_total_recorded_klasses);
|
||||
|
||||
log_info(cds, heap)("Archived subgraph records in %s archive heap region = %d",
|
||||
is_closed_archive ? "closed" : "open",
|
||||
_num_total_subgraph_recordings);
|
||||
log_info(cds, heap)(" Walked %d objects", _num_total_walked_objs);
|
||||
log_info(cds, heap)(" Archived %d objects", _num_total_archived_objs);
|
||||
log_info(cds, heap)(" Recorded %d klasses", _num_total_recorded_klasses);
|
||||
|
||||
#ifndef PRODUCT
|
||||
for (int i = 0; i < num_archivable_static_fields; i++) {
|
||||
ArchivableStaticFieldInfo* f = &archivable_static_fields[i];
|
||||
for (int i = 0; i < num; i++) {
|
||||
ArchivableStaticFieldInfo* f = &fields[i];
|
||||
verify_subgraph_from_static_field(f->klass, f->offset);
|
||||
}
|
||||
log_info(cds, heap)("Verified %d references", _num_total_verifications);
|
||||
log_info(cds, heap)(" Verified %d references", _num_total_verifications);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue