8275731: CDS archived enums objects are recreated at runtime

Reviewed-by: coleenp, ccheung
This commit is contained in:
Ioi Lam 2022-02-28 20:33:18 +00:00
parent c7cd1487fe
commit d983d108c5
16 changed files with 857 additions and 54 deletions

View file

@ -25,6 +25,7 @@
#include "precompiled.hpp"
#include "cds/archiveBuilder.hpp"
#include "cds/archiveUtils.hpp"
#include "cds/cdsHeapVerifier.hpp"
#include "cds/filemap.hpp"
#include "cds/heapShared.inline.hpp"
#include "cds/metaspaceShared.hpp"
@ -42,7 +43,6 @@
#include "gc/shared/gcLocker.hpp"
#include "gc/shared/gcVMOperations.hpp"
#include "logging/log.hpp"
#include "logging/logMessage.hpp"
#include "logging/logStream.hpp"
#include "memory/iterator.inline.hpp"
#include "memory/metadataFactory.hpp"
@ -143,11 +143,24 @@ bool HeapShared::is_archived_object_during_dumptime(oop p) {
}
#endif
////////////////////////////////////////////////////////////////
//
// Java heap object archiving support
//
////////////////////////////////////////////////////////////////
static bool is_subgraph_root_class_of(ArchivableStaticFieldInfo fields[], int num, InstanceKlass* ik) {
for (int i = 0; i < num; i++) {
if (fields[i].klass == ik) {
return true;
}
}
return false;
}
bool HeapShared::is_subgraph_root_class(InstanceKlass* ik) {
return is_subgraph_root_class_of(closed_archive_subgraph_entry_fields,
num_closed_archive_subgraph_entry_fields, ik) ||
is_subgraph_root_class_of(open_archive_subgraph_entry_fields,
num_open_archive_subgraph_entry_fields, ik) ||
is_subgraph_root_class_of(fmg_open_archive_subgraph_entry_fields,
num_fmg_open_archive_subgraph_entry_fields, ik);
}
void HeapShared::fixup_regions() {
FileMapInfo* mapinfo = FileMapInfo::current_info();
if (is_mapped()) {
@ -203,9 +216,9 @@ HeapShared::ArchivedObjectCache* HeapShared::_archived_object_cache = NULL;
oop HeapShared::find_archived_heap_object(oop obj) {
assert(DumpSharedSpaces, "dump-time only");
ArchivedObjectCache* cache = archived_object_cache();
oop* p = cache->get(obj);
CachedOopInfo* p = cache->get(obj);
if (p != NULL) {
return *p;
return p->_obj;
} else {
return NULL;
}
@ -302,7 +315,8 @@ oop HeapShared::archive_object(oop obj) {
assert(hash_original == hash_archived, "Different hash codes: original %x, archived %x", hash_original, hash_archived);
ArchivedObjectCache* cache = archived_object_cache();
cache->put(obj, archived_oop);
CachedOopInfo info = make_cached_oop_info(archived_oop);
cache->put(obj, info);
if (log_is_enabled(Debug, cds, heap)) {
ResourceMark rm;
log_debug(cds, heap)("Archived heap object " PTR_FORMAT " ==> " PTR_FORMAT " : %s",
@ -336,6 +350,94 @@ void HeapShared::archive_klass_objects() {
}
}
// -- Handling of Enum objects
// Java Enum classes have synthetic <clinit> methods that look like this
// enum MyEnum {FOO, BAR}
// MyEnum::<clinint> {
// /*static final MyEnum*/ MyEnum::FOO = new MyEnum("FOO");
// /*static final MyEnum*/ MyEnum::BAR = new MyEnum("BAR");
// }
//
// If MyEnum::FOO object is referenced by any of the archived subgraphs, we must
// ensure the archived value equals (in object address) to the runtime value of
// MyEnum::FOO.
//
// However, since MyEnum::<clinint> is synthetically generated by javac, there's
// no way of programatically handling this inside the Java code (as you would handle
// ModuleLayer::EMPTY_LAYER, for example).
//
// Instead, we archive all static field of such Enum classes. At runtime,
// HeapShared::initialize_enum_klass() will skip the <clinit> method and pull
// the static fields out of the archived heap.
void HeapShared::check_enum_obj(int level,
KlassSubGraphInfo* subgraph_info,
oop orig_obj,
bool is_closed_archive) {
Klass* k = orig_obj->klass();
Klass* relocated_k = ArchiveBuilder::get_relocated_klass(k);
if (!k->is_instance_klass()) {
return;
}
InstanceKlass* ik = InstanceKlass::cast(k);
if (ik->java_super() == vmClasses::Enum_klass() && !ik->has_archived_enum_objs()) {
ResourceMark rm;
ik->set_has_archived_enum_objs();
relocated_k->set_has_archived_enum_objs();
oop mirror = ik->java_mirror();
for (JavaFieldStream fs(ik); !fs.done(); fs.next()) {
if (fs.access_flags().is_static()) {
fieldDescriptor& fd = fs.field_descriptor();
if (fd.field_type() != T_OBJECT && fd.field_type() != T_ARRAY) {
guarantee(false, "static field %s::%s must be T_OBJECT or T_ARRAY",
ik->external_name(), fd.name()->as_C_string());
}
oop oop_field = mirror->obj_field(fd.offset());
if (oop_field == NULL) {
guarantee(false, "static field %s::%s must not be null",
ik->external_name(), fd.name()->as_C_string());
} else if (oop_field->klass() != ik && oop_field->klass() != ik->array_klass_or_null()) {
guarantee(false, "static field %s::%s is of the wrong type",
ik->external_name(), fd.name()->as_C_string());
}
oop archived_oop_field = archive_reachable_objects_from(level, subgraph_info, oop_field, is_closed_archive);
int root_index = append_root(archived_oop_field);
log_info(cds, heap)("Archived enum obj @%d %s::%s (" INTPTR_FORMAT " -> " INTPTR_FORMAT ")",
root_index, ik->external_name(), fd.name()->as_C_string(),
p2i((oopDesc*)oop_field), p2i((oopDesc*)archived_oop_field));
SystemDictionaryShared::add_enum_klass_static_field(ik, root_index);
}
}
}
}
// See comments in HeapShared::check_enum_obj()
bool HeapShared::initialize_enum_klass(InstanceKlass* k, TRAPS) {
if (!is_fully_available()) {
return false;
}
RunTimeClassInfo* info = RunTimeClassInfo::get_for(k);
assert(info != NULL, "sanity");
if (log_is_enabled(Info, cds, heap)) {
ResourceMark rm;
log_info(cds, heap)("Initializing Enum class: %s", k->external_name());
}
oop mirror = k->java_mirror();
int i = 0;
for (JavaFieldStream fs(k); !fs.done(); fs.next()) {
if (fs.access_flags().is_static()) {
int root_index = info->enum_klass_static_field_root_index_at(i++);
fieldDescriptor& fd = fs.field_descriptor();
assert(fd.field_type() == T_OBJECT || fd.field_type() == T_ARRAY, "must be");
mirror->obj_field_put(fd.offset(), get_root(root_index, /*clear=*/true));
}
}
return true;
}
void HeapShared::run_full_gc_in_vm_thread() {
if (HeapShared::can_write()) {
// Avoid fragmentation while archiving heap objects.
@ -377,6 +479,7 @@ void HeapShared::archive_objects(GrowableArray<MemRegion>* closed_regions,
log_info(cds)("Dumping objects to open archive heap region ...");
copy_open_objects(open_regions);
CDSHeapVerifier::verify();
destroy_archived_object_cache();
}
@ -903,6 +1006,11 @@ class WalkOopAndArchiveClosure: public BasicOopIterateClosure {
KlassSubGraphInfo* _subgraph_info;
oop _orig_referencing_obj;
oop _archived_referencing_obj;
// The following are for maintaining a stack for determining
// CachedOopInfo::_referrer
static WalkOopAndArchiveClosure* _current;
WalkOopAndArchiveClosure* _last;
public:
WalkOopAndArchiveClosure(int level,
bool is_closed_archive,
@ -912,7 +1020,13 @@ class WalkOopAndArchiveClosure: public BasicOopIterateClosure {
_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) {}
_orig_referencing_obj(orig), _archived_referencing_obj(archived) {
_last = _current;
_current = this;
}
~WalkOopAndArchiveClosure() {
_current = _last;
}
void do_oop(narrowOop *p) { WalkOopAndArchiveClosure::do_oop_work(p); }
void do_oop( oop *p) { WalkOopAndArchiveClosure::do_oop_work(p); }
@ -949,8 +1063,26 @@ class WalkOopAndArchiveClosure: public BasicOopIterateClosure {
}
}
}
public:
static WalkOopAndArchiveClosure* current() { return _current; }
oop orig_referencing_obj() { return _orig_referencing_obj; }
KlassSubGraphInfo* subgraph_info() { return _subgraph_info; }
};
WalkOopAndArchiveClosure* WalkOopAndArchiveClosure::_current = NULL;
HeapShared::CachedOopInfo HeapShared::make_cached_oop_info(oop orig_obj) {
CachedOopInfo info;
WalkOopAndArchiveClosure* walker = WalkOopAndArchiveClosure::current();
info._subgraph_info = (walker == NULL) ? NULL : walker->subgraph_info();
info._referrer = (walker == NULL) ? NULL : walker->orig_referencing_obj();
info._obj = orig_obj;
return info;
}
void HeapShared::check_closed_region_object(InstanceKlass* k) {
// Check fields in the object
for (JavaFieldStream fs(k); !fs.done(); fs.next()) {
@ -1076,6 +1208,8 @@ oop HeapShared::archive_reachable_objects_from(int level,
if (is_closed_archive && orig_k->is_instance_klass()) {
check_closed_region_object(InstanceKlass::cast(orig_k));
}
check_enum_obj(level + 1, subgraph_info, orig_obj, is_closed_archive);
return archived_obj;
}