8253081: G1 fails on stale objects in archived module graph in Open Archive regions

Change the handling of Open Archive areas, instead of assuming that everything in there is live always, a root containing references to all live root objects is provided. Adapt G1 to handle Open Archive regions as any other old region apart from never compacting or evacuating them.

Co-authored-by: Ioi Lam <iklam@openjdk.org>
Reviewed-by: kbarrett, sjohanss, redestad
This commit is contained in:
Thomas Schatzl 2020-11-18 08:21:03 +00:00
parent c08921487b
commit d30956055b
32 changed files with 584 additions and 230 deletions

View File

@ -59,6 +59,7 @@ public:
}
void restore(ClassLoaderData* loader_data, bool do_entries, bool do_oops);
void clear_archived_oops();
};
static ArchivedClassLoaderData _archived_boot_loader_data;
@ -123,6 +124,15 @@ void ArchivedClassLoaderData::restore(ClassLoaderData* loader_data, bool do_entr
}
}
void ArchivedClassLoaderData::clear_archived_oops() {
assert(UseSharedSpaces, "must be");
if (_modules != NULL) {
for (int i = 0; i < _modules->length(); i++) {
_modules->at(i)->clear_archived_oops();
}
}
}
// ------------------------------
static ClassLoaderData* null_class_loader_data() {
@ -183,6 +193,13 @@ void ClassLoaderDataShared::serialize(SerializeClosure* f) {
}
}
void ClassLoaderDataShared::clear_archived_oops() {
assert(UseSharedSpaces && !MetaspaceShared::use_full_module_graph(), "must be");
_archived_boot_loader_data.clear_archived_oops();
_archived_platform_loader_data.clear_archived_oops();
_archived_system_loader_data.clear_archived_oops();
}
oop ClassLoaderDataShared::restore_archived_oops_for_null_class_loader_data() {
assert(UseSharedSpaces && MetaspaceShared::use_full_module_graph(), "must be");
_archived_boot_loader_data.restore(null_class_loader_data(), false, true);

View File

@ -39,6 +39,7 @@ public:
static void init_archived_tables();
static void init_archived_oops();
static void serialize(SerializeClosure* f);
static void clear_archived_oops();
static oop restore_archived_oops_for_null_class_loader_data();
static void restore_java_platform_loader_from_archive(ClassLoaderData* loader_data);
static void restore_java_system_loader_from_archive(ClassLoaderData* loader_data);

View File

@ -890,14 +890,14 @@ void java_lang_Class::fixup_mirror(Klass* k, TRAPS) {
}
}
if (k->is_shared() && k->has_raw_archived_mirror()) {
if (k->is_shared() && k->has_archived_mirror_index()) {
if (HeapShared::open_archive_heap_region_mapped()) {
bool present = restore_archived_mirror(k, Handle(), Handle(), Handle(), CHECK);
assert(present, "Missing archived mirror for %s", k->external_name());
return;
} else {
k->clear_java_mirror_handle();
k->clear_has_raw_archived_mirror();
k->clear_archived_mirror_index();
}
}
create_mirror(k, Handle(), Handle(), Handle(), Handle(), CHECK);
@ -1163,9 +1163,9 @@ oop java_lang_Class::archive_mirror(Klass* k, TRAPS) {
"HeapShared::is_heap_object_archiving_allowed() must be true");
// Mirror is already archived
if (k->has_raw_archived_mirror()) {
assert(k->archived_java_mirror_raw() != NULL, "no archived mirror");
return k->archived_java_mirror_raw();
if (k->has_archived_mirror_index()) {
assert(k->archived_java_mirror() != NULL, "no archived mirror");
return k->archived_java_mirror();
}
// No mirror
@ -1197,9 +1197,7 @@ oop java_lang_Class::archive_mirror(Klass* k, TRAPS) {
return NULL;
}
k->set_archived_java_mirror_raw(archived_mirror);
k->set_has_raw_archived_mirror();
k->set_archived_java_mirror(archived_mirror);
ResourceMark rm;
log_trace(cds, heap, mirror)(
@ -1317,10 +1315,11 @@ bool java_lang_Class::restore_archived_mirror(Klass *k,
return true;
}
oop m = HeapShared::materialize_archived_object(k->archived_java_mirror_raw_narrow());
if (m == NULL) {
return false;
}
oop m = k->archived_java_mirror();
assert(m != NULL, "must have stored non-null archived mirror");
// Sanity: clear it now to prevent re-initialization if any of the following fails
k->clear_archived_mirror_index();
// mirror is archived, restore
log_debug(cds, mirror)("Archived mirror is: " PTR_FORMAT, p2i(m));
@ -1346,7 +1345,6 @@ bool java_lang_Class::restore_archived_mirror(Klass *k,
}
k->set_java_mirror(mirror);
k->clear_has_raw_archived_mirror();
set_mirror_module_field(k, mirror, module, THREAD);

View File

@ -467,7 +467,7 @@ void ModuleEntry::init_archived_oops() {
if (module_obj != NULL) {
oop m = HeapShared::find_archived_heap_object(module_obj);
assert(m != NULL, "sanity");
_archived_module_narrow_oop = CompressedOops::encode(m);
_archived_module_index = HeapShared::append_root(m);
}
assert(shared_protection_domain() == NULL, "never set during -Xshare:dump");
// Clear handles and restore at run time. Handles cannot be archived.
@ -481,8 +481,8 @@ void ModuleEntry::load_from_archive(ClassLoaderData* loader_data) {
JFR_ONLY(INIT_ID(this);)
}
void ModuleEntry::restore_archive_oops(ClassLoaderData* loader_data) {
Handle module_handle(Thread::current(), HeapShared::materialize_archived_object(_archived_module_narrow_oop));
void ModuleEntry::restore_archived_oops(ClassLoaderData* loader_data) {
Handle module_handle(Thread::current(), HeapShared::get_root(_archived_module_index, /*clear=*/true));
assert(module_handle.not_null(), "huh");
set_module(loader_data->add_handle(module_handle));
@ -495,6 +495,10 @@ void ModuleEntry::restore_archive_oops(ClassLoaderData* loader_data) {
}
}
void ModuleEntry::clear_archived_oops() {
HeapShared::clear_root(_archived_module_index);
}
static int compare_module_by_name(ModuleEntry* a, ModuleEntry* b) {
assert(a == b || a->name() != b->name(), "no duplicated names");
return a->name()->fast_compare(b->name());
@ -561,7 +565,7 @@ void ModuleEntryTable::restore_archived_oops(ClassLoaderData* loader_data, Array
assert(UseSharedSpaces, "runtime only");
for (int i = 0; i < archived_modules->length(); i++) {
ModuleEntry* archived_entry = archived_modules->at(i);
archived_entry->restore_archive_oops(loader_data);
archived_entry->restore_archived_oops(loader_data);
}
}
#endif // INCLUDE_CDS_JAVA_HEAP

View File

@ -76,7 +76,7 @@ private:
bool _must_walk_reads; // walk module's reads list at GC safepoints to purge out dead modules
bool _is_open; // whether the packages in the module are all unqualifiedly exported
bool _is_patched; // whether the module is patched via --patch-module
CDS_JAVA_HEAP_ONLY(narrowOop _archived_module_narrow_oop;)
CDS_JAVA_HEAP_ONLY(int _archived_module_index;)
JFR_ONLY(DEFINE_TRACE_ID_FIELD;)
enum {MODULE_READS_SIZE = 101}; // Initial size of list of modules that the module can read.
@ -201,7 +201,8 @@ public:
static Array<ModuleEntry*>* write_growable_array(GrowableArray<ModuleEntry*>* array);
static GrowableArray<ModuleEntry*>* restore_growable_array(Array<ModuleEntry*>* archived_array);
void load_from_archive(ClassLoaderData* loader_data);
void restore_archive_oops(ClassLoaderData* loader_data);
void restore_archived_oops(ClassLoaderData* loader_data);
void clear_archived_oops();
#endif
};

View File

@ -1979,8 +1979,12 @@ void SystemDictionary::initialize(TRAPS) {
oop lock_obj = oopFactory::new_intArray(0, CHECK);
_system_loader_lock_obj = OopHandle(Universe::vm_global(), lock_obj);
// Initialize basic classes
// Resolve basic classes
resolve_well_known_classes(CHECK);
// Resolve classes used by archived heap objects
if (UseSharedSpaces) {
HeapShared::resolve_classes(CHECK);
}
}
// Compact table of directions on the initialization of klasses:

View File

@ -75,6 +75,7 @@ bool SystemDictionaryShared::_dump_in_progress = false;
class DumpTimeSharedClassInfo: public CHeapObj<mtClass> {
bool _excluded;
bool _is_early_klass;
public:
struct DTLoaderConstraint {
Symbol* _name;
@ -121,6 +122,7 @@ public:
_clsfile_size = -1;
_clsfile_crc32 = -1;
_excluded = false;
_is_early_klass = JvmtiExport::is_early_phase();
_verifier_constraints = NULL;
_verifier_constraint_flags = NULL;
_loader_constraints = NULL;
@ -177,6 +179,11 @@ public:
return _excluded || _failed_verification || _klass == NULL;
}
// Was this class loaded while JvmtiExport::is_early_phase()==true
bool is_early_klass() {
return _is_early_klass;
}
void set_failed_verification() {
_failed_verification = true;
}
@ -1325,6 +1332,11 @@ bool SystemDictionaryShared::is_hidden_lambda_proxy(InstanceKlass* ik) {
}
}
bool SystemDictionaryShared::is_early_klass(InstanceKlass* ik) {
DumpTimeSharedClassInfo* info = _dumptime_table->get(ik);
return (info != NULL) ? info->is_early_klass() : false;
}
void SystemDictionaryShared::warn_excluded(InstanceKlass* k, const char* reason) {
ResourceMark rm;
log_warning(cds)("Skipping %s: %s", k->name()->as_C_string(), reason);
@ -2302,8 +2314,8 @@ bool SystemDictionaryShared::empty_dumptime_table() {
class ArchivedMirrorPatcher {
protected:
static void update(Klass* k) {
if (k->has_raw_archived_mirror()) {
oop m = HeapShared::materialize_archived_object(k->archived_java_mirror_raw_narrow());
if (k->has_archived_mirror_index()) {
oop m = k->archived_java_mirror();
if (m != NULL) {
java_lang_Class::update_archived_mirror_native_pointers(m);
}

View File

@ -229,6 +229,7 @@ private:
public:
static bool is_hidden_lambda_proxy(InstanceKlass* ik);
static bool is_early_klass(InstanceKlass* k); // Was k loaded while JvmtiExport::is_early_phase()==true
static Handle init_security_info(Handle class_loader, InstanceKlass* ik, PackageEntry* pkg_entry, TRAPS);
static InstanceKlass* find_builtin_class(Symbol* class_name);

View File

@ -804,17 +804,6 @@ void G1CollectedHeap::dealloc_archive_regions(MemRegion* ranges, size_t count) {
decrease_used(size_used);
}
oop G1CollectedHeap::materialize_archived_object(oop obj) {
assert(is_archived_object(obj), "not an archived obj");
// Loading an archived object makes it strongly reachable. If it is
// loaded during concurrent marking, it must be enqueued to the SATB
// queue, shading the previously white object gray.
G1BarrierSet::enqueue(obj);
return obj;
}
HeapWord* G1CollectedHeap::attempt_allocation_humongous(size_t word_size) {
ResourceMark rm; // For retrieving the thread names in log messages.
@ -4021,11 +4010,13 @@ void G1CollectedHeap::free_humongous_region(HeapRegion* hr,
free_region(hr, free_list);
}
void G1CollectedHeap::remove_from_old_sets(const uint old_regions_removed,
const uint humongous_regions_removed) {
if (old_regions_removed > 0 || humongous_regions_removed > 0) {
void G1CollectedHeap::remove_from_old_gen_sets(const uint old_regions_removed,
const uint archive_regions_removed,
const uint humongous_regions_removed) {
if (old_regions_removed > 0 || archive_regions_removed > 0 || humongous_regions_removed > 0) {
MutexLocker x(OldSets_lock, Mutex::_no_safepoint_check_flag);
_old_set.bulk_remove(old_regions_removed);
_archive_set.bulk_remove(archive_regions_removed);
_humongous_set.bulk_remove(humongous_regions_removed);
}
@ -4453,7 +4444,7 @@ void G1CollectedHeap::eagerly_reclaim_humongous_regions() {
G1FreeHumongousRegionClosure cl(&local_cleanup_list);
heap_region_iterate(&cl);
remove_from_old_sets(0, cl.humongous_regions_reclaimed());
remove_from_old_gen_sets(0, 0, cl.humongous_regions_reclaimed());
G1HRPrinter* hrp = hr_printer();
if (hrp->is_active()) {
@ -4528,22 +4519,23 @@ bool G1CollectedHeap::check_young_list_empty() {
#endif // ASSERT
// Remove the given HeapRegion from the appropriate region set.
// Remove the given HeapRegion from the appropriate region set.
void G1CollectedHeap::prepare_region_for_full_compaction(HeapRegion* hr) {
if (hr->is_old()) {
if (hr->is_archive()) {
_archive_set.remove(hr);
} else if (hr->is_humongous()) {
_humongous_set.remove(hr);
} else if (hr->is_old()) {
_old_set.remove(hr);
} else if (hr->is_young()) {
// Note that emptying the _young_list is postponed and instead done as
// the first step when rebuilding the regions sets again. The reason for
// this is that during a full GC string deduplication needs to know if
// Note that emptying the eden and survivor lists is postponed and instead
// done as the first step when rebuilding the regions sets again. The reason
// for this is that during a full GC string deduplication needs to know if
// a collected region was young or old when the full GC was initiated.
hr->uninstall_surv_rate_group();
} else {
// We ignore free regions, we'll empty the free list afterwards.
// We ignore humongous and archive regions, we're not tearing down these
// sets.
assert(hr->is_archive() || hr->is_free() || hr->is_humongous(),
"it cannot be another type");
assert(hr->is_free(), "it cannot be another type");
}
}
@ -4567,6 +4559,9 @@ private:
bool _free_list_only;
HeapRegionSet* _old_set;
HeapRegionSet* _archive_set;
HeapRegionSet* _humongous_set;
HeapRegionManager* _hrm;
size_t _total_used;
@ -4574,12 +4569,16 @@ private:
public:
RebuildRegionSetsClosure(bool free_list_only,
HeapRegionSet* old_set,
HeapRegionSet* archive_set,
HeapRegionSet* humongous_set,
HeapRegionManager* hrm) :
_free_list_only(free_list_only),
_old_set(old_set), _hrm(hrm), _total_used(0) {
_free_list_only(free_list_only), _old_set(old_set), _archive_set(archive_set),
_humongous_set(humongous_set), _hrm(hrm), _total_used(0) {
assert(_hrm->num_free_regions() == 0, "pre-condition");
if (!free_list_only) {
assert(_old_set->is_empty(), "pre-condition");
assert(_archive_set->is_empty(), "pre-condition");
assert(_humongous_set->is_empty(), "pre-condition");
}
}
@ -4592,11 +4591,14 @@ public:
} else if (!_free_list_only) {
assert(r->rem_set()->is_empty(), "At this point remembered sets must have been cleared.");
if (r->is_archive() || r->is_humongous()) {
// We ignore archive and humongous regions. We left these sets unchanged.
if (r->is_humongous()) {
_humongous_set->add(r);
} else if (r->is_archive()) {
_archive_set->add(r);
} else {
assert(r->is_young() || r->is_free() || r->is_old(), "invariant");
// We now move all (non-humongous, non-old, non-archive) regions to old gen, and register them as such.
// We now move all (non-humongous, non-old, non-archive) regions to old gen,
// and register them as such.
r->move_to_old();
_old_set->add(r);
}
@ -4619,7 +4621,9 @@ void G1CollectedHeap::rebuild_region_sets(bool free_list_only) {
_survivor.clear();
}
RebuildRegionSetsClosure cl(free_list_only, &_old_set, &_hrm);
RebuildRegionSetsClosure cl(free_list_only,
&_old_set, &_archive_set, &_humongous_set,
&_hrm);
heap_region_iterate(&cl);
if (!free_list_only) {

View File

@ -724,8 +724,6 @@ public:
// mapping failed, with the same non-overlapping and sorted MemRegion array.
void dealloc_archive_regions(MemRegion* range, size_t count);
oop materialize_archived_object(oop obj);
private:
// Shrink the garbage-first heap by at most the given size (in bytes!).
@ -1122,7 +1120,9 @@ public:
// True iff an evacuation has failed in the most-recent collection.
bool evacuation_failed() { return _evacuation_failed; }
void remove_from_old_sets(const uint old_regions_removed, const uint humongous_regions_removed);
void remove_from_old_gen_sets(const uint old_regions_removed,
const uint archive_regions_removed,
const uint humongous_regions_removed);
void prepend_to_freelist(FreeRegionList* list);
void decrement_summary_bytes(size_t bytes);
@ -1314,23 +1314,19 @@ public:
bool is_marked_next(oop obj) const;
// Determine if an object is dead, given the object and also
// the region to which the object belongs. An object is dead
// iff a) it was not allocated since the last mark, b) it
// is not marked, and c) it is not in an archive region.
// the region to which the object belongs.
bool is_obj_dead(const oop obj, const HeapRegion* hr) const {
return
hr->is_obj_dead(obj, _cm->prev_mark_bitmap()) &&
!hr->is_archive();
return hr->is_obj_dead(obj, _cm->prev_mark_bitmap());
}
// This function returns true when an object has been
// around since the previous marking and hasn't yet
// been marked during this marking, and is not in an archive region.
// been marked during this marking, and is not in a closed archive region.
bool is_obj_ill(const oop obj, const HeapRegion* hr) const {
return
!hr->obj_allocated_since_next_marking(obj) &&
!is_marked_next(obj) &&
!hr->is_archive();
!hr->is_closed_archive();
}
// Determine if an object is dead, given only the object itself.

View File

@ -282,7 +282,7 @@ inline bool G1CollectedHeap::is_obj_ill(const oop obj) const {
}
inline bool G1CollectedHeap::is_obj_dead_full(const oop obj, const HeapRegion* hr) const {
return !is_marked_next(obj) && !hr->is_archive();
return !is_marked_next(obj) && !hr->is_closed_archive();
}
inline bool G1CollectedHeap::is_obj_dead_full(const oop obj) const {

View File

@ -1200,6 +1200,7 @@ class G1ReclaimEmptyRegionsTask : public AbstractGangTask {
size_t _freed_bytes;
FreeRegionList* _local_cleanup_list;
uint _old_regions_removed;
uint _archive_regions_removed;
uint _humongous_regions_removed;
public:
@ -1209,26 +1210,32 @@ class G1ReclaimEmptyRegionsTask : public AbstractGangTask {
_freed_bytes(0),
_local_cleanup_list(local_cleanup_list),
_old_regions_removed(0),
_archive_regions_removed(0),
_humongous_regions_removed(0) { }
size_t freed_bytes() { return _freed_bytes; }
const uint old_regions_removed() { return _old_regions_removed; }
const uint archive_regions_removed() { return _archive_regions_removed; }
const uint humongous_regions_removed() { return _humongous_regions_removed; }
bool do_heap_region(HeapRegion *hr) {
if (hr->used() > 0 && hr->max_live_bytes() == 0 && !hr->is_young() && !hr->is_archive()) {
if (hr->used() > 0 && hr->max_live_bytes() == 0 && !hr->is_young() && !hr->is_closed_archive()) {
log_trace(gc)("Reclaimed empty old gen region %u (%s) bot " PTR_FORMAT,
hr->hrm_index(), hr->get_short_type_str(), p2i(hr->bottom()));
_freed_bytes += hr->used();
hr->set_containing_set(NULL);
if (hr->is_humongous()) {
_humongous_regions_removed++;
_g1h->free_humongous_region(hr, _local_cleanup_list);
} else if (hr->is_open_archive()) {
_archive_regions_removed++;
_g1h->free_region(hr, _local_cleanup_list);
} else {
_old_regions_removed++;
_g1h->free_region(hr, _local_cleanup_list);
}
hr->clear_cardtable();
_g1h->concurrent_mark()->clear_statistics_in_region(hr->hrm_index());
log_trace(gc)("Reclaimed empty region %u (%s) bot " PTR_FORMAT, hr->hrm_index(), hr->get_short_type_str(), p2i(hr->bottom()));
}
return false;
@ -1253,8 +1260,10 @@ public:
_g1h->heap_region_par_iterate_from_worker_offset(&cl, &_hrclaimer, worker_id);
assert(cl.is_complete(), "Shouldn't have aborted!");
// Now update the old/humongous region sets
_g1h->remove_from_old_sets(cl.old_regions_removed(), cl.humongous_regions_removed());
// Now update the old/archive/humongous region sets
_g1h->remove_from_old_gen_sets(cl.old_regions_removed(),
cl.archive_regions_removed(),
cl.humongous_regions_removed());
{
MutexLocker x(ParGCRareEvent_lock, Mutex::_no_safepoint_check_flag);
_g1h->decrement_summary_bytes(cl.freed_bytes());

View File

@ -49,8 +49,15 @@ bool G1FullGCPrepareTask::G1CalculatePointersClosure::do_heap_region(HeapRegion*
if (!_bitmap->is_marked(obj)) {
free_humongous_region(hr);
}
} else if (hr->is_open_archive()) {
bool is_empty = _bitmap->get_next_marked_addr(hr->bottom(), hr->top()) >= hr->top();
if (is_empty) {
free_open_archive_region(hr);
}
} else {
assert(hr->is_archive(), "Only archive regions can also be pinned.");
// There are no other pinned regions than humongous or all kinds of archive regions
// at this time.
assert(hr->is_closed_archive(), "Only closed archive regions can also be pinned.");
}
} else {
assert(!hr->is_humongous(), "moving humongous objects not supported.");
@ -87,8 +94,6 @@ void G1FullGCPrepareTask::work(uint worker_id) {
G1CalculatePointersClosure closure(collector(), compaction_point);
G1CollectedHeap::heap()->heap_region_par_iterate_from_start(&closure, &_hrclaimer);
// Update humongous region sets
closure.update_sets();
compaction_point->update();
// Check if any regions was freed by this worker and store in task.
@ -104,7 +109,7 @@ G1FullGCPrepareTask::G1CalculatePointersClosure::G1CalculatePointersClosure(G1Fu
_collector(collector),
_bitmap(collector->mark_bitmap()),
_cp(cp),
_humongous_regions_removed(0) { }
_regions_freed(false) { }
void G1FullGCPrepareTask::G1CalculatePointersClosure::free_humongous_region(HeapRegion* hr) {
assert(hr->is_humongous(), "must be but region %u is %s", hr->hrm_index(), hr->get_short_type_str());
@ -112,13 +117,29 @@ void G1FullGCPrepareTask::G1CalculatePointersClosure::free_humongous_region(Heap
FreeRegionList dummy_free_list("Humongous Dummy Free List for G1MarkSweep");
hr->set_containing_set(NULL);
_humongous_regions_removed++;
_regions_freed = true;
_g1h->free_humongous_region(hr, &dummy_free_list);
prepare_for_compaction(hr);
dummy_free_list.remove_all();
}
void G1FullGCPrepareTask::G1CalculatePointersClosure::free_open_archive_region(HeapRegion* hr) {
assert(hr->is_pinned(), "must be");
assert(!hr->is_humongous(), "handled elsewhere");
assert(hr->is_open_archive(),
"Only Open archive regions may be freed here.");
FreeRegionList dummy_free_list("Pinned Dummy Free List for G1MarkSweep");
hr->set_containing_set(NULL);
_regions_freed = true;
_g1h->free_region(hr, &dummy_free_list);
prepare_for_compaction(hr);
dummy_free_list.remove_all();
}
void G1FullGCPrepareTask::G1CalculatePointersClosure::reset_region_metadata(HeapRegion* hr) {
hr->rem_set()->clear();
hr->clear_cardtable();
@ -202,15 +223,8 @@ void G1FullGCPrepareTask::prepare_serial_compaction() {
cp->update();
}
void G1FullGCPrepareTask::G1CalculatePointersClosure::update_sets() {
// We'll recalculate total used bytes and recreate the free list
// at the end of the GC, so no point in updating those values here.
_g1h->remove_from_old_sets(0, _humongous_regions_removed);
}
bool G1FullGCPrepareTask::G1CalculatePointersClosure::freed_regions() {
if (_humongous_regions_removed > 0) {
// Free regions from dead humongous regions.
if (_regions_freed) {
return true;
}

View File

@ -56,18 +56,19 @@ protected:
G1FullCollector* _collector;
G1CMBitMap* _bitmap;
G1FullGCCompactionPoint* _cp;
uint _humongous_regions_removed;
bool _regions_freed;
virtual void prepare_for_compaction(HeapRegion* hr);
void prepare_for_compaction_work(G1FullGCCompactionPoint* cp, HeapRegion* hr);
void free_humongous_region(HeapRegion* hr);
void free_open_archive_region(HeapRegion* hr);
void reset_region_metadata(HeapRegion* hr);
public:
G1CalculatePointersClosure(G1FullCollector* collector,
G1FullGCCompactionPoint* cp);
void update_sets();
bool do_heap_region(HeapRegion* hr);
bool freed_regions();
};

View File

@ -24,6 +24,7 @@
#include "precompiled.hpp"
#include "code/nmethod.hpp"
#include "gc/g1/g1Allocator.inline.hpp"
#include "gc/g1/g1BlockOffsetTable.inline.hpp"
#include "gc/g1/g1CollectedHeap.inline.hpp"
#include "gc/g1/g1CollectionSet.hpp"

View File

@ -177,6 +177,8 @@ public:
bool block_is_obj(const HeapWord* p) const;
// Returns whether the given object is dead based on TAMS and bitmap.
// An object is dead iff a) it was not allocated since the last mark (>TAMS), b) it
// is not marked (bitmap).
bool is_obj_dead(const oop obj, const G1CMBitMap* const prev_bitmap) const;
// Returns the object size for all valid block starts

View File

@ -166,7 +166,7 @@ inline bool HeapRegion::is_obj_dead(const oop obj, const G1CMBitMap* const prev_
assert(is_in_reserved(obj), "Object " PTR_FORMAT " must be in region", p2i(obj));
return !obj_allocated_since_prev_marking(obj) &&
!prev_bitmap->is_marked(obj) &&
!is_open_archive();
!is_closed_archive();
}
inline size_t HeapRegion::block_size(const HeapWord *addr) const {

View File

@ -538,6 +538,9 @@
product(bool, VerifyDuringGC, false, DIAGNOSTIC, \
"Verify memory system during GC (between phases)") \
\
product(bool, VerifyArchivedFields, trueInDebug, DIAGNOSTIC, \
"Verify memory when archived oop fields are loaded from CDS)") \
\
product(ccstrlist, VerifyGCType, "", DIAGNOSTIC, \
"GC type(s) to verify when Verify*GC is enabled." \
"Available types are collector specific.") \

View File

@ -246,6 +246,7 @@ void FileMapHeader::populate(FileMapInfo* mapinfo, size_t alignment) {
if (!DynamicDumpSharedSpaces) {
set_shared_path_table(mapinfo->_shared_path_table);
CDS_JAVA_HEAP_ONLY(_heap_obj_roots = CompressedOops::encode(HeapShared::roots());)
}
}
@ -1902,6 +1903,7 @@ void FileMapInfo::map_heap_regions_impl() {
&num_open_archive_heap_ranges,
true /* open */)) {
HeapShared::set_open_archive_heap_region_mapped();
HeapShared::set_roots(header()->heap_obj_roots());
}
}
}

View File

@ -238,7 +238,7 @@ class FileMapHeader: private CDSFileMapHeaderBase {
// some expensive operations.
bool _use_full_module_graph; // Can we use the full archived module graph?
size_t _ptrmap_size_in_bits; // Size of pointer relocation bitmap
narrowOop _heap_obj_roots; // An objArray that stores all the roots of archived heap objects
char* from_mapped_offset(size_t offset) const {
return mapped_base_address() + offset;
}
@ -286,6 +286,7 @@ public:
jshort app_module_paths_start_index() const { return _app_module_paths_start_index; }
jshort app_class_paths_start_index() const { return _app_class_paths_start_index; }
jshort num_module_paths() const { return _num_module_paths; }
narrowOop heap_obj_roots() const { return _heap_obj_roots; }
void set_has_platform_or_app_classes(bool v) { _has_platform_or_app_classes = v; }
void set_cloned_vtables(char* p) { set_mapped_offset(p, &_cloned_vtables_offset); }
@ -295,6 +296,8 @@ public:
void set_header_size(size_t s) { _header_size = s; }
void set_ptrmap_size_in_bits(size_t s) { _ptrmap_size_in_bits = s; }
void set_mapped_base_address(char* p) { _mapped_base_address = p; }
void set_heap_obj_roots(narrowOop r) { _heap_obj_roots = r; }
void set_i2i_entry_code_buffers(address p, size_t s) {
set_mapped_offset((char*)p, &_i2i_entry_code_buffers_offset);
_i2i_entry_code_buffers_size = s;

View File

@ -48,8 +48,12 @@
#include "memory/universe.hpp"
#include "oops/compressedOops.inline.hpp"
#include "oops/fieldStreams.inline.hpp"
#include "oops/objArrayOop.hpp"
#include "oops/oop.inline.hpp"
#include "prims/jvmtiExport.hpp"
#include "runtime/fieldDescriptor.inline.hpp"
#include "runtime/globals_extension.hpp"
#include "runtime/init.hpp"
#include "runtime/javaCalls.hpp"
#include "runtime/safepointVerifiers.hpp"
#include "utilities/bitMap.inline.hpp"
@ -105,6 +109,10 @@ const static int num_open_archive_subgraph_entry_fields =
const static int num_fmg_open_archive_subgraph_entry_fields =
sizeof(fmg_open_archive_subgraph_entry_fields) / sizeof(ArchivableStaticFieldInfo);
GrowableArrayCHeap<oop, mtClassShared>* HeapShared::_pending_roots = NULL;
narrowOop HeapShared::_roots_narrow;
OopHandle HeapShared::_roots;
////////////////////////////////////////////////////////////////
//
// Java heap object archiving support
@ -114,6 +122,13 @@ void HeapShared::fixup_mapped_heap_regions() {
FileMapInfo *mapinfo = FileMapInfo::current_info();
mapinfo->fixup_mapped_heap_regions();
set_archive_heap_region_fixed();
if (is_mapped()) {
_roots = OopHandle(Universe::vm_global(), decode_from_archive(_roots_narrow));
if (!MetaspaceShared::use_full_module_graph()) {
// Need to remove all the archived java.lang.Module objects from HeapShared::roots().
ClassLoaderDataShared::clear_archived_oops();
}
}
SystemDictionaryShared::update_archived_mirror_native_pointers();
}
@ -166,6 +181,70 @@ oop HeapShared::find_archived_heap_object(oop obj) {
}
}
int HeapShared::append_root(oop obj) {
assert(DumpSharedSpaces, "dump-time only");
// No GC should happen since we aren't scanning _pending_roots.
assert(Thread::current() == (Thread*)VMThread::vm_thread(), "should be in vm thread");
if (_pending_roots == NULL) {
_pending_roots = new GrowableArrayCHeap<oop, mtClassShared>(500);
}
return _pending_roots->append(obj);
}
objArrayOop HeapShared::roots() {
if (DumpSharedSpaces) {
assert(Thread::current() == (Thread*)VMThread::vm_thread(), "should be in vm thread");
if (!is_heap_object_archiving_allowed()) {
return NULL;
}
} else {
assert(UseSharedSpaces, "must be");
}
objArrayOop roots = (objArrayOop)_roots.resolve();
assert(roots != NULL, "should have been initialized");
return roots;
}
void HeapShared::set_roots(narrowOop roots) {
assert(UseSharedSpaces, "runtime only");
assert(open_archive_heap_region_mapped(), "must be");
_roots_narrow = roots;
}
// Returns an objArray that contains all the roots of the archived objects
oop HeapShared::get_root(int index, bool clear) {
assert(index >= 0, "sanity");
if (DumpSharedSpaces) {
assert(Thread::current() == (Thread*)VMThread::vm_thread(), "should be in vm thread");
assert(_pending_roots != NULL, "sanity");
return _pending_roots->at(index);
} else {
assert(UseSharedSpaces, "must be");
assert(!_roots.is_empty(), "must have loaded shared heap");
oop result = roots()->obj_at(index);
if (clear) {
clear_root(index);
}
return result;
}
}
void HeapShared::clear_root(int index) {
assert(index >= 0, "sanity");
assert(UseSharedSpaces, "must be");
if (open_archive_heap_region_mapped()) {
if (log_is_enabled(Debug, cds, heap)) {
oop old = roots()->obj_at(index);
log_debug(cds, heap)("Clearing root %d: was " PTR_FORMAT, index, p2i(old));
}
roots()->obj_at_put(index, NULL);
}
}
oop HeapShared::archive_heap_object(oop obj, Thread* THREAD) {
assert(DumpSharedSpaces, "dump-time only");
@ -201,8 +280,11 @@ oop HeapShared::archive_heap_object(oop obj, Thread* THREAD) {
ArchivedObjectCache* cache = archived_object_cache();
cache->put(obj, archived_oop);
log_debug(cds, heap)("Archived heap object " PTR_FORMAT " ==> " PTR_FORMAT,
p2i(obj), p2i(archived_oop));
if (log_is_enabled(Debug, cds, heap)) {
ResourceMark rm;
log_debug(cds, heap)("Archived heap object " PTR_FORMAT " ==> " PTR_FORMAT " : %s",
p2i(obj), p2i(archived_oop), obj->klass()->external_name());
}
} else {
log_error(cds, heap)(
"Cannot allocate space for object " PTR_FORMAT " in archived heap region",
@ -214,16 +296,6 @@ oop HeapShared::archive_heap_object(oop obj, Thread* THREAD) {
return archived_oop;
}
oop HeapShared::materialize_archived_object(narrowOop v) {
assert(archive_heap_region_fixed(),
"must be called after archive heap regions are fixed");
if (!CompressedOops::is_null(v)) {
oop obj = HeapShared::decode_from_archive(v);
return G1CollectedHeap::heap()->materialize_archived_object(obj);
}
return NULL;
}
void HeapShared::archive_klass_objects(Thread* THREAD) {
GrowableArray<Klass*>* klasses = MetaspaceShared::collected_klasses();
assert(klasses != NULL, "sanity");
@ -260,8 +332,8 @@ void HeapShared::run_full_gc_in_vm_thread() {
}
}
void HeapShared::archive_java_heap_objects(GrowableArray<MemRegion> *closed,
GrowableArray<MemRegion> *open) {
void HeapShared::archive_java_heap_objects(GrowableArray<MemRegion>* closed,
GrowableArray<MemRegion>* open) {
G1HeapVerifier::verify_ready_for_archiving();
@ -277,10 +349,6 @@ void HeapShared::archive_java_heap_objects(GrowableArray<MemRegion> *closed,
log_info(cds)("Dumping objects to open archive heap region ...");
copy_open_archive_heap_objects(open);
if (MetaspaceShared::use_full_module_graph()) {
ClassLoaderDataShared::init_archived_oops();
}
destroy_archived_object_cache();
}
@ -329,12 +397,44 @@ void HeapShared::copy_open_archive_heap_objects(
false /* is_closed_archive */,
true /* is_full_module_graph */,
THREAD);
ClassLoaderDataShared::init_archived_oops();
}
copy_roots();
G1CollectedHeap::heap()->end_archive_alloc_range(open_archive,
os::vm_allocation_granularity());
}
// Copy _pending_archive_roots into an objArray
void HeapShared::copy_roots() {
int length = _pending_roots != NULL ? _pending_roots->length() : 0;
int size = objArrayOopDesc::object_size(length);
Klass* k = Universe::objectArrayKlassObj(); // already relocated to point to archived klass
HeapWord* mem = G1CollectedHeap::heap()->archive_mem_allocate(size);
memset(mem, 0, size * BytesPerWord);
{
// This is copied from MemAllocator::finish
if (UseBiasedLocking) {
oopDesc::set_mark(mem, k->prototype_header());
} else {
oopDesc::set_mark(mem, markWord::prototype());
}
oopDesc::release_set_klass(mem, k);
}
{
// This is copied from ObjArrayAllocator::initialize
arrayOopDesc::set_length(mem, length);
}
_roots = OopHandle(Universe::vm_global(), (oop)mem);
for (int i = 0; i < length; i++) {
roots()->obj_at_put(i, _pending_roots->at(i));
}
log_info(cds)("archived obj roots[%d] = %d words, klass = %p, obj = %p", length, size, k, mem);
}
void HeapShared::init_narrow_oop_decoding(address base, int shift) {
_narrow_oop_base = base;
_narrow_oop_shift = shift;
@ -374,16 +474,15 @@ void KlassSubGraphInfo::add_subgraph_entry_field(
assert(DumpSharedSpaces, "dump time only");
if (_subgraph_entry_fields == NULL) {
_subgraph_entry_fields =
new(ResourceObj::C_HEAP, mtClass) GrowableArray<juint>(10, mtClass);
new(ResourceObj::C_HEAP, mtClass) GrowableArray<int>(10, mtClass);
}
_subgraph_entry_fields->append((juint)static_field_offset);
_subgraph_entry_fields->append(CompressedOops::narrow_oop_value(v));
_subgraph_entry_fields->append(is_closed_archive ? 1 : 0);
_subgraph_entry_fields->append(static_field_offset);
_subgraph_entry_fields->append(HeapShared::append_root(v));
}
// Add the Klass* for an object in the current KlassSubGraphInfo's subgraphs.
// Only objects of boot classes can be included in sub-graph.
void KlassSubGraphInfo::add_subgraph_object_klass(Klass* orig_k, Klass *relocated_k) {
void KlassSubGraphInfo::add_subgraph_object_klass(Klass* orig_k, Klass* relocated_k) {
assert(DumpSharedSpaces, "dump time only");
assert(relocated_k == MetaspaceShared::get_relocated_klass(orig_k),
"must be the relocated Klass in the shared space");
@ -437,6 +536,24 @@ void KlassSubGraphInfo::add_subgraph_object_klass(Klass* orig_k, Klass *relocate
}
_subgraph_object_klasses->append_if_missing(relocated_k);
_has_non_early_klasses |= is_non_early_klass(orig_k);
}
bool KlassSubGraphInfo::is_non_early_klass(Klass* k) {
if (k->is_objArray_klass()) {
k = ObjArrayKlass::cast(k)->bottom_klass();
}
if (k->is_instance_klass()) {
if (!SystemDictionaryShared::is_early_klass(InstanceKlass::cast(k))) {
ResourceMark rm;
log_info(cds, heap)("non-early: %s", k->external_name());
return true;
} else {
return false;
}
} else {
return false;
}
}
// Initialize an archived subgraph_info_record from the given KlassSubGraphInfo.
@ -445,14 +562,22 @@ void ArchivedKlassSubGraphInfoRecord::init(KlassSubGraphInfo* info) {
_entry_field_records = NULL;
_subgraph_object_klasses = NULL;
_is_full_module_graph = info->is_full_module_graph();
_has_non_early_klasses = info->has_non_early_klasses();
if (_has_non_early_klasses) {
ResourceMark rm;
log_info(cds, heap)(
"Subgraph of klass %s has non-early klasses and cannot be used when JVMTI ClassFileLoadHook is enabled",
_k->external_name());
}
// populate the entry fields
GrowableArray<juint>* entry_fields = info->subgraph_entry_fields();
GrowableArray<int>* entry_fields = info->subgraph_entry_fields();
if (entry_fields != NULL) {
int num_entry_fields = entry_fields->length();
assert(num_entry_fields % 3 == 0, "sanity");
assert(num_entry_fields % 2 == 0, "sanity");
_entry_field_records =
MetaspaceShared::new_ro_array<juint>(num_entry_fields);
MetaspaceShared::new_ro_array<int>(num_entry_fields);
for (int i = 0 ; i < num_entry_fields; i++) {
_entry_field_records->at_put(i, entry_fields->at(i));
}
@ -525,10 +650,91 @@ void HeapShared::serialize_subgraph_info_table_header(SerializeClosure* soc) {
_run_time_subgraph_info_table.serialize_header(soc);
}
void HeapShared::initialize_from_archived_subgraph(Klass* k, TRAPS) {
if (!open_archive_heap_region_mapped()) {
static void verify_the_heap(Klass* k, const char* which) {
if (VerifyArchivedFields) {
ResourceMark rm;
log_info(cds, heap)("Verify heap %s initializing static field(s) in %s",
which, k->external_name());
VM_Verify verify_op;
VMThread::execute(&verify_op);
if (!FLAG_IS_DEFAULT(VerifyArchivedFields)) {
// If VerifyArchivedFields has a non-default value (e.g., specified on the command-line), do
// more expensive checks.
if (is_init_completed()) {
FlagSetting fs1(VerifyBeforeGC, true);
FlagSetting fs2(VerifyDuringGC, true);
FlagSetting fs3(VerifyAfterGC, true);
Universe::heap()->collect(GCCause::_java_lang_system_gc);
}
}
}
}
// Before GC can execute, we must ensure that all oops reachable from HeapShared::roots()
// have a valid klass. I.e., oopDesc::klass() must have already been resolved.
//
// Note: if a ArchivedKlassSubGraphInfoRecord contains non-early classes, and JVMTI
// ClassFileLoadHook is enabled, it's possible for this class to be dynamically replaced. In
// this case, we will not load the ArchivedKlassSubGraphInfoRecord and will clear its roots.
void HeapShared::resolve_classes(TRAPS) {
if (!is_mapped()) {
return; // nothing to do
}
resolve_classes_for_subgraphs(closed_archive_subgraph_entry_fields,
num_closed_archive_subgraph_entry_fields,
CHECK);
resolve_classes_for_subgraphs(open_archive_subgraph_entry_fields,
num_open_archive_subgraph_entry_fields,
CHECK);
resolve_classes_for_subgraphs(fmg_open_archive_subgraph_entry_fields,
num_fmg_open_archive_subgraph_entry_fields,
CHECK);
}
void HeapShared::resolve_classes_for_subgraphs(ArchivableStaticFieldInfo fields[],
int num, TRAPS) {
for (int i = 0; i < num; i++) {
ArchivableStaticFieldInfo* info = &fields[i];
TempNewSymbol klass_name = SymbolTable::new_symbol(info->klass_name);
InstanceKlass* k = SystemDictionaryShared::find_builtin_class(klass_name);
assert(k != NULL && k->is_shared_boot_class(), "sanity");
resolve_classes_for_subgraph_of(k, CHECK);
}
}
void HeapShared::resolve_classes_for_subgraph_of(Klass* k, TRAPS) {
const ArchivedKlassSubGraphInfoRecord* record = resolve_or_init_classes_for_subgraph_of(k, /*do_init=*/false, THREAD);
if (HAS_PENDING_EXCEPTION) {
CLEAR_PENDING_EXCEPTION;
}
if (record == NULL) {
clear_archived_roots_of(k);
}
}
void HeapShared::initialize_from_archived_subgraph(Klass* k, TRAPS) {
if (!is_mapped()) {
return; // nothing to do
}
const ArchivedKlassSubGraphInfoRecord* record =
resolve_or_init_classes_for_subgraph_of(k, /*do_init=*/true, THREAD);
if (HAS_PENDING_EXCEPTION) {
CLEAR_PENDING_EXCEPTION;
// None of the field value will be set if there was an exception when initializing the classes.
// The java code will not see any of the archived objects in the
// subgraphs referenced from k in this case.
return;
}
if (record != NULL) {
init_archived_fields_for(k, record, THREAD);
}
}
const ArchivedKlassSubGraphInfoRecord*
HeapShared::resolve_or_init_classes_for_subgraph_of(Klass* k, bool do_init, TRAPS) {
assert(!DumpSharedSpaces, "Should not be called with DumpSharedSpaces");
unsigned int hash = SystemDictionaryShared::hash_for_shared_dictionary(k);
@ -538,79 +744,97 @@ void HeapShared::initialize_from_archived_subgraph(Klass* k, TRAPS) {
// during VM initialization time. No lock is needed.
if (record != NULL) {
if (record->is_full_module_graph() && !MetaspaceShared::use_full_module_graph()) {
return;
if (log_is_enabled(Info, cds, heap)) {
ResourceMark rm;
log_info(cds, heap)("subgraph %s cannot be used because full module graph is disabled",
k->external_name());
}
return NULL;
}
int i;
if (record->has_non_early_klasses() && JvmtiExport::should_post_class_file_load_hook()) {
if (log_is_enabled(Info, cds, heap)) {
ResourceMark rm;
log_info(cds, heap)("subgraph %s cannot be used because JVMTI ClassFileLoadHook is enabled",
k->external_name());
}
return NULL;
}
resolve_or_init(k, do_init, CHECK_NULL);
// Load/link/initialize the klasses of the objects in the subgraph.
// NULL class loader is used.
Array<Klass*>* klasses = record->subgraph_object_klasses();
if (klasses != NULL) {
for (i = 0; i < klasses->length(); i++) {
Klass* obj_k = klasses->at(i);
Klass* resolved_k = SystemDictionary::resolve_or_null(
(obj_k)->name(), THREAD);
if (resolved_k != obj_k) {
assert(!SystemDictionary::is_well_known_klass(resolved_k),
"shared well-known classes must not be replaced by JVMTI ClassFileLoadHook");
ResourceMark rm(THREAD);
log_info(cds, heap)("Failed to load subgraph because %s was not loaded from archive",
resolved_k->external_name());
return;
}
if ((obj_k)->is_instance_klass()) {
InstanceKlass* ik = InstanceKlass::cast(obj_k);
ik->initialize(THREAD);
} else if ((obj_k)->is_objArray_klass()) {
ObjArrayKlass* oak = ObjArrayKlass::cast(obj_k);
oak->initialize(THREAD);
}
for (int i = 0; i < klasses->length(); i++) {
resolve_or_init(klasses->at(i), do_init, CHECK_NULL);
}
}
}
if (HAS_PENDING_EXCEPTION) {
CLEAR_PENDING_EXCEPTION;
// None of the field value will be set if there was an exception.
// The java code will not see any of the archived objects in the
// subgraphs referenced from k in this case.
return;
return record;
}
void HeapShared::resolve_or_init(Klass* k, bool do_init, TRAPS) {
if (!do_init) {
if (k->class_loader_data() == NULL) {
Klass* resolved_k = SystemDictionary::resolve_or_null(k->name(), CHECK);
assert(resolved_k == k, "classes used by archived heap must not be replaced by JVMTI ClassFileLoadHook");
}
} else {
assert(k->class_loader_data() != NULL, "must have been resolved by HeapShared::resolve_classes");
if (k->is_instance_klass()) {
InstanceKlass* ik = InstanceKlass::cast(k);
ik->initialize(CHECK);
} else if (k->is_objArray_klass()) {
ObjArrayKlass* oak = ObjArrayKlass::cast(k);
oak->initialize(CHECK);
}
}
}
void HeapShared::init_archived_fields_for(Klass* k, const ArchivedKlassSubGraphInfoRecord* record, TRAPS) {
verify_the_heap(k, "before");
// Load the subgraph entry fields from the record and store them back to
// the corresponding fields within the mirror.
oop m = k->java_mirror();
Array<int>* 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");
for (int i = 0; i < efr_len; i += 2) {
int field_offset = entry_field_records->at(i);
int root_index = entry_field_records->at(i+1);
oop v = get_root(root_index, /*clear=*/true);
m->obj_field_put(field_offset, v);
log_debug(cds, heap)(" " PTR_FORMAT " init field @ %2d = " PTR_FORMAT, p2i(k), field_offset, p2i(v));
}
// Load the subgraph entry fields from the record and store them back to
// the corresponding fields within the mirror.
oop m = k->java_mirror();
Array<juint>* entry_field_records = record->entry_field_records();
// Done. Java code can see the archived sub-graphs referenced from k's
// mirror after this point.
if (log_is_enabled(Info, cds, heap)) {
ResourceMark rm;
log_info(cds, heap)("initialize_from_archived_subgraph %s " PTR_FORMAT "%s",
k->external_name(), p2i(k), JvmtiExport::is_early_phase() ? " (early)" : "");
}
}
verify_the_heap(k, "after ");
}
void HeapShared::clear_archived_roots_of(Klass* k) {
unsigned int hash = SystemDictionaryShared::hash_for_shared_dictionary(k);
const ArchivedKlassSubGraphInfoRecord* record = _run_time_subgraph_info_table.lookup(k, hash, 0);
if (record != NULL) {
Array<int>* entry_field_records = record->entry_field_records();
if (entry_field_records != NULL) {
int efr_len = entry_field_records->length();
assert(efr_len % 3 == 0, "sanity");
for (i = 0; i < efr_len;) {
int field_offset = entry_field_records->at(i);
narrowOop nv = CompressedOops::narrow_oop_cast(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 += 3;
log_debug(cds, heap)(" " PTR_FORMAT " init field @ %2d = " PTR_FORMAT, p2i(k), field_offset, p2i(v));
}
// Done. Java code can see the archived sub-graphs referenced from k's
// mirror after this point.
if (log_is_enabled(Info, cds, heap)) {
ResourceMark rm;
log_info(cds, heap)("initialize_from_archived_subgraph %s " PTR_FORMAT,
k->external_name(), p2i(k));
assert(efr_len % 2 == 0, "sanity");
for (int i = 0; i < efr_len; i += 2) {
int root_index = entry_field_records->at(i+1);
clear_root(root_index);
}
}
}

View File

@ -33,6 +33,7 @@
#include "oops/compressedOops.hpp"
#include "oops/objArrayKlass.hpp"
#include "oops/oop.hpp"
#include "oops/oopHandle.hpp"
#include "oops/typeArrayKlass.hpp"
#include "utilities/bitMap.hpp"
#include "utilities/growableArray.hpp"
@ -64,14 +65,24 @@ class KlassSubGraphInfo: public CHeapObj<mtClass> {
// A list of _k's static fields as the entry points of archived sub-graphs.
// For each entry field, it is a tuple of field_offset, field_value and
// is_closed_archive flag.
GrowableArray<juint>* _subgraph_entry_fields;
GrowableArray<int>* _subgraph_entry_fields;
// Does this KlassSubGraphInfo belong to the archived full module graph
bool _is_full_module_graph;
// Does this KlassSubGraphInfo references any classes that were loaded while
// JvmtiExport::is_early_phase()!=true. If so, this KlassSubGraphInfo cannot be
// used at runtime if JVMTI ClassFileLoadHook is enabled.
bool _has_non_early_klasses;
static bool is_non_early_klass(Klass* k);
public:
KlassSubGraphInfo(Klass* k, bool is_full_module_graph) :
_k(k), _subgraph_object_klasses(NULL),
_subgraph_entry_fields(NULL),
_is_full_module_graph(is_full_module_graph) {}
_is_full_module_graph(is_full_module_graph),
_has_non_early_klasses(false) {}
~KlassSubGraphInfo() {
if (_subgraph_object_klasses != NULL) {
delete _subgraph_object_klasses;
@ -85,7 +96,7 @@ class KlassSubGraphInfo: public CHeapObj<mtClass> {
GrowableArray<Klass*>* subgraph_object_klasses() {
return _subgraph_object_klasses;
}
GrowableArray<juint>* subgraph_entry_fields() {
GrowableArray<int>* subgraph_entry_fields() {
return _subgraph_entry_fields;
}
void add_subgraph_entry_field(int static_field_offset, oop v,
@ -96,6 +107,7 @@ class KlassSubGraphInfo: public CHeapObj<mtClass> {
_subgraph_object_klasses->length();
}
bool is_full_module_graph() const { return _is_full_module_graph; }
bool has_non_early_klasses() const { return _has_non_early_klasses; }
};
// An archived record of object sub-graphs reachable from static
@ -105,9 +117,10 @@ class ArchivedKlassSubGraphInfoRecord {
private:
Klass* _k;
bool _is_full_module_graph;
bool _has_non_early_klasses;
// contains pairs of field offset and value for each subgraph entry field
Array<juint>* _entry_field_records;
Array<int>* _entry_field_records;
// klasses of objects in archived sub-graphs referenced from the entry points
// (static fields) in the containing class
@ -117,9 +130,10 @@ class ArchivedKlassSubGraphInfoRecord {
_k(NULL), _entry_field_records(NULL), _subgraph_object_klasses(NULL) {}
void init(KlassSubGraphInfo* info);
Klass* klass() const { return _k; }
Array<juint>* entry_field_records() const { return _entry_field_records; }
Array<int>* entry_field_records() const { return _entry_field_records; }
Array<Klass*>* subgraph_object_klasses() const { return _subgraph_object_klasses; }
bool is_full_module_graph() const { return _is_full_module_graph; }
bool has_non_early_klasses() const { return _has_non_early_klasses; }
};
#endif // INCLUDE_CDS_JAVA_HEAP
@ -224,6 +238,10 @@ private:
static SeenObjectsTable *_seen_objects_table;
static GrowableArrayCHeap<oop, mtClassShared>* _pending_roots;
static narrowOop _roots_narrow;
static OopHandle _roots;
static void init_seen_objects_table() {
assert(_seen_objects_table == NULL, "must be");
_seen_objects_table = new (ResourceObj::C_HEAP, mtClass)SeenObjectsTable();
@ -254,7 +272,16 @@ private:
static void set_has_been_seen_during_subgraph_recording(oop obj);
static void check_module_oop(oop orig_module_obj);
static void copy_roots();
static void resolve_classes_for_subgraphs(ArchivableStaticFieldInfo fields[],
int num, TRAPS);
static void resolve_classes_for_subgraph_of(Klass* k, TRAPS);
static void clear_archived_roots_of(Klass* k);
static const ArchivedKlassSubGraphInfoRecord*
resolve_or_init_classes_for_subgraph_of(Klass* k, bool do_init, TRAPS);
static void resolve_or_init(Klass* k, bool do_init, TRAPS);
static void init_archived_fields_for(Klass* k, const ArchivedKlassSubGraphInfoRecord* record, TRAPS);
public:
static void reset_archived_object_states(TRAPS);
static void create_archived_object_cache() {
@ -271,7 +298,6 @@ private:
static oop find_archived_heap_object(oop obj);
static oop archive_heap_object(oop obj, Thread* THREAD);
static oop materialize_archived_object(narrowOop v);
static void archive_klass_objects(Thread* THREAD);
@ -295,6 +321,32 @@ private:
static ResourceBitMap calculate_oopmap(MemRegion region);
static void add_to_dumped_interned_strings(oop string);
// We use the HeapShared::roots() array to make sure that objects stored in the
// archived heap regions are not prematurely collected. These roots include:
//
// - mirrors of classes that have not yet been loaded.
// - ConstantPool::resolved_references() of classes that have not yet been loaded.
// - ArchivedKlassSubGraphInfoRecords that have not been initialized
// - java.lang.Module objects that have not yet been added to the module graph
//
// When a mirror M becomes referenced by a newly loaded class K, M will be removed
// from HeapShared::roots() via clear_root(), and K will be responsible for
// keeping M alive.
//
// Other types of roots are also cleared similarly when they become referenced.
// Dump-time only. Returns the index of the root, which can be used at run time to read
// the root using get_root(index, ...).
static int append_root(oop obj);
// Dump-time and runtime
static objArrayOop roots();
static oop get_root(int index, bool clear=false);
// Run-time only
static void set_roots(narrowOop roots);
static void clear_root(int index);
#endif // INCLUDE_CDS_JAVA_HEAP
public:
@ -307,31 +359,35 @@ private:
static bool is_heap_region(int idx) {
CDS_JAVA_HEAP_ONLY(return (idx >= MetaspaceShared::first_closed_archive_heap_region &&
idx <= MetaspaceShared::last_open_archive_heap_region));
idx <= MetaspaceShared::last_open_archive_heap_region);)
NOT_CDS_JAVA_HEAP_RETURN_(false);
}
static void set_closed_archive_heap_region_mapped() {
CDS_JAVA_HEAP_ONLY(_closed_archive_heap_region_mapped = true);
CDS_JAVA_HEAP_ONLY(_closed_archive_heap_region_mapped = true;)
NOT_CDS_JAVA_HEAP_RETURN;
}
static bool closed_archive_heap_region_mapped() {
CDS_JAVA_HEAP_ONLY(return _closed_archive_heap_region_mapped);
CDS_JAVA_HEAP_ONLY(return _closed_archive_heap_region_mapped;)
NOT_CDS_JAVA_HEAP_RETURN_(false);
}
static void set_open_archive_heap_region_mapped() {
CDS_JAVA_HEAP_ONLY(_open_archive_heap_region_mapped = true);
CDS_JAVA_HEAP_ONLY(_open_archive_heap_region_mapped = true;)
NOT_CDS_JAVA_HEAP_RETURN;
}
static bool open_archive_heap_region_mapped() {
CDS_JAVA_HEAP_ONLY(return _open_archive_heap_region_mapped);
CDS_JAVA_HEAP_ONLY(return _open_archive_heap_region_mapped;)
NOT_CDS_JAVA_HEAP_RETURN_(false);
}
static bool is_mapped() {
return closed_archive_heap_region_mapped() && open_archive_heap_region_mapped();
}
static void fixup_mapped_heap_regions() NOT_CDS_JAVA_HEAP_RETURN;
inline static bool is_archived_object(oop p) NOT_CDS_JAVA_HEAP_RETURN_(false);
static void resolve_classes(TRAPS) NOT_CDS_JAVA_HEAP_RETURN;
static void initialize_from_archived_subgraph(Klass* k, TRAPS) NOT_CDS_JAVA_HEAP_RETURN;
// NarrowOops stored in the CDS archive may use a different encoding scheme

View File

@ -505,7 +505,7 @@ void MetaspaceShared::serialize(SerializeClosure* soc) {
CppVtables::serialize(soc);
soc->do_tag(--tag);
CDS_JAVA_HEAP_ONLY(ClassLoaderDataShared::serialize(soc));
CDS_JAVA_HEAP_ONLY(ClassLoaderDataShared::serialize(soc);)
soc->do_tag(666);
}
@ -1477,8 +1477,6 @@ MapArchiveResult MetaspaceShared::map_archives(FileMapInfo* static_mapinfo, File
// map_heap_regions() compares the current narrow oop and klass encodings
// with the archived ones, so it must be done after all encodings are determined.
static_mapinfo->map_heap_regions();
disable_full_module_graph(); // Disabled temporarily for JDK-8253081
}
});
log_info(cds)("optimized module handling: %s", MetaspaceShared::use_optimized_module_handling() ? "enabled" : "disabled");

View File

@ -372,6 +372,7 @@ void ConstantPool::restore_unshareable_info(TRAPS) {
// Create handle for the archived resolved reference array object
Handle refs_handle(THREAD, archived);
set_resolved_references(loader_data->add_handle(refs_handle));
_cache->clear_archived_references();
} else
#endif
{

View File

@ -774,15 +774,22 @@ void ConstantPoolCache::deallocate_contents(ClassLoaderData* data) {
#if INCLUDE_CDS_JAVA_HEAP
oop ConstantPoolCache::archived_references() {
if (CompressedOops::is_null(_archived_references)) {
if (_archived_references_index < 0) {
return NULL;
}
return HeapShared::materialize_archived_object(_archived_references);
return HeapShared::get_root(_archived_references_index);
}
void ConstantPoolCache::clear_archived_references() {
if (_archived_references_index >= 0) {
HeapShared::clear_root(_archived_references_index);
_archived_references_index = -1;
}
}
void ConstantPoolCache::set_archived_references(oop o) {
assert(DumpSharedSpaces, "called only during runtime");
_archived_references = CompressedOops::encode(o);
_archived_references_index = HeapShared::append_root(o);
}
#endif

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1998, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1998, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -417,7 +417,7 @@ class ConstantPoolCache: public MetaspaceObj {
Array<u2>* _reference_map;
// The narrowOop pointer to the archived resolved_references. Set at CDS dump
// time when caching java heap object is supported.
CDS_JAVA_HEAP_ONLY(narrowOop _archived_references;)
CDS_JAVA_HEAP_ONLY(int _archived_references_index;)
// Sizing
debug_only(friend class ClassVerifier;)
@ -444,6 +444,7 @@ class ConstantPoolCache: public MetaspaceObj {
oop archived_references() NOT_CDS_JAVA_HEAP_RETURN_(NULL);
void set_archived_references(oop o) NOT_CDS_JAVA_HEAP_RETURN;
void clear_archived_references() NOT_CDS_JAVA_HEAP_RETURN;
inline oop resolved_references();
void set_resolved_references(OopHandle s) { _resolved_references = s; }

View File

@ -89,7 +89,7 @@ inline ConstantPoolCache::ConstantPoolCache(int length,
const intStack& invokedynamic_references_map) :
_length(length),
_constant_pool(NULL) {
CDS_JAVA_HEAP_ONLY(_archived_references = narrowOop::null;)
CDS_JAVA_HEAP_ONLY(_archived_references_index = -1;)
initialize(inverse_index_map, invokedynamic_inverse_index_map,
invokedynamic_references_map);
for (int i = 0; i < length; i++) {

View File

@ -52,7 +52,13 @@ void InstanceMirrorKlass::oop_oop_iterate(oop obj, OopClosureType* closure) {
Klass* klass = java_lang_Class::as_Klass_raw(obj);
// We'll get NULL for primitive mirrors.
if (klass != NULL) {
if (klass->is_instance_klass() && klass->class_loader_data()->has_class_mirror_holder()) {
if (klass->class_loader_data() == NULL) {
// This is a mirror that belongs to a shared class that has not be loaded yet.
// It's only reachable via HeapShared::roots(). All of its fields should be zero
// so there's no need to scan.
assert(klass->is_shared(), "must be");
return;
} else if (klass->is_instance_klass() && klass->class_loader_data()->has_class_mirror_holder()) {
// A non-strong hidden class or an unsafe anonymous class doesn't have its own class loader,
// so when handling the java mirror for the class we need to make sure its class
// loader data is claimed, this is done by calling do_cld explicitly.

View File

@ -205,7 +205,7 @@ Klass::Klass(KlassID id) : _id(id),
_prototype_header(markWord::prototype()),
_shared_class_path_index(-1) {
CDS_ONLY(_shared_class_flags = 0;)
CDS_JAVA_HEAP_ONLY(_archived_mirror = narrowOop::null;)
CDS_JAVA_HEAP_ONLY(_archived_mirror_index = -1;)
_primary_supers[0] = this;
set_super_check_offset(in_bytes(primary_supers_offset()));
}
@ -574,10 +574,9 @@ void Klass::restore_unshareable_info(ClassLoaderData* loader_data, Handle protec
// set. We leave the class on the CLD list, even if incomplete so that we don't
// modify the CLD list outside a safepoint.
if (class_loader_data() == NULL) {
// Restore class_loader_data to the null class loader data
set_class_loader_data(loader_data);
// Add to null class loader list first before creating the mirror
// Add to class loader list first before creating the mirror
// (same order as class file parsing)
loader_data->add_class(this);
}
@ -598,7 +597,7 @@ void Klass::restore_unshareable_info(ClassLoaderData* loader_data, Handle protec
// Obtain java.lang.Module, if available
Handle module_handle(THREAD, ((module_entry != NULL) ? module_entry->module() : (oop)NULL));
if (this->has_raw_archived_mirror()) {
if (this->has_archived_mirror_index()) {
ResourceMark rm(THREAD);
log_debug(cds, mirror)("%s has raw archived mirror", external_name());
if (HeapShared::open_archive_heap_region_mapped()) {
@ -613,7 +612,7 @@ void Klass::restore_unshareable_info(ClassLoaderData* loader_data, Handle protec
// No archived mirror data
log_debug(cds, mirror)("No archived mirror data for %s", external_name());
clear_java_mirror_handle();
this->clear_has_raw_archived_mirror();
this->clear_archived_mirror_index();
}
// Only recreate it if not present. A previous attempt to restore may have
@ -626,21 +625,22 @@ void Klass::restore_unshareable_info(ClassLoaderData* loader_data, Handle protec
}
#if INCLUDE_CDS_JAVA_HEAP
// Used at CDS dump time to access the archived mirror. No GC barrier.
oop Klass::archived_java_mirror_raw() {
assert(has_raw_archived_mirror(), "must have raw archived mirror");
return CompressedOops::decode(_archived_mirror);
oop Klass::archived_java_mirror() {
assert(has_archived_mirror_index(), "must have archived mirror");
return HeapShared::get_root(_archived_mirror_index);
}
narrowOop Klass::archived_java_mirror_raw_narrow() {
assert(has_raw_archived_mirror(), "must have raw archived mirror");
return _archived_mirror;
void Klass::clear_archived_mirror_index() {
if (_archived_mirror_index >= 0) {
HeapShared::clear_root(_archived_mirror_index);
}
_archived_mirror_index = -1;
}
// No GC barrier
void Klass::set_archived_java_mirror_raw(oop m) {
void Klass::set_archived_java_mirror(oop m) {
assert(DumpSharedSpaces, "called only during runtime");
_archived_mirror = CompressedOops::encode(m);
_archived_mirror_index = HeapShared::append_root(m);
}
#endif // INCLUDE_CDS_JAVA_HEAP

View File

@ -176,14 +176,11 @@ private:
// Flags of the current shared class.
u2 _shared_class_flags;
enum {
_has_raw_archived_mirror = 1,
_archived_lambda_proxy_is_available = 2
};
#endif
// The _archived_mirror is set at CDS dump time pointing to the cached mirror
// in the open archive heap region when archiving java object is supported.
CDS_JAVA_HEAP_ONLY(narrowOop _archived_mirror;)
CDS_JAVA_HEAP_ONLY(int _archived_mirror_index;)
protected:
@ -262,9 +259,8 @@ protected:
oop java_mirror_no_keepalive() const;
void set_java_mirror(Handle m);
oop archived_java_mirror_raw() NOT_CDS_JAVA_HEAP_RETURN_(NULL); // no GC barrier
narrowOop archived_java_mirror_raw_narrow() NOT_CDS_JAVA_HEAP_RETURN_(narrowOop::null); // no GC barrier
void set_archived_java_mirror_raw(oop m) NOT_CDS_JAVA_HEAP_RETURN; // no GC barrier
oop archived_java_mirror() NOT_CDS_JAVA_HEAP_RETURN_(NULL);
void set_archived_java_mirror(oop m) NOT_CDS_JAVA_HEAP_RETURN;
// Temporary mirror switch used by RedefineClasses
void replace_java_mirror(oop mirror);
@ -307,17 +303,13 @@ protected:
_shared_class_path_index = index;
};
void set_has_raw_archived_mirror() {
CDS_ONLY(_shared_class_flags |= _has_raw_archived_mirror;)
}
void clear_has_raw_archived_mirror() {
CDS_ONLY(_shared_class_flags &= ~_has_raw_archived_mirror;)
}
bool has_raw_archived_mirror() const {
CDS_ONLY(return (_shared_class_flags & _has_raw_archived_mirror) != 0;)
NOT_CDS(return false;)
bool has_archived_mirror_index() const {
CDS_JAVA_HEAP_ONLY(return _archived_mirror_index >= 0;)
NOT_CDS_JAVA_HEAP(return false);
}
void clear_archived_mirror_index() NOT_CDS_JAVA_HEAP_RETURN;
void set_lambda_proxy_is_available() {
CDS_ONLY(_shared_class_flags |= _archived_lambda_proxy_is_available;)
}
@ -534,7 +526,7 @@ protected:
bool is_unshareable_info_restored() const {
assert(is_shared(), "use this for shared classes only");
if (has_raw_archived_mirror()) {
if (has_archived_mirror_index()) {
// _java_mirror is not a valid OopHandle but rather an encoded reference in the shared heap
return false;
} else if (_java_mirror.ptr_raw() == NULL) {

View File

@ -85,7 +85,6 @@ gc/metaspace/CompressedClassSpaceSizeInJmapHeap.java 8241293 macosx-x64
# :hotspot_runtime
runtime/cds/DeterministicDump.java 8253495 generic-all
runtime/cds/serviceability/ReplaceCriticalClassesForSubgraphs.java 8253081 generic-all
runtime/jni/terminatedThread/TestTerminatedThread.java 8219652 aix-ppc64
runtime/ReservedStack/ReservedStackTest.java 8231031 generic-all

View File

@ -172,10 +172,7 @@ public class ReplaceCriticalClasses {
opts.addSuffix("-XX:+WhiteBoxAPI",
"-Xbootclasspath/a:" + ClassFileInstaller.getJarPath("whitebox.jar"));
}
if (subgraph) {
opts.addSuffix("-Xlog:cds,cds+heap");
}
opts.addSuffix("-Xlog:cds,cds+heap");
opts.addSuffix("ReplaceCriticalClasses",
"child",
shared,