diff -r 595ab4f025d7 -r 72d4e10305b9 src/hotspot/share/memory/heapShared.cpp --- a/src/hotspot/share/memory/heapShared.cpp Sat Nov 03 12:37:55 2018 -0700 +++ b/src/hotspot/share/memory/heapShared.cpp Sat Nov 03 15:40:19 2018 -0400 @@ -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 @@ // 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 @@ 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 @@ } // 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 @@ } _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 @@ GrowableArray* 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(num_entry_fields); for (int i = 0 ; i < num_entry_fields; i++) { @@ -365,8 +403,8 @@ // 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 @@ Array* 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 @@ 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 @@ 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 @@ } }; +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 @@ 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 @@ 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 @@ 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 @@ // 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 @@ 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 @@ _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 @@ 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::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 @@ // 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 }