mirror of
https://github.com/openjdk/jdk.git
synced 2025-09-18 10:04:42 +02:00
8275731: CDS archived enums objects are recreated at runtime
Reviewed-by: coleenp, ccheung
This commit is contained in:
parent
c7cd1487fe
commit
d983d108c5
16 changed files with 857 additions and 54 deletions
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue