mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-28 15:24:43 +02:00
8027959: Early reclamation of large objects in G1
Try to reclaim humongous objects at every young collection after doing a conservative estimate of its liveness. Reviewed-by: brutisso, mgerdin
This commit is contained in:
parent
bc56e061ec
commit
f161bd6d16
12 changed files with 537 additions and 32 deletions
|
@ -1926,6 +1926,8 @@ G1CollectedHeap::G1CollectedHeap(G1CollectorPolicy* policy_) :
|
||||||
_secondary_free_list("Secondary Free List", new SecondaryFreeRegionListMtSafeChecker()),
|
_secondary_free_list("Secondary Free List", new SecondaryFreeRegionListMtSafeChecker()),
|
||||||
_old_set("Old Set", false /* humongous */, new OldRegionSetMtSafeChecker()),
|
_old_set("Old Set", false /* humongous */, new OldRegionSetMtSafeChecker()),
|
||||||
_humongous_set("Master Humongous Set", true /* humongous */, new HumongousRegionSetMtSafeChecker()),
|
_humongous_set("Master Humongous Set", true /* humongous */, new HumongousRegionSetMtSafeChecker()),
|
||||||
|
_humongous_is_live(),
|
||||||
|
_has_humongous_reclaim_candidates(false),
|
||||||
_free_regions_coming(false),
|
_free_regions_coming(false),
|
||||||
_young_list(new YoungList(this)),
|
_young_list(new YoungList(this)),
|
||||||
_gc_time_stamp(0),
|
_gc_time_stamp(0),
|
||||||
|
@ -2082,6 +2084,7 @@ jint G1CollectedHeap::initialize() {
|
||||||
_g1h = this;
|
_g1h = this;
|
||||||
|
|
||||||
_in_cset_fast_test.initialize(_g1_reserved.start(), _g1_reserved.end(), HeapRegion::GrainBytes);
|
_in_cset_fast_test.initialize(_g1_reserved.start(), _g1_reserved.end(), HeapRegion::GrainBytes);
|
||||||
|
_humongous_is_live.initialize(_g1_reserved.start(), _g1_reserved.end(), HeapRegion::GrainBytes);
|
||||||
|
|
||||||
// Create the ConcurrentMark data structure and thread.
|
// Create the ConcurrentMark data structure and thread.
|
||||||
// (Must do this late, so that "max_regions" is defined.)
|
// (Must do this late, so that "max_regions" is defined.)
|
||||||
|
@ -2177,6 +2180,11 @@ void G1CollectedHeap::stop() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void G1CollectedHeap::clear_humongous_is_live_table() {
|
||||||
|
guarantee(G1ReclaimDeadHumongousObjectsAtYoungGC, "Should only be called if true");
|
||||||
|
_humongous_is_live.clear();
|
||||||
|
}
|
||||||
|
|
||||||
size_t G1CollectedHeap::conservative_max_heap_alignment() {
|
size_t G1CollectedHeap::conservative_max_heap_alignment() {
|
||||||
return HeapRegion::max_region_size();
|
return HeapRegion::max_region_size();
|
||||||
}
|
}
|
||||||
|
@ -3771,6 +3779,61 @@ size_t G1CollectedHeap::cards_scanned() {
|
||||||
return g1_rem_set()->cardsScanned();
|
return g1_rem_set()->cardsScanned();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool G1CollectedHeap::humongous_region_is_always_live(uint index) {
|
||||||
|
HeapRegion* region = region_at(index);
|
||||||
|
assert(region->startsHumongous(), "Must start a humongous object");
|
||||||
|
return oop(region->bottom())->is_objArray() || !region->rem_set()->is_empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
class RegisterHumongousWithInCSetFastTestClosure : public HeapRegionClosure {
|
||||||
|
private:
|
||||||
|
size_t _total_humongous;
|
||||||
|
size_t _candidate_humongous;
|
||||||
|
public:
|
||||||
|
RegisterHumongousWithInCSetFastTestClosure() : _total_humongous(0), _candidate_humongous(0) {
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool doHeapRegion(HeapRegion* r) {
|
||||||
|
if (!r->startsHumongous()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
G1CollectedHeap* g1h = G1CollectedHeap::heap();
|
||||||
|
|
||||||
|
uint region_idx = r->hrs_index();
|
||||||
|
bool is_candidate = !g1h->humongous_region_is_always_live(region_idx);
|
||||||
|
// Is_candidate already filters out humongous regions with some remembered set.
|
||||||
|
// This will not lead to humongous object that we mistakenly keep alive because
|
||||||
|
// during young collection the remembered sets will only be added to.
|
||||||
|
if (is_candidate) {
|
||||||
|
g1h->register_humongous_region_with_in_cset_fast_test(region_idx);
|
||||||
|
_candidate_humongous++;
|
||||||
|
}
|
||||||
|
_total_humongous++;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t total_humongous() const { return _total_humongous; }
|
||||||
|
size_t candidate_humongous() const { return _candidate_humongous; }
|
||||||
|
};
|
||||||
|
|
||||||
|
void G1CollectedHeap::register_humongous_regions_with_in_cset_fast_test() {
|
||||||
|
if (!G1ReclaimDeadHumongousObjectsAtYoungGC) {
|
||||||
|
g1_policy()->phase_times()->record_fast_reclaim_humongous_stats(0, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RegisterHumongousWithInCSetFastTestClosure cl;
|
||||||
|
heap_region_iterate(&cl);
|
||||||
|
g1_policy()->phase_times()->record_fast_reclaim_humongous_stats(cl.total_humongous(),
|
||||||
|
cl.candidate_humongous());
|
||||||
|
_has_humongous_reclaim_candidates = cl.candidate_humongous() > 0;
|
||||||
|
|
||||||
|
if (_has_humongous_reclaim_candidates) {
|
||||||
|
clear_humongous_is_live_table();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
G1CollectedHeap::setup_surviving_young_words() {
|
G1CollectedHeap::setup_surviving_young_words() {
|
||||||
assert(_surviving_young_words == NULL, "pre-condition");
|
assert(_surviving_young_words == NULL, "pre-condition");
|
||||||
|
@ -4058,6 +4121,8 @@ G1CollectedHeap::do_collection_pause_at_safepoint(double target_pause_time_ms) {
|
||||||
|
|
||||||
g1_policy()->finalize_cset(target_pause_time_ms, evacuation_info);
|
g1_policy()->finalize_cset(target_pause_time_ms, evacuation_info);
|
||||||
|
|
||||||
|
register_humongous_regions_with_in_cset_fast_test();
|
||||||
|
|
||||||
_cm->note_start_of_gc();
|
_cm->note_start_of_gc();
|
||||||
// We should not verify the per-thread SATB buffers given that
|
// We should not verify the per-thread SATB buffers given that
|
||||||
// we have not filtered them yet (we'll do so during the
|
// we have not filtered them yet (we'll do so during the
|
||||||
|
@ -4108,6 +4173,9 @@ G1CollectedHeap::do_collection_pause_at_safepoint(double target_pause_time_ms) {
|
||||||
true /* verify_fingers */);
|
true /* verify_fingers */);
|
||||||
|
|
||||||
free_collection_set(g1_policy()->collection_set(), evacuation_info);
|
free_collection_set(g1_policy()->collection_set(), evacuation_info);
|
||||||
|
|
||||||
|
eagerly_reclaim_humongous_regions();
|
||||||
|
|
||||||
g1_policy()->clear_collection_set();
|
g1_policy()->clear_collection_set();
|
||||||
|
|
||||||
cleanup_surviving_young_words();
|
cleanup_surviving_young_words();
|
||||||
|
@ -4608,7 +4676,9 @@ void G1ParCopyClosure<barrier, do_mark_object>::do_oop_work(T* p) {
|
||||||
|
|
||||||
assert(_worker_id == _par_scan_state->queue_num(), "sanity");
|
assert(_worker_id == _par_scan_state->queue_num(), "sanity");
|
||||||
|
|
||||||
if (_g1->in_cset_fast_test(obj)) {
|
G1CollectedHeap::in_cset_state_t state = _g1->in_cset_state(obj);
|
||||||
|
|
||||||
|
if (state == G1CollectedHeap::InCSet) {
|
||||||
oop forwardee;
|
oop forwardee;
|
||||||
if (obj->is_forwarded()) {
|
if (obj->is_forwarded()) {
|
||||||
forwardee = obj->forwardee();
|
forwardee = obj->forwardee();
|
||||||
|
@ -4627,6 +4697,9 @@ void G1ParCopyClosure<barrier, do_mark_object>::do_oop_work(T* p) {
|
||||||
do_klass_barrier(p, forwardee);
|
do_klass_barrier(p, forwardee);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
if (state == G1CollectedHeap::IsHumongous) {
|
||||||
|
_g1->set_humongous_is_live(obj);
|
||||||
|
}
|
||||||
// The object is not in collection set. If we're a root scanning
|
// The object is not in collection set. If we're a root scanning
|
||||||
// closure during an initial mark pause then attempt to mark the object.
|
// closure during an initial mark pause then attempt to mark the object.
|
||||||
if (do_mark_object == G1MarkFromRoot) {
|
if (do_mark_object == G1MarkFromRoot) {
|
||||||
|
@ -5450,12 +5523,21 @@ class G1KeepAliveClosure: public OopClosure {
|
||||||
public:
|
public:
|
||||||
G1KeepAliveClosure(G1CollectedHeap* g1) : _g1(g1) {}
|
G1KeepAliveClosure(G1CollectedHeap* g1) : _g1(g1) {}
|
||||||
void do_oop(narrowOop* p) { guarantee(false, "Not needed"); }
|
void do_oop(narrowOop* p) { guarantee(false, "Not needed"); }
|
||||||
void do_oop( oop* p) {
|
void do_oop(oop* p) {
|
||||||
oop obj = *p;
|
oop obj = *p;
|
||||||
|
|
||||||
if (_g1->obj_in_cs(obj)) {
|
G1CollectedHeap::in_cset_state_t cset_state = _g1->in_cset_state(obj);
|
||||||
|
if (obj == NULL || cset_state == G1CollectedHeap::InNeither) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (cset_state == G1CollectedHeap::InCSet) {
|
||||||
assert( obj->is_forwarded(), "invariant" );
|
assert( obj->is_forwarded(), "invariant" );
|
||||||
*p = obj->forwardee();
|
*p = obj->forwardee();
|
||||||
|
} else {
|
||||||
|
assert(!obj->is_forwarded(), "invariant" );
|
||||||
|
assert(cset_state == G1CollectedHeap::IsHumongous,
|
||||||
|
err_msg("Only allowed InCSet state is IsHumongous, but is %d", cset_state));
|
||||||
|
_g1->set_humongous_is_live(obj);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -5485,7 +5567,7 @@ public:
|
||||||
template <class T> void do_oop_work(T* p) {
|
template <class T> void do_oop_work(T* p) {
|
||||||
oop obj = oopDesc::load_decode_heap_oop(p);
|
oop obj = oopDesc::load_decode_heap_oop(p);
|
||||||
|
|
||||||
if (_g1h->obj_in_cs(obj)) {
|
if (_g1h->is_in_cset_or_humongous(obj)) {
|
||||||
// If the referent object has been forwarded (either copied
|
// If the referent object has been forwarded (either copied
|
||||||
// to a new location or to itself in the event of an
|
// to a new location or to itself in the event of an
|
||||||
// evacuation failure) then we need to update the reference
|
// evacuation failure) then we need to update the reference
|
||||||
|
@ -6435,6 +6517,147 @@ void G1CollectedHeap::free_collection_set(HeapRegion* cs_head, EvacuationInfo& e
|
||||||
policy->phase_times()->record_non_young_free_cset_time_ms(non_young_time_ms);
|
policy->phase_times()->record_non_young_free_cset_time_ms(non_young_time_ms);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class G1FreeHumongousRegionClosure : public HeapRegionClosure {
|
||||||
|
private:
|
||||||
|
FreeRegionList* _free_region_list;
|
||||||
|
HeapRegionSet* _proxy_set;
|
||||||
|
HeapRegionSetCount _humongous_regions_removed;
|
||||||
|
size_t _freed_bytes;
|
||||||
|
public:
|
||||||
|
|
||||||
|
G1FreeHumongousRegionClosure(FreeRegionList* free_region_list) :
|
||||||
|
_free_region_list(free_region_list), _humongous_regions_removed(), _freed_bytes(0) {
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool doHeapRegion(HeapRegion* r) {
|
||||||
|
if (!r->startsHumongous()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
G1CollectedHeap* g1h = G1CollectedHeap::heap();
|
||||||
|
|
||||||
|
// The following checks whether the humongous object is live are sufficient.
|
||||||
|
// The main additional check (in addition to having a reference from the roots
|
||||||
|
// or the young gen) is whether the humongous object has a remembered set entry.
|
||||||
|
//
|
||||||
|
// A humongous object cannot be live if there is no remembered set for it
|
||||||
|
// because:
|
||||||
|
// - there can be no references from within humongous starts regions referencing
|
||||||
|
// the object because we never allocate other objects into them.
|
||||||
|
// (I.e. there are no intra-region references that may be missed by the
|
||||||
|
// remembered set)
|
||||||
|
// - as soon there is a remembered set entry to the humongous starts region
|
||||||
|
// (i.e. it has "escaped" to an old object) this remembered set entry will stay
|
||||||
|
// until the end of a concurrent mark.
|
||||||
|
//
|
||||||
|
// It is not required to check whether the object has been found dead by marking
|
||||||
|
// or not, in fact it would prevent reclamation within a concurrent cycle, as
|
||||||
|
// all objects allocated during that time are considered live.
|
||||||
|
// SATB marking is even more conservative than the remembered set.
|
||||||
|
// So if at this point in the collection there is no remembered set entry,
|
||||||
|
// nobody has a reference to it.
|
||||||
|
// At the start of collection we flush all refinement logs, and remembered sets
|
||||||
|
// are completely up-to-date wrt to references to the humongous object.
|
||||||
|
//
|
||||||
|
// Other implementation considerations:
|
||||||
|
// - never consider object arrays: while they are a valid target, they have not
|
||||||
|
// been observed to be used as temporary objects.
|
||||||
|
// - they would also pose considerable effort for cleaning up the the remembered
|
||||||
|
// sets.
|
||||||
|
// While this cleanup is not strictly necessary to be done (or done instantly),
|
||||||
|
// given that their occurrence is very low, this saves us this additional
|
||||||
|
// complexity.
|
||||||
|
uint region_idx = r->hrs_index();
|
||||||
|
if (g1h->humongous_is_live(region_idx) ||
|
||||||
|
g1h->humongous_region_is_always_live(region_idx)) {
|
||||||
|
|
||||||
|
if (G1TraceReclaimDeadHumongousObjectsAtYoungGC) {
|
||||||
|
gclog_or_tty->print_cr("Live humongous %d region %d with remset "SIZE_FORMAT" code roots "SIZE_FORMAT" is dead-bitmap %d live-other %d obj array %d",
|
||||||
|
r->isHumongous(),
|
||||||
|
region_idx,
|
||||||
|
r->rem_set()->occupied(),
|
||||||
|
r->rem_set()->strong_code_roots_list_length(),
|
||||||
|
g1h->mark_in_progress() && !g1h->g1_policy()->during_initial_mark_pause(),
|
||||||
|
g1h->humongous_is_live(region_idx),
|
||||||
|
oop(r->bottom())->is_objArray()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
guarantee(!((oop)(r->bottom()))->is_objArray(),
|
||||||
|
err_msg("Eagerly reclaiming object arrays is not supported, but the object "PTR_FORMAT" is.",
|
||||||
|
r->bottom()));
|
||||||
|
|
||||||
|
if (G1TraceReclaimDeadHumongousObjectsAtYoungGC) {
|
||||||
|
gclog_or_tty->print_cr("Reclaim humongous region %d start "PTR_FORMAT" region %d length "UINT32_FORMAT" with remset "SIZE_FORMAT" code roots "SIZE_FORMAT" is dead-bitmap %d live-other %d obj array %d",
|
||||||
|
r->isHumongous(),
|
||||||
|
r->bottom(),
|
||||||
|
region_idx,
|
||||||
|
r->region_num(),
|
||||||
|
r->rem_set()->occupied(),
|
||||||
|
r->rem_set()->strong_code_roots_list_length(),
|
||||||
|
g1h->mark_in_progress() && !g1h->g1_policy()->during_initial_mark_pause(),
|
||||||
|
g1h->humongous_is_live(region_idx),
|
||||||
|
oop(r->bottom())->is_objArray()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_freed_bytes += r->used();
|
||||||
|
r->set_containing_set(NULL);
|
||||||
|
_humongous_regions_removed.increment(1u, r->capacity());
|
||||||
|
g1h->free_humongous_region(r, _free_region_list, false);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
HeapRegionSetCount& humongous_free_count() {
|
||||||
|
return _humongous_regions_removed;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t bytes_freed() const {
|
||||||
|
return _freed_bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t humongous_reclaimed() const {
|
||||||
|
return _humongous_regions_removed.length();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void G1CollectedHeap::eagerly_reclaim_humongous_regions() {
|
||||||
|
assert_at_safepoint(true);
|
||||||
|
|
||||||
|
if (!G1ReclaimDeadHumongousObjectsAtYoungGC || !_has_humongous_reclaim_candidates) {
|
||||||
|
g1_policy()->phase_times()->record_fast_reclaim_humongous_time_ms(0.0, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
double start_time = os::elapsedTime();
|
||||||
|
|
||||||
|
FreeRegionList local_cleanup_list("Local Humongous Cleanup List");
|
||||||
|
|
||||||
|
G1FreeHumongousRegionClosure cl(&local_cleanup_list);
|
||||||
|
heap_region_iterate(&cl);
|
||||||
|
|
||||||
|
HeapRegionSetCount empty_set;
|
||||||
|
remove_from_old_sets(empty_set, cl.humongous_free_count());
|
||||||
|
|
||||||
|
G1HRPrinter* hr_printer = _g1h->hr_printer();
|
||||||
|
if (hr_printer->is_active()) {
|
||||||
|
FreeRegionListIterator iter(&local_cleanup_list);
|
||||||
|
while (iter.more_available()) {
|
||||||
|
HeapRegion* hr = iter.get_next();
|
||||||
|
hr_printer->cleanup(hr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
prepend_to_freelist(&local_cleanup_list);
|
||||||
|
decrement_summary_bytes(cl.bytes_freed());
|
||||||
|
|
||||||
|
g1_policy()->phase_times()->record_fast_reclaim_humongous_time_ms((os::elapsedTime() - start_time) * 1000.0,
|
||||||
|
cl.humongous_reclaimed());
|
||||||
|
}
|
||||||
|
|
||||||
// This routine is similar to the above but does not record
|
// This routine is similar to the above but does not record
|
||||||
// any policy statistics or update free lists; we are abandoning
|
// any policy statistics or update free lists; we are abandoning
|
||||||
// the current incremental collection set in preparation of a
|
// the current incremental collection set in preparation of a
|
||||||
|
|
|
@ -197,16 +197,6 @@ public:
|
||||||
bool do_object_b(oop p);
|
bool do_object_b(oop p);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Instances of this class are used for quick tests on whether a reference points
|
|
||||||
// into the collection set. Each of the array's elements denotes whether the
|
|
||||||
// corresponding region is in the collection set.
|
|
||||||
class G1FastCSetBiasedMappedArray : public G1BiasedMappedArray<bool> {
|
|
||||||
protected:
|
|
||||||
bool default_value() const { return false; }
|
|
||||||
public:
|
|
||||||
void clear() { G1BiasedMappedArray<bool>::clear(); }
|
|
||||||
};
|
|
||||||
|
|
||||||
class RefineCardTableEntryClosure;
|
class RefineCardTableEntryClosure;
|
||||||
|
|
||||||
class G1CollectedHeap : public SharedHeap {
|
class G1CollectedHeap : public SharedHeap {
|
||||||
|
@ -237,6 +227,7 @@ class G1CollectedHeap : public SharedHeap {
|
||||||
friend class EvacPopObjClosure;
|
friend class EvacPopObjClosure;
|
||||||
friend class G1ParCleanupCTTask;
|
friend class G1ParCleanupCTTask;
|
||||||
|
|
||||||
|
friend class G1FreeHumongousRegionClosure;
|
||||||
// Other related classes.
|
// Other related classes.
|
||||||
friend class G1MarkSweep;
|
friend class G1MarkSweep;
|
||||||
|
|
||||||
|
@ -267,6 +258,9 @@ private:
|
||||||
// It keeps track of the humongous regions.
|
// It keeps track of the humongous regions.
|
||||||
HeapRegionSet _humongous_set;
|
HeapRegionSet _humongous_set;
|
||||||
|
|
||||||
|
void clear_humongous_is_live_table();
|
||||||
|
void eagerly_reclaim_humongous_regions();
|
||||||
|
|
||||||
// The number of regions we could create by expansion.
|
// The number of regions we could create by expansion.
|
||||||
uint _expansion_regions;
|
uint _expansion_regions;
|
||||||
|
|
||||||
|
@ -367,10 +361,25 @@ private:
|
||||||
// than the current allocation region.
|
// than the current allocation region.
|
||||||
size_t _summary_bytes_used;
|
size_t _summary_bytes_used;
|
||||||
|
|
||||||
// This array is used for a quick test on whether a reference points into
|
// Records whether the region at the given index is kept live by roots or
|
||||||
// the collection set or not. Each of the array's elements denotes whether the
|
// references from the young generation.
|
||||||
// corresponding region is in the collection set or not.
|
class HumongousIsLiveBiasedMappedArray : public G1BiasedMappedArray<bool> {
|
||||||
G1FastCSetBiasedMappedArray _in_cset_fast_test;
|
protected:
|
||||||
|
bool default_value() const { return false; }
|
||||||
|
public:
|
||||||
|
void clear() { G1BiasedMappedArray<bool>::clear(); }
|
||||||
|
void set_live(uint region) {
|
||||||
|
set_by_index(region, true);
|
||||||
|
}
|
||||||
|
bool is_live(uint region) {
|
||||||
|
return get_by_index(region);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
HumongousIsLiveBiasedMappedArray _humongous_is_live;
|
||||||
|
// Stores whether during humongous object registration we found candidate regions.
|
||||||
|
// If not, we can skip a few steps.
|
||||||
|
bool _has_humongous_reclaim_candidates;
|
||||||
|
|
||||||
volatile unsigned _gc_time_stamp;
|
volatile unsigned _gc_time_stamp;
|
||||||
|
|
||||||
|
@ -690,10 +699,24 @@ public:
|
||||||
virtual void gc_prologue(bool full);
|
virtual void gc_prologue(bool full);
|
||||||
virtual void gc_epilogue(bool full);
|
virtual void gc_epilogue(bool full);
|
||||||
|
|
||||||
|
inline void set_humongous_is_live(oop obj);
|
||||||
|
|
||||||
|
bool humongous_is_live(uint region) {
|
||||||
|
return _humongous_is_live.is_live(region);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns whether the given region (which must be a humongous (start) region)
|
||||||
|
// is to be considered conservatively live regardless of any other conditions.
|
||||||
|
bool humongous_region_is_always_live(uint index);
|
||||||
|
// Register the given region to be part of the collection set.
|
||||||
|
inline void register_humongous_region_with_in_cset_fast_test(uint index);
|
||||||
|
// Register regions with humongous objects (actually on the start region) in
|
||||||
|
// the in_cset_fast_test table.
|
||||||
|
void register_humongous_regions_with_in_cset_fast_test();
|
||||||
// We register a region with the fast "in collection set" test. We
|
// We register a region with the fast "in collection set" test. We
|
||||||
// simply set to true the array slot corresponding to this region.
|
// simply set to true the array slot corresponding to this region.
|
||||||
void register_region_with_in_cset_fast_test(HeapRegion* r) {
|
void register_region_with_in_cset_fast_test(HeapRegion* r) {
|
||||||
_in_cset_fast_test.set_by_index(r->hrs_index(), true);
|
_in_cset_fast_test.set_in_cset(r->hrs_index());
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is a fast test on whether a reference points into the
|
// This is a fast test on whether a reference points into the
|
||||||
|
@ -1283,9 +1306,61 @@ public:
|
||||||
virtual bool is_in(const void* p) const;
|
virtual bool is_in(const void* p) const;
|
||||||
|
|
||||||
// Return "TRUE" iff the given object address is within the collection
|
// Return "TRUE" iff the given object address is within the collection
|
||||||
// set.
|
// set. Slow implementation.
|
||||||
inline bool obj_in_cs(oop obj);
|
inline bool obj_in_cs(oop obj);
|
||||||
|
|
||||||
|
inline bool is_in_cset(oop obj);
|
||||||
|
|
||||||
|
inline bool is_in_cset_or_humongous(const oop obj);
|
||||||
|
|
||||||
|
enum in_cset_state_t {
|
||||||
|
InNeither, // neither in collection set nor humongous
|
||||||
|
InCSet, // region is in collection set only
|
||||||
|
IsHumongous // region is a humongous start region
|
||||||
|
};
|
||||||
|
private:
|
||||||
|
// Instances of this class are used for quick tests on whether a reference points
|
||||||
|
// into the collection set or is a humongous object (points into a humongous
|
||||||
|
// object).
|
||||||
|
// Each of the array's elements denotes whether the corresponding region is in
|
||||||
|
// the collection set or a humongous region.
|
||||||
|
// We use this to quickly reclaim humongous objects: by making a humongous region
|
||||||
|
// succeed this test, we sort-of add it to the collection set. During the reference
|
||||||
|
// iteration closures, when we see a humongous region, we simply mark it as
|
||||||
|
// referenced, i.e. live.
|
||||||
|
class G1FastCSetBiasedMappedArray : public G1BiasedMappedArray<char> {
|
||||||
|
protected:
|
||||||
|
char default_value() const { return G1CollectedHeap::InNeither; }
|
||||||
|
public:
|
||||||
|
void set_humongous(uintptr_t index) {
|
||||||
|
assert(get_by_index(index) != InCSet, "Should not overwrite InCSet values");
|
||||||
|
set_by_index(index, G1CollectedHeap::IsHumongous);
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear_humongous(uintptr_t index) {
|
||||||
|
set_by_index(index, G1CollectedHeap::InNeither);
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_in_cset(uintptr_t index) {
|
||||||
|
assert(get_by_index(index) != G1CollectedHeap::IsHumongous, "Should not overwrite IsHumongous value");
|
||||||
|
set_by_index(index, G1CollectedHeap::InCSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_in_cset_or_humongous(HeapWord* addr) const { return get_by_address(addr) != G1CollectedHeap::InNeither; }
|
||||||
|
bool is_in_cset(HeapWord* addr) const { return get_by_address(addr) == G1CollectedHeap::InCSet; }
|
||||||
|
G1CollectedHeap::in_cset_state_t at(HeapWord* addr) const { return (G1CollectedHeap::in_cset_state_t)get_by_address(addr); }
|
||||||
|
void clear() { G1BiasedMappedArray<char>::clear(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
// This array is used for a quick test on whether a reference points into
|
||||||
|
// the collection set or not. Each of the array's elements denotes whether the
|
||||||
|
// corresponding region is in the collection set or not.
|
||||||
|
G1FastCSetBiasedMappedArray _in_cset_fast_test;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
inline in_cset_state_t in_cset_state(const oop obj);
|
||||||
|
|
||||||
// Return "TRUE" iff the given object address is in the reserved
|
// Return "TRUE" iff the given object address is in the reserved
|
||||||
// region of g1.
|
// region of g1.
|
||||||
bool is_in_g1_reserved(const void* p) const {
|
bool is_in_g1_reserved(const void* p) const {
|
||||||
|
@ -1340,6 +1415,10 @@ public:
|
||||||
// Return the region with the given index. It assumes the index is valid.
|
// Return the region with the given index. It assumes the index is valid.
|
||||||
inline HeapRegion* region_at(uint index) const;
|
inline HeapRegion* region_at(uint index) const;
|
||||||
|
|
||||||
|
// Calculate the region index of the given address. Given address must be
|
||||||
|
// within the heap.
|
||||||
|
inline uint addr_to_region(HeapWord* addr) const;
|
||||||
|
|
||||||
// Divide the heap region sequence into "chunks" of some size (the number
|
// Divide the heap region sequence into "chunks" of some size (the number
|
||||||
// of regions divided by the number of parallel threads times some
|
// of regions divided by the number of parallel threads times some
|
||||||
// overpartition factor, currently 4). Assumes that this will be called
|
// overpartition factor, currently 4). Assumes that this will be called
|
||||||
|
|
|
@ -40,6 +40,13 @@
|
||||||
// Return the region with the given index. It assumes the index is valid.
|
// Return the region with the given index. It assumes the index is valid.
|
||||||
inline HeapRegion* G1CollectedHeap::region_at(uint index) const { return _hrs.at(index); }
|
inline HeapRegion* G1CollectedHeap::region_at(uint index) const { return _hrs.at(index); }
|
||||||
|
|
||||||
|
inline uint G1CollectedHeap::addr_to_region(HeapWord* addr) const {
|
||||||
|
assert(is_in_reserved(addr),
|
||||||
|
err_msg("Cannot calculate region index for address "PTR_FORMAT" that is outside of the heap ["PTR_FORMAT", "PTR_FORMAT")",
|
||||||
|
p2i(addr), p2i(_reserved.start()), p2i(_reserved.end())));
|
||||||
|
return (uint)(pointer_delta(addr, _reserved.start(), sizeof(uint8_t)) >> HeapRegion::LogOfHRGrainBytes);
|
||||||
|
}
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
inline HeapRegion*
|
inline HeapRegion*
|
||||||
G1CollectedHeap::heap_region_containing_raw(const T addr) const {
|
G1CollectedHeap::heap_region_containing_raw(const T addr) const {
|
||||||
|
@ -172,12 +179,11 @@ inline bool G1CollectedHeap::isMarkedNext(oop obj) const {
|
||||||
return _cm->nextMarkBitMap()->isMarked((HeapWord *)obj);
|
return _cm->nextMarkBitMap()->isMarked((HeapWord *)obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// This is a fast test on whether a reference points into the
|
// This is a fast test on whether a reference points into the
|
||||||
// collection set or not. Assume that the reference
|
// collection set or not. Assume that the reference
|
||||||
// points into the heap.
|
// points into the heap.
|
||||||
inline bool G1CollectedHeap::in_cset_fast_test(oop obj) {
|
inline bool G1CollectedHeap::is_in_cset(oop obj) {
|
||||||
bool ret = _in_cset_fast_test.get_by_address((HeapWord*)obj);
|
bool ret = _in_cset_fast_test.is_in_cset((HeapWord*)obj);
|
||||||
// let's make sure the result is consistent with what the slower
|
// let's make sure the result is consistent with what the slower
|
||||||
// test returns
|
// test returns
|
||||||
assert( ret || !obj_in_cs(obj), "sanity");
|
assert( ret || !obj_in_cs(obj), "sanity");
|
||||||
|
@ -185,6 +191,18 @@ inline bool G1CollectedHeap::in_cset_fast_test(oop obj) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool G1CollectedHeap::is_in_cset_or_humongous(const oop obj) {
|
||||||
|
return _in_cset_fast_test.is_in_cset_or_humongous((HeapWord*)obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
G1CollectedHeap::in_cset_state_t G1CollectedHeap::in_cset_state(const oop obj) {
|
||||||
|
return _in_cset_fast_test.at((HeapWord*)obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
void G1CollectedHeap::register_humongous_region_with_in_cset_fast_test(uint index) {
|
||||||
|
_in_cset_fast_test.set_humongous(index);
|
||||||
|
}
|
||||||
|
|
||||||
#ifndef PRODUCT
|
#ifndef PRODUCT
|
||||||
// Support for G1EvacuationFailureALot
|
// Support for G1EvacuationFailureALot
|
||||||
|
|
||||||
|
@ -288,4 +306,22 @@ inline bool G1CollectedHeap::is_obj_ill(const oop obj) const {
|
||||||
return is_obj_ill(obj, heap_region_containing(obj));
|
return is_obj_ill(obj, heap_region_containing(obj));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void G1CollectedHeap::set_humongous_is_live(oop obj) {
|
||||||
|
uint region = addr_to_region((HeapWord*)obj);
|
||||||
|
// We not only set the "live" flag in the humongous_is_live table, but also
|
||||||
|
// reset the entry in the _in_cset_fast_test table so that subsequent references
|
||||||
|
// to the same humongous object do not go into the slow path again.
|
||||||
|
// This is racy, as multiple threads may at the same time enter here, but this
|
||||||
|
// is benign.
|
||||||
|
// During collection we only ever set the "live" flag, and only ever clear the
|
||||||
|
// entry in the in_cset_fast_table.
|
||||||
|
// We only ever evaluate the contents of these tables (in the VM thread) after
|
||||||
|
// having synchronized the worker threads with the VM thread, or in the same
|
||||||
|
// thread (i.e. within the VM thread).
|
||||||
|
if (!_humongous_is_live.is_live(region)) {
|
||||||
|
_humongous_is_live.set_live(region);
|
||||||
|
_in_cset_fast_test.clear_humongous(region);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#endif // SHARE_VM_GC_IMPLEMENTATION_G1_G1COLLECTEDHEAP_INLINE_HPP
|
#endif // SHARE_VM_GC_IMPLEMENTATION_G1_G1COLLECTEDHEAP_INLINE_HPP
|
||||||
|
|
|
@ -255,6 +255,10 @@ void G1GCPhaseTimes::print_stats(int level, const char* str, double value) {
|
||||||
LineBuffer(level).append_and_print_cr("[%s: %.1lf ms]", str, value);
|
LineBuffer(level).append_and_print_cr("[%s: %.1lf ms]", str, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void G1GCPhaseTimes::print_stats(int level, const char* str, size_t value) {
|
||||||
|
LineBuffer(level).append_and_print_cr("[%s: "SIZE_FORMAT"]", str, value);
|
||||||
|
}
|
||||||
|
|
||||||
void G1GCPhaseTimes::print_stats(int level, const char* str, double value, uint workers) {
|
void G1GCPhaseTimes::print_stats(int level, const char* str, double value, uint workers) {
|
||||||
LineBuffer(level).append_and_print_cr("[%s: %.1lf ms, GC Workers: %u]", str, value, workers);
|
LineBuffer(level).append_and_print_cr("[%s: %.1lf ms, GC Workers: %u]", str, value, workers);
|
||||||
}
|
}
|
||||||
|
@ -357,6 +361,14 @@ void G1GCPhaseTimes::print(double pause_time_sec) {
|
||||||
_last_redirty_logged_cards_processed_cards.print(3, "Redirtied Cards");
|
_last_redirty_logged_cards_processed_cards.print(3, "Redirtied Cards");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (G1ReclaimDeadHumongousObjectsAtYoungGC) {
|
||||||
|
print_stats(2, "Humongous Reclaim", _cur_fast_reclaim_humongous_time_ms);
|
||||||
|
if (G1Log::finest()) {
|
||||||
|
print_stats(3, "Humongous Total", _cur_fast_reclaim_humongous_total);
|
||||||
|
print_stats(3, "Humongous Candidate", _cur_fast_reclaim_humongous_candidates);
|
||||||
|
print_stats(3, "Humongous Reclaimed", _cur_fast_reclaim_humongous_reclaimed);
|
||||||
|
}
|
||||||
|
}
|
||||||
print_stats(2, "Free CSet",
|
print_stats(2, "Free CSet",
|
||||||
(_recorded_young_free_cset_time_ms +
|
(_recorded_young_free_cset_time_ms +
|
||||||
_recorded_non_young_free_cset_time_ms));
|
_recorded_non_young_free_cset_time_ms));
|
||||||
|
|
|
@ -157,11 +157,17 @@ class G1GCPhaseTimes : public CHeapObj<mtGC> {
|
||||||
double _recorded_young_free_cset_time_ms;
|
double _recorded_young_free_cset_time_ms;
|
||||||
double _recorded_non_young_free_cset_time_ms;
|
double _recorded_non_young_free_cset_time_ms;
|
||||||
|
|
||||||
|
double _cur_fast_reclaim_humongous_time_ms;
|
||||||
|
size_t _cur_fast_reclaim_humongous_total;
|
||||||
|
size_t _cur_fast_reclaim_humongous_candidates;
|
||||||
|
size_t _cur_fast_reclaim_humongous_reclaimed;
|
||||||
|
|
||||||
double _cur_verify_before_time_ms;
|
double _cur_verify_before_time_ms;
|
||||||
double _cur_verify_after_time_ms;
|
double _cur_verify_after_time_ms;
|
||||||
|
|
||||||
// Helper methods for detailed logging
|
// Helper methods for detailed logging
|
||||||
void print_stats(int level, const char* str, double value);
|
void print_stats(int level, const char* str, double value);
|
||||||
|
void print_stats(int level, const char* str, size_t value);
|
||||||
void print_stats(int level, const char* str, double value, uint workers);
|
void print_stats(int level, const char* str, double value, uint workers);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -282,6 +288,16 @@ class G1GCPhaseTimes : public CHeapObj<mtGC> {
|
||||||
_recorded_non_young_free_cset_time_ms = time_ms;
|
_recorded_non_young_free_cset_time_ms = time_ms;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void record_fast_reclaim_humongous_stats(size_t total, size_t candidates) {
|
||||||
|
_cur_fast_reclaim_humongous_total = total;
|
||||||
|
_cur_fast_reclaim_humongous_candidates = candidates;
|
||||||
|
}
|
||||||
|
|
||||||
|
void record_fast_reclaim_humongous_time_ms(double value, size_t reclaimed) {
|
||||||
|
_cur_fast_reclaim_humongous_time_ms = value;
|
||||||
|
_cur_fast_reclaim_humongous_reclaimed = reclaimed;
|
||||||
|
}
|
||||||
|
|
||||||
void record_young_cset_choice_time_ms(double time_ms) {
|
void record_young_cset_choice_time_ms(double time_ms) {
|
||||||
_recorded_young_cset_choice_time_ms = time_ms;
|
_recorded_young_cset_choice_time_ms = time_ms;
|
||||||
}
|
}
|
||||||
|
@ -348,6 +364,10 @@ class G1GCPhaseTimes : public CHeapObj<mtGC> {
|
||||||
return _recorded_non_young_free_cset_time_ms;
|
return _recorded_non_young_free_cset_time_ms;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double fast_reclaim_humongous_time_ms() {
|
||||||
|
return _cur_fast_reclaim_humongous_time_ms;
|
||||||
|
}
|
||||||
|
|
||||||
double average_last_update_rs_time() {
|
double average_last_update_rs_time() {
|
||||||
return _last_update_rs_times_ms.average();
|
return _last_update_rs_times_ms.average();
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,7 +44,7 @@ template <class T>
|
||||||
inline void FilterIntoCSClosure::do_oop_nv(T* p) {
|
inline void FilterIntoCSClosure::do_oop_nv(T* p) {
|
||||||
T heap_oop = oopDesc::load_heap_oop(p);
|
T heap_oop = oopDesc::load_heap_oop(p);
|
||||||
if (!oopDesc::is_null(heap_oop) &&
|
if (!oopDesc::is_null(heap_oop) &&
|
||||||
_g1->obj_in_cs(oopDesc::decode_heap_oop_not_null(heap_oop))) {
|
_g1->is_in_cset_or_humongous(oopDesc::decode_heap_oop_not_null(heap_oop))) {
|
||||||
_oc->do_oop(p);
|
_oc->do_oop(p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -67,7 +67,8 @@ inline void G1ParScanClosure::do_oop_nv(T* p) {
|
||||||
|
|
||||||
if (!oopDesc::is_null(heap_oop)) {
|
if (!oopDesc::is_null(heap_oop)) {
|
||||||
oop obj = oopDesc::decode_heap_oop_not_null(heap_oop);
|
oop obj = oopDesc::decode_heap_oop_not_null(heap_oop);
|
||||||
if (_g1->in_cset_fast_test(obj)) {
|
G1CollectedHeap::in_cset_state_t state = _g1->in_cset_state(obj);
|
||||||
|
if (state == G1CollectedHeap::InCSet) {
|
||||||
// We're not going to even bother checking whether the object is
|
// We're not going to even bother checking whether the object is
|
||||||
// already forwarded or not, as this usually causes an immediate
|
// already forwarded or not, as this usually causes an immediate
|
||||||
// stall. We'll try to prefetch the object (for write, given that
|
// stall. We'll try to prefetch the object (for write, given that
|
||||||
|
@ -86,6 +87,9 @@ inline void G1ParScanClosure::do_oop_nv(T* p) {
|
||||||
|
|
||||||
_par_scan_state->push_on_queue(p);
|
_par_scan_state->push_on_queue(p);
|
||||||
} else {
|
} else {
|
||||||
|
if (state == G1CollectedHeap::IsHumongous) {
|
||||||
|
_g1->set_humongous_is_live(obj);
|
||||||
|
}
|
||||||
_par_scan_state->update_rs(_from, p, _worker_id);
|
_par_scan_state->update_rs(_from, p, _worker_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -97,12 +101,14 @@ inline void G1ParPushHeapRSClosure::do_oop_nv(T* p) {
|
||||||
|
|
||||||
if (!oopDesc::is_null(heap_oop)) {
|
if (!oopDesc::is_null(heap_oop)) {
|
||||||
oop obj = oopDesc::decode_heap_oop_not_null(heap_oop);
|
oop obj = oopDesc::decode_heap_oop_not_null(heap_oop);
|
||||||
if (_g1->in_cset_fast_test(obj)) {
|
if (_g1->is_in_cset_or_humongous(obj)) {
|
||||||
Prefetch::write(obj->mark_addr(), 0);
|
Prefetch::write(obj->mark_addr(), 0);
|
||||||
Prefetch::read(obj->mark_addr(), (HeapWordSize*2));
|
Prefetch::read(obj->mark_addr(), (HeapWordSize*2));
|
||||||
|
|
||||||
// Place on the references queue
|
// Place on the references queue
|
||||||
_par_scan_state->push_on_queue(p);
|
_par_scan_state->push_on_queue(p);
|
||||||
|
} else {
|
||||||
|
assert(!_g1->obj_in_cs(obj), "checking");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,15 +52,20 @@ template <class T> void G1ParScanThreadState::do_oop_evac(T* p, HeapRegion* from
|
||||||
// set, due to (benign) races in the claim mechanism during RSet scanning more
|
// set, due to (benign) races in the claim mechanism during RSet scanning more
|
||||||
// than one thread might claim the same card. So the same card may be
|
// than one thread might claim the same card. So the same card may be
|
||||||
// processed multiple times. So redo this check.
|
// processed multiple times. So redo this check.
|
||||||
if (_g1h->in_cset_fast_test(obj)) {
|
G1CollectedHeap::in_cset_state_t in_cset_state = _g1h->in_cset_state(obj);
|
||||||
|
if (in_cset_state == G1CollectedHeap::InCSet) {
|
||||||
oop forwardee;
|
oop forwardee;
|
||||||
if (obj->is_forwarded()) {
|
if (obj->is_forwarded()) {
|
||||||
forwardee = obj->forwardee();
|
forwardee = obj->forwardee();
|
||||||
} else {
|
} else {
|
||||||
forwardee = copy_to_survivor_space(obj);
|
forwardee = copy_to_survivor_space(obj);
|
||||||
}
|
}
|
||||||
assert(forwardee != NULL, "forwardee should not be NULL");
|
|
||||||
oopDesc::encode_store_heap_oop(p, forwardee);
|
oopDesc::encode_store_heap_oop(p, forwardee);
|
||||||
|
} else if (in_cset_state == G1CollectedHeap::IsHumongous) {
|
||||||
|
_g1h->set_humongous_is_live(obj);
|
||||||
|
} else {
|
||||||
|
assert(in_cset_state == G1CollectedHeap::InNeither,
|
||||||
|
err_msg("In_cset_state must be InNeither here, but is %d", in_cset_state));
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(obj != NULL, "Must be");
|
assert(obj != NULL, "Must be");
|
||||||
|
|
|
@ -289,6 +289,13 @@
|
||||||
"The amount of code root chunks that should be kept at most " \
|
"The amount of code root chunks that should be kept at most " \
|
||||||
"as percentage of already allocated.") \
|
"as percentage of already allocated.") \
|
||||||
\
|
\
|
||||||
|
experimental(bool, G1ReclaimDeadHumongousObjectsAtYoungGC, true, \
|
||||||
|
"Try to reclaim dead large objects at every young GC.") \
|
||||||
|
\
|
||||||
|
experimental(bool, G1TraceReclaimDeadHumongousObjectsAtYoungGC, false, \
|
||||||
|
"Print some information about large object liveness " \
|
||||||
|
"at every young GC.") \
|
||||||
|
\
|
||||||
experimental(uintx, G1OldCSetRegionThresholdPercent, 10, \
|
experimental(uintx, G1OldCSetRegionThresholdPercent, 10, \
|
||||||
"An upper bound for the number of old CSet regions expressed " \
|
"An upper bound for the number of old CSet regions expressed " \
|
||||||
"as a percentage of the heap size.") \
|
"as a percentage of the heap size.") \
|
||||||
|
|
|
@ -695,6 +695,9 @@ void OtherRegionsTable::scrub(CardTableModRefBS* ctbs,
|
||||||
clear_fcc();
|
clear_fcc();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool OtherRegionsTable::is_empty() const {
|
||||||
|
return occ_sparse() == 0 && occ_coarse() == 0 && _first_all_fine_prts == NULL;
|
||||||
|
}
|
||||||
|
|
||||||
size_t OtherRegionsTable::occupied() const {
|
size_t OtherRegionsTable::occupied() const {
|
||||||
size_t sum = occ_fine();
|
size_t sum = occ_fine();
|
||||||
|
|
|
@ -185,6 +185,9 @@ public:
|
||||||
// objects.
|
// objects.
|
||||||
void scrub(CardTableModRefBS* ctbs, BitMap* region_bm, BitMap* card_bm);
|
void scrub(CardTableModRefBS* ctbs, BitMap* region_bm, BitMap* card_bm);
|
||||||
|
|
||||||
|
// Returns whether this remembered set (and all sub-sets) contain no entries.
|
||||||
|
bool is_empty() const;
|
||||||
|
|
||||||
size_t occupied() const;
|
size_t occupied() const;
|
||||||
size_t occ_fine() const;
|
size_t occ_fine() const;
|
||||||
size_t occ_coarse() const;
|
size_t occ_coarse() const;
|
||||||
|
@ -269,6 +272,10 @@ public:
|
||||||
return _other_regions.hr();
|
return _other_regions.hr();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool is_empty() const {
|
||||||
|
return (strong_code_roots_list_length() == 0) && _other_regions.is_empty();
|
||||||
|
}
|
||||||
|
|
||||||
size_t occupied() {
|
size_t occupied() {
|
||||||
MutexLockerEx x(&_m, Mutex::_no_safepoint_check_flag);
|
MutexLockerEx x(&_m, Mutex::_no_safepoint_check_flag);
|
||||||
return occupied_locked();
|
return occupied_locked();
|
||||||
|
@ -371,7 +378,7 @@ public:
|
||||||
void strong_code_roots_do(CodeBlobClosure* blk) const;
|
void strong_code_roots_do(CodeBlobClosure* blk) const;
|
||||||
|
|
||||||
// Returns the number of elements in the strong code roots list
|
// Returns the number of elements in the strong code roots list
|
||||||
size_t strong_code_roots_list_length() {
|
size_t strong_code_roots_list_length() const {
|
||||||
return _code_roots.length();
|
return _code_roots.length();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
98
hotspot/test/gc/g1/TestEagerReclaimHumongousRegions.java
Normal file
98
hotspot/test/gc/g1/TestEagerReclaimHumongousRegions.java
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2014, 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
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @test TestEagerReclaimHumongousRegions
|
||||||
|
* @bug 8027959
|
||||||
|
* @summary Test to make sure that eager reclaim of humongous objects work. We simply try to fill
|
||||||
|
* up the heap with humongous objects that should be eagerly reclaimable to avoid Full GC.
|
||||||
|
* @key gc
|
||||||
|
* @library /testlibrary
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
|
||||||
|
import com.oracle.java.testlibrary.OutputAnalyzer;
|
||||||
|
import com.oracle.java.testlibrary.ProcessTools;
|
||||||
|
import com.oracle.java.testlibrary.Asserts;
|
||||||
|
|
||||||
|
class ReclaimRegionFast {
|
||||||
|
public static final int M = 1024*1024;
|
||||||
|
|
||||||
|
public static LinkedList<Object> garbageList = new LinkedList<Object>();
|
||||||
|
|
||||||
|
public static void genGarbage() {
|
||||||
|
for (int i = 0; i < 32*1024; i++) {
|
||||||
|
garbageList.add(new int[100]);
|
||||||
|
}
|
||||||
|
garbageList.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// A large object referenced by a static.
|
||||||
|
static int[] filler = new int[10 * M];
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
|
||||||
|
int[] large = new int[M];
|
||||||
|
|
||||||
|
Object ref_from_stack = large;
|
||||||
|
|
||||||
|
for (int i = 0; i < 100; i++) {
|
||||||
|
// A large object that will be reclaimed eagerly.
|
||||||
|
large = new int[6*M];
|
||||||
|
genGarbage();
|
||||||
|
// Make sure that the compiler cannot completely remove
|
||||||
|
// the allocation of the large object until here.
|
||||||
|
System.out.println(large);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep the reference to the first object alive.
|
||||||
|
System.out.println(ref_from_stack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TestEagerReclaimHumongousRegions {
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(
|
||||||
|
"-XX:+UseG1GC",
|
||||||
|
"-Xms128M",
|
||||||
|
"-Xmx128M",
|
||||||
|
"-Xmn16M",
|
||||||
|
"-XX:+PrintGC",
|
||||||
|
ReclaimRegionFast.class.getName());
|
||||||
|
|
||||||
|
Pattern p = Pattern.compile("Full GC");
|
||||||
|
|
||||||
|
OutputAnalyzer output = new OutputAnalyzer(pb.start());
|
||||||
|
|
||||||
|
int found = 0;
|
||||||
|
Matcher m = p.matcher(output.getStdout());
|
||||||
|
while (m.find()) { found++; }
|
||||||
|
System.out.println("Issued " + found + " Full GCs");
|
||||||
|
Asserts.assertLT(found, 10, "Found that " + found + " Full GCs were issued. This is larger than the bound. Eager reclaim seems to not work at all");
|
||||||
|
|
||||||
|
output.shouldHaveExitValue(0);
|
||||||
|
}
|
||||||
|
}
|
|
@ -23,7 +23,7 @@
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @test TestGCLogMessages
|
* @test TestGCLogMessages
|
||||||
* @bug 8035406 8027295 8035398 8019342
|
* @bug 8035406 8027295 8035398 8019342 8027959
|
||||||
* @summary Ensure that the PrintGCDetails output for a minor GC with G1
|
* @summary Ensure that the PrintGCDetails output for a minor GC with G1
|
||||||
* includes the expected necessary messages.
|
* includes the expected necessary messages.
|
||||||
* @key gc
|
* @key gc
|
||||||
|
@ -54,6 +54,7 @@ public class TestGCLogMessages {
|
||||||
output.shouldNotContain("[String Dedup Fixup");
|
output.shouldNotContain("[String Dedup Fixup");
|
||||||
output.shouldNotContain("[Young Free CSet");
|
output.shouldNotContain("[Young Free CSet");
|
||||||
output.shouldNotContain("[Non-Young Free CSet");
|
output.shouldNotContain("[Non-Young Free CSet");
|
||||||
|
output.shouldNotContain("[Humongous Reclaim");
|
||||||
output.shouldHaveExitValue(0);
|
output.shouldHaveExitValue(0);
|
||||||
|
|
||||||
pb = ProcessTools.createJavaProcessBuilder("-XX:+UseG1GC",
|
pb = ProcessTools.createJavaProcessBuilder("-XX:+UseG1GC",
|
||||||
|
@ -71,6 +72,10 @@ public class TestGCLogMessages {
|
||||||
output.shouldContain("[String Dedup Fixup");
|
output.shouldContain("[String Dedup Fixup");
|
||||||
output.shouldNotContain("[Young Free CSet");
|
output.shouldNotContain("[Young Free CSet");
|
||||||
output.shouldNotContain("[Non-Young Free CSet");
|
output.shouldNotContain("[Non-Young Free CSet");
|
||||||
|
output.shouldContain("[Humongous Reclaim");
|
||||||
|
output.shouldNotContain("[Humongous Total");
|
||||||
|
output.shouldNotContain("[Humongous Candidate");
|
||||||
|
output.shouldNotContain("[Humongous Reclaimed");
|
||||||
output.shouldHaveExitValue(0);
|
output.shouldHaveExitValue(0);
|
||||||
|
|
||||||
pb = ProcessTools.createJavaProcessBuilder("-XX:+UseG1GC",
|
pb = ProcessTools.createJavaProcessBuilder("-XX:+UseG1GC",
|
||||||
|
@ -90,6 +95,10 @@ public class TestGCLogMessages {
|
||||||
output.shouldContain("[String Dedup Fixup");
|
output.shouldContain("[String Dedup Fixup");
|
||||||
output.shouldContain("[Young Free CSet");
|
output.shouldContain("[Young Free CSet");
|
||||||
output.shouldContain("[Non-Young Free CSet");
|
output.shouldContain("[Non-Young Free CSet");
|
||||||
|
output.shouldContain("[Humongous Reclaim");
|
||||||
|
output.shouldContain("[Humongous Total");
|
||||||
|
output.shouldContain("[Humongous Candidate");
|
||||||
|
output.shouldContain("[Humongous Reclaimed");
|
||||||
output.shouldHaveExitValue(0);
|
output.shouldHaveExitValue(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue