Wed, 23 Jul 2014 09:03:32 +0200
8027959: Early reclamation of large objects in G1
Summary: Try to reclaim humongous objects at every young collection after doing a conservative estimate of its liveness.
Reviewed-by: brutisso, mgerdin
1.1 --- a/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp Mon Jul 21 10:00:31 2014 +0200 1.2 +++ b/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp Wed Jul 23 09:03:32 2014 +0200 1.3 @@ -1928,6 +1928,8 @@ 1.4 _secondary_free_list("Secondary Free List", new SecondaryFreeRegionListMtSafeChecker()), 1.5 _old_set("Old Set", false /* humongous */, new OldRegionSetMtSafeChecker()), 1.6 _humongous_set("Master Humongous Set", true /* humongous */, new HumongousRegionSetMtSafeChecker()), 1.7 + _humongous_is_live(), 1.8 + _has_humongous_reclaim_candidates(false), 1.9 _free_regions_coming(false), 1.10 _young_list(new YoungList(this)), 1.11 _gc_time_stamp(0), 1.12 @@ -2084,6 +2086,7 @@ 1.13 _g1h = this; 1.14 1.15 _in_cset_fast_test.initialize(_g1_reserved.start(), _g1_reserved.end(), HeapRegion::GrainBytes); 1.16 + _humongous_is_live.initialize(_g1_reserved.start(), _g1_reserved.end(), HeapRegion::GrainBytes); 1.17 1.18 // Create the ConcurrentMark data structure and thread. 1.19 // (Must do this late, so that "max_regions" is defined.) 1.20 @@ -2179,6 +2182,11 @@ 1.21 } 1.22 } 1.23 1.24 +void G1CollectedHeap::clear_humongous_is_live_table() { 1.25 + guarantee(G1ReclaimDeadHumongousObjectsAtYoungGC, "Should only be called if true"); 1.26 + _humongous_is_live.clear(); 1.27 +} 1.28 + 1.29 size_t G1CollectedHeap::conservative_max_heap_alignment() { 1.30 return HeapRegion::max_region_size(); 1.31 } 1.32 @@ -3797,6 +3805,61 @@ 1.33 return g1_rem_set()->cardsScanned(); 1.34 } 1.35 1.36 +bool G1CollectedHeap::humongous_region_is_always_live(uint index) { 1.37 + HeapRegion* region = region_at(index); 1.38 + assert(region->startsHumongous(), "Must start a humongous object"); 1.39 + return oop(region->bottom())->is_objArray() || !region->rem_set()->is_empty(); 1.40 +} 1.41 + 1.42 +class RegisterHumongousWithInCSetFastTestClosure : public HeapRegionClosure { 1.43 + private: 1.44 + size_t _total_humongous; 1.45 + size_t _candidate_humongous; 1.46 + public: 1.47 + RegisterHumongousWithInCSetFastTestClosure() : _total_humongous(0), _candidate_humongous(0) { 1.48 + } 1.49 + 1.50 + virtual bool doHeapRegion(HeapRegion* r) { 1.51 + if (!r->startsHumongous()) { 1.52 + return false; 1.53 + } 1.54 + G1CollectedHeap* g1h = G1CollectedHeap::heap(); 1.55 + 1.56 + uint region_idx = r->hrs_index(); 1.57 + bool is_candidate = !g1h->humongous_region_is_always_live(region_idx); 1.58 + // Is_candidate already filters out humongous regions with some remembered set. 1.59 + // This will not lead to humongous object that we mistakenly keep alive because 1.60 + // during young collection the remembered sets will only be added to. 1.61 + if (is_candidate) { 1.62 + g1h->register_humongous_region_with_in_cset_fast_test(region_idx); 1.63 + _candidate_humongous++; 1.64 + } 1.65 + _total_humongous++; 1.66 + 1.67 + return false; 1.68 + } 1.69 + 1.70 + size_t total_humongous() const { return _total_humongous; } 1.71 + size_t candidate_humongous() const { return _candidate_humongous; } 1.72 +}; 1.73 + 1.74 +void G1CollectedHeap::register_humongous_regions_with_in_cset_fast_test() { 1.75 + if (!G1ReclaimDeadHumongousObjectsAtYoungGC) { 1.76 + g1_policy()->phase_times()->record_fast_reclaim_humongous_stats(0, 0); 1.77 + return; 1.78 + } 1.79 + 1.80 + RegisterHumongousWithInCSetFastTestClosure cl; 1.81 + heap_region_iterate(&cl); 1.82 + g1_policy()->phase_times()->record_fast_reclaim_humongous_stats(cl.total_humongous(), 1.83 + cl.candidate_humongous()); 1.84 + _has_humongous_reclaim_candidates = cl.candidate_humongous() > 0; 1.85 + 1.86 + if (_has_humongous_reclaim_candidates) { 1.87 + clear_humongous_is_live_table(); 1.88 + } 1.89 +} 1.90 + 1.91 void 1.92 G1CollectedHeap::setup_surviving_young_words() { 1.93 assert(_surviving_young_words == NULL, "pre-condition"); 1.94 @@ -4083,6 +4146,8 @@ 1.95 1.96 g1_policy()->finalize_cset(target_pause_time_ms, evacuation_info); 1.97 1.98 + register_humongous_regions_with_in_cset_fast_test(); 1.99 + 1.100 _cm->note_start_of_gc(); 1.101 // We should not verify the per-thread SATB buffers given that 1.102 // we have not filtered them yet (we'll do so during the 1.103 @@ -4133,6 +4198,9 @@ 1.104 true /* verify_fingers */); 1.105 1.106 free_collection_set(g1_policy()->collection_set(), evacuation_info); 1.107 + 1.108 + eagerly_reclaim_humongous_regions(); 1.109 + 1.110 g1_policy()->clear_collection_set(); 1.111 1.112 cleanup_surviving_young_words(); 1.113 @@ -4644,7 +4712,9 @@ 1.114 1.115 assert(_worker_id == _par_scan_state->queue_num(), "sanity"); 1.116 1.117 - if (_g1->in_cset_fast_test(obj)) { 1.118 + G1CollectedHeap::in_cset_state_t state = _g1->in_cset_state(obj); 1.119 + 1.120 + if (state == G1CollectedHeap::InCSet) { 1.121 oop forwardee; 1.122 if (obj->is_forwarded()) { 1.123 forwardee = obj->forwardee(); 1.124 @@ -4663,6 +4733,9 @@ 1.125 do_klass_barrier(p, forwardee); 1.126 } 1.127 } else { 1.128 + if (state == G1CollectedHeap::IsHumongous) { 1.129 + _g1->set_humongous_is_live(obj); 1.130 + } 1.131 // The object is not in collection set. If we're a root scanning 1.132 // closure during an initial mark pause then attempt to mark the object. 1.133 if (do_mark_object == G1MarkFromRoot) { 1.134 @@ -5492,12 +5565,21 @@ 1.135 public: 1.136 G1KeepAliveClosure(G1CollectedHeap* g1) : _g1(g1) {} 1.137 void do_oop(narrowOop* p) { guarantee(false, "Not needed"); } 1.138 - void do_oop( oop* p) { 1.139 + void do_oop(oop* p) { 1.140 oop obj = *p; 1.141 1.142 - if (_g1->obj_in_cs(obj)) { 1.143 + G1CollectedHeap::in_cset_state_t cset_state = _g1->in_cset_state(obj); 1.144 + if (obj == NULL || cset_state == G1CollectedHeap::InNeither) { 1.145 + return; 1.146 + } 1.147 + if (cset_state == G1CollectedHeap::InCSet) { 1.148 assert( obj->is_forwarded(), "invariant" ); 1.149 *p = obj->forwardee(); 1.150 + } else { 1.151 + assert(!obj->is_forwarded(), "invariant" ); 1.152 + assert(cset_state == G1CollectedHeap::IsHumongous, 1.153 + err_msg("Only allowed InCSet state is IsHumongous, but is %d", cset_state)); 1.154 + _g1->set_humongous_is_live(obj); 1.155 } 1.156 } 1.157 }; 1.158 @@ -5527,7 +5609,7 @@ 1.159 template <class T> void do_oop_work(T* p) { 1.160 oop obj = oopDesc::load_decode_heap_oop(p); 1.161 1.162 - if (_g1h->obj_in_cs(obj)) { 1.163 + if (_g1h->is_in_cset_or_humongous(obj)) { 1.164 // If the referent object has been forwarded (either copied 1.165 // to a new location or to itself in the event of an 1.166 // evacuation failure) then we need to update the reference 1.167 @@ -5552,10 +5634,10 @@ 1.168 assert(!Metaspace::contains((const void*)p), 1.169 err_msg("Unexpectedly found a pointer from metadata: " 1.170 PTR_FORMAT, p)); 1.171 - _copy_non_heap_obj_cl->do_oop(p); 1.172 - } 1.173 + _copy_non_heap_obj_cl->do_oop(p); 1.174 } 1.175 } 1.176 + } 1.177 }; 1.178 1.179 // Serial drain queue closure. Called as the 'complete_gc' 1.180 @@ -6477,6 +6559,147 @@ 1.181 policy->phase_times()->record_non_young_free_cset_time_ms(non_young_time_ms); 1.182 } 1.183 1.184 +class G1FreeHumongousRegionClosure : public HeapRegionClosure { 1.185 + private: 1.186 + FreeRegionList* _free_region_list; 1.187 + HeapRegionSet* _proxy_set; 1.188 + HeapRegionSetCount _humongous_regions_removed; 1.189 + size_t _freed_bytes; 1.190 + public: 1.191 + 1.192 + G1FreeHumongousRegionClosure(FreeRegionList* free_region_list) : 1.193 + _free_region_list(free_region_list), _humongous_regions_removed(), _freed_bytes(0) { 1.194 + } 1.195 + 1.196 + virtual bool doHeapRegion(HeapRegion* r) { 1.197 + if (!r->startsHumongous()) { 1.198 + return false; 1.199 + } 1.200 + 1.201 + G1CollectedHeap* g1h = G1CollectedHeap::heap(); 1.202 + 1.203 + // The following checks whether the humongous object is live are sufficient. 1.204 + // The main additional check (in addition to having a reference from the roots 1.205 + // or the young gen) is whether the humongous object has a remembered set entry. 1.206 + // 1.207 + // A humongous object cannot be live if there is no remembered set for it 1.208 + // because: 1.209 + // - there can be no references from within humongous starts regions referencing 1.210 + // the object because we never allocate other objects into them. 1.211 + // (I.e. there are no intra-region references that may be missed by the 1.212 + // remembered set) 1.213 + // - as soon there is a remembered set entry to the humongous starts region 1.214 + // (i.e. it has "escaped" to an old object) this remembered set entry will stay 1.215 + // until the end of a concurrent mark. 1.216 + // 1.217 + // It is not required to check whether the object has been found dead by marking 1.218 + // or not, in fact it would prevent reclamation within a concurrent cycle, as 1.219 + // all objects allocated during that time are considered live. 1.220 + // SATB marking is even more conservative than the remembered set. 1.221 + // So if at this point in the collection there is no remembered set entry, 1.222 + // nobody has a reference to it. 1.223 + // At the start of collection we flush all refinement logs, and remembered sets 1.224 + // are completely up-to-date wrt to references to the humongous object. 1.225 + // 1.226 + // Other implementation considerations: 1.227 + // - never consider object arrays: while they are a valid target, they have not 1.228 + // been observed to be used as temporary objects. 1.229 + // - they would also pose considerable effort for cleaning up the the remembered 1.230 + // sets. 1.231 + // While this cleanup is not strictly necessary to be done (or done instantly), 1.232 + // given that their occurrence is very low, this saves us this additional 1.233 + // complexity. 1.234 + uint region_idx = r->hrs_index(); 1.235 + if (g1h->humongous_is_live(region_idx) || 1.236 + g1h->humongous_region_is_always_live(region_idx)) { 1.237 + 1.238 + if (G1TraceReclaimDeadHumongousObjectsAtYoungGC) { 1.239 + 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", 1.240 + r->isHumongous(), 1.241 + region_idx, 1.242 + r->rem_set()->occupied(), 1.243 + r->rem_set()->strong_code_roots_list_length(), 1.244 + g1h->mark_in_progress() && !g1h->g1_policy()->during_initial_mark_pause(), 1.245 + g1h->humongous_is_live(region_idx), 1.246 + oop(r->bottom())->is_objArray() 1.247 + ); 1.248 + } 1.249 + 1.250 + return false; 1.251 + } 1.252 + 1.253 + guarantee(!((oop)(r->bottom()))->is_objArray(), 1.254 + err_msg("Eagerly reclaiming object arrays is not supported, but the object "PTR_FORMAT" is.", 1.255 + r->bottom())); 1.256 + 1.257 + if (G1TraceReclaimDeadHumongousObjectsAtYoungGC) { 1.258 + 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", 1.259 + r->isHumongous(), 1.260 + r->bottom(), 1.261 + region_idx, 1.262 + r->region_num(), 1.263 + r->rem_set()->occupied(), 1.264 + r->rem_set()->strong_code_roots_list_length(), 1.265 + g1h->mark_in_progress() && !g1h->g1_policy()->during_initial_mark_pause(), 1.266 + g1h->humongous_is_live(region_idx), 1.267 + oop(r->bottom())->is_objArray() 1.268 + ); 1.269 + } 1.270 + _freed_bytes += r->used(); 1.271 + r->set_containing_set(NULL); 1.272 + _humongous_regions_removed.increment(1u, r->capacity()); 1.273 + g1h->free_humongous_region(r, _free_region_list, false); 1.274 + 1.275 + return false; 1.276 + } 1.277 + 1.278 + HeapRegionSetCount& humongous_free_count() { 1.279 + return _humongous_regions_removed; 1.280 + } 1.281 + 1.282 + size_t bytes_freed() const { 1.283 + return _freed_bytes; 1.284 + } 1.285 + 1.286 + size_t humongous_reclaimed() const { 1.287 + return _humongous_regions_removed.length(); 1.288 + } 1.289 +}; 1.290 + 1.291 +void G1CollectedHeap::eagerly_reclaim_humongous_regions() { 1.292 + assert_at_safepoint(true); 1.293 + 1.294 + if (!G1ReclaimDeadHumongousObjectsAtYoungGC || !_has_humongous_reclaim_candidates) { 1.295 + g1_policy()->phase_times()->record_fast_reclaim_humongous_time_ms(0.0, 0); 1.296 + return; 1.297 + } 1.298 + 1.299 + double start_time = os::elapsedTime(); 1.300 + 1.301 + FreeRegionList local_cleanup_list("Local Humongous Cleanup List"); 1.302 + 1.303 + G1FreeHumongousRegionClosure cl(&local_cleanup_list); 1.304 + heap_region_iterate(&cl); 1.305 + 1.306 + HeapRegionSetCount empty_set; 1.307 + remove_from_old_sets(empty_set, cl.humongous_free_count()); 1.308 + 1.309 + G1HRPrinter* hr_printer = _g1h->hr_printer(); 1.310 + if (hr_printer->is_active()) { 1.311 + FreeRegionListIterator iter(&local_cleanup_list); 1.312 + while (iter.more_available()) { 1.313 + HeapRegion* hr = iter.get_next(); 1.314 + hr_printer->cleanup(hr); 1.315 + } 1.316 + } 1.317 + 1.318 + prepend_to_freelist(&local_cleanup_list); 1.319 + decrement_summary_bytes(cl.bytes_freed()); 1.320 + 1.321 + g1_policy()->phase_times()->record_fast_reclaim_humongous_time_ms((os::elapsedTime() - start_time) * 1000.0, 1.322 + cl.humongous_reclaimed()); 1.323 +} 1.324 + 1.325 // This routine is similar to the above but does not record 1.326 // any policy statistics or update free lists; we are abandoning 1.327 // the current incremental collection set in preparation of a
2.1 --- a/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp Mon Jul 21 10:00:31 2014 +0200 2.2 +++ b/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp Wed Jul 23 09:03:32 2014 +0200 2.3 @@ -197,16 +197,6 @@ 2.4 bool do_object_b(oop p); 2.5 }; 2.6 2.7 -// Instances of this class are used for quick tests on whether a reference points 2.8 -// into the collection set. Each of the array's elements denotes whether the 2.9 -// corresponding region is in the collection set. 2.10 -class G1FastCSetBiasedMappedArray : public G1BiasedMappedArray<bool> { 2.11 - protected: 2.12 - bool default_value() const { return false; } 2.13 - public: 2.14 - void clear() { G1BiasedMappedArray<bool>::clear(); } 2.15 -}; 2.16 - 2.17 class RefineCardTableEntryClosure; 2.18 2.19 class G1CollectedHeap : public SharedHeap { 2.20 @@ -237,6 +227,7 @@ 2.21 friend class EvacPopObjClosure; 2.22 friend class G1ParCleanupCTTask; 2.23 2.24 + friend class G1FreeHumongousRegionClosure; 2.25 // Other related classes. 2.26 friend class G1MarkSweep; 2.27 2.28 @@ -267,6 +258,9 @@ 2.29 // It keeps track of the humongous regions. 2.30 HeapRegionSet _humongous_set; 2.31 2.32 + void clear_humongous_is_live_table(); 2.33 + void eagerly_reclaim_humongous_regions(); 2.34 + 2.35 // The number of regions we could create by expansion. 2.36 uint _expansion_regions; 2.37 2.38 @@ -367,10 +361,25 @@ 2.39 // than the current allocation region. 2.40 size_t _summary_bytes_used; 2.41 2.42 - // This array is used for a quick test on whether a reference points into 2.43 - // the collection set or not. Each of the array's elements denotes whether the 2.44 - // corresponding region is in the collection set or not. 2.45 - G1FastCSetBiasedMappedArray _in_cset_fast_test; 2.46 + // Records whether the region at the given index is kept live by roots or 2.47 + // references from the young generation. 2.48 + class HumongousIsLiveBiasedMappedArray : public G1BiasedMappedArray<bool> { 2.49 + protected: 2.50 + bool default_value() const { return false; } 2.51 + public: 2.52 + void clear() { G1BiasedMappedArray<bool>::clear(); } 2.53 + void set_live(uint region) { 2.54 + set_by_index(region, true); 2.55 + } 2.56 + bool is_live(uint region) { 2.57 + return get_by_index(region); 2.58 + } 2.59 + }; 2.60 + 2.61 + HumongousIsLiveBiasedMappedArray _humongous_is_live; 2.62 + // Stores whether during humongous object registration we found candidate regions. 2.63 + // If not, we can skip a few steps. 2.64 + bool _has_humongous_reclaim_candidates; 2.65 2.66 volatile unsigned _gc_time_stamp; 2.67 2.68 @@ -690,10 +699,24 @@ 2.69 virtual void gc_prologue(bool full); 2.70 virtual void gc_epilogue(bool full); 2.71 2.72 + inline void set_humongous_is_live(oop obj); 2.73 + 2.74 + bool humongous_is_live(uint region) { 2.75 + return _humongous_is_live.is_live(region); 2.76 + } 2.77 + 2.78 + // Returns whether the given region (which must be a humongous (start) region) 2.79 + // is to be considered conservatively live regardless of any other conditions. 2.80 + bool humongous_region_is_always_live(uint index); 2.81 + // Register the given region to be part of the collection set. 2.82 + inline void register_humongous_region_with_in_cset_fast_test(uint index); 2.83 + // Register regions with humongous objects (actually on the start region) in 2.84 + // the in_cset_fast_test table. 2.85 + void register_humongous_regions_with_in_cset_fast_test(); 2.86 // We register a region with the fast "in collection set" test. We 2.87 // simply set to true the array slot corresponding to this region. 2.88 void register_region_with_in_cset_fast_test(HeapRegion* r) { 2.89 - _in_cset_fast_test.set_by_index(r->hrs_index(), true); 2.90 + _in_cset_fast_test.set_in_cset(r->hrs_index()); 2.91 } 2.92 2.93 // This is a fast test on whether a reference points into the 2.94 @@ -1292,9 +1315,61 @@ 2.95 virtual bool is_in(const void* p) const; 2.96 2.97 // Return "TRUE" iff the given object address is within the collection 2.98 - // set. 2.99 + // set. Slow implementation. 2.100 inline bool obj_in_cs(oop obj); 2.101 2.102 + inline bool is_in_cset(oop obj); 2.103 + 2.104 + inline bool is_in_cset_or_humongous(const oop obj); 2.105 + 2.106 + enum in_cset_state_t { 2.107 + InNeither, // neither in collection set nor humongous 2.108 + InCSet, // region is in collection set only 2.109 + IsHumongous // region is a humongous start region 2.110 + }; 2.111 + private: 2.112 + // Instances of this class are used for quick tests on whether a reference points 2.113 + // into the collection set or is a humongous object (points into a humongous 2.114 + // object). 2.115 + // Each of the array's elements denotes whether the corresponding region is in 2.116 + // the collection set or a humongous region. 2.117 + // We use this to quickly reclaim humongous objects: by making a humongous region 2.118 + // succeed this test, we sort-of add it to the collection set. During the reference 2.119 + // iteration closures, when we see a humongous region, we simply mark it as 2.120 + // referenced, i.e. live. 2.121 + class G1FastCSetBiasedMappedArray : public G1BiasedMappedArray<char> { 2.122 + protected: 2.123 + char default_value() const { return G1CollectedHeap::InNeither; } 2.124 + public: 2.125 + void set_humongous(uintptr_t index) { 2.126 + assert(get_by_index(index) != InCSet, "Should not overwrite InCSet values"); 2.127 + set_by_index(index, G1CollectedHeap::IsHumongous); 2.128 + } 2.129 + 2.130 + void clear_humongous(uintptr_t index) { 2.131 + set_by_index(index, G1CollectedHeap::InNeither); 2.132 + } 2.133 + 2.134 + void set_in_cset(uintptr_t index) { 2.135 + assert(get_by_index(index) != G1CollectedHeap::IsHumongous, "Should not overwrite IsHumongous value"); 2.136 + set_by_index(index, G1CollectedHeap::InCSet); 2.137 + } 2.138 + 2.139 + bool is_in_cset_or_humongous(HeapWord* addr) const { return get_by_address(addr) != G1CollectedHeap::InNeither; } 2.140 + bool is_in_cset(HeapWord* addr) const { return get_by_address(addr) == G1CollectedHeap::InCSet; } 2.141 + G1CollectedHeap::in_cset_state_t at(HeapWord* addr) const { return (G1CollectedHeap::in_cset_state_t)get_by_address(addr); } 2.142 + void clear() { G1BiasedMappedArray<char>::clear(); } 2.143 + }; 2.144 + 2.145 + // This array is used for a quick test on whether a reference points into 2.146 + // the collection set or not. Each of the array's elements denotes whether the 2.147 + // corresponding region is in the collection set or not. 2.148 + G1FastCSetBiasedMappedArray _in_cset_fast_test; 2.149 + 2.150 + public: 2.151 + 2.152 + inline in_cset_state_t in_cset_state(const oop obj); 2.153 + 2.154 // Return "TRUE" iff the given object address is in the reserved 2.155 // region of g1. 2.156 bool is_in_g1_reserved(const void* p) const { 2.157 @@ -1349,6 +1424,10 @@ 2.158 // Return the region with the given index. It assumes the index is valid. 2.159 inline HeapRegion* region_at(uint index) const; 2.160 2.161 + // Calculate the region index of the given address. Given address must be 2.162 + // within the heap. 2.163 + inline uint addr_to_region(HeapWord* addr) const; 2.164 + 2.165 // Divide the heap region sequence into "chunks" of some size (the number 2.166 // of regions divided by the number of parallel threads times some 2.167 // overpartition factor, currently 4). Assumes that this will be called
3.1 --- a/src/share/vm/gc_implementation/g1/g1CollectedHeap.inline.hpp Mon Jul 21 10:00:31 2014 +0200 3.2 +++ b/src/share/vm/gc_implementation/g1/g1CollectedHeap.inline.hpp Wed Jul 23 09:03:32 2014 +0200 3.3 @@ -40,6 +40,13 @@ 3.4 // Return the region with the given index. It assumes the index is valid. 3.5 inline HeapRegion* G1CollectedHeap::region_at(uint index) const { return _hrs.at(index); } 3.6 3.7 +inline uint G1CollectedHeap::addr_to_region(HeapWord* addr) const { 3.8 + assert(is_in_reserved(addr), 3.9 + err_msg("Cannot calculate region index for address "PTR_FORMAT" that is outside of the heap ["PTR_FORMAT", "PTR_FORMAT")", 3.10 + p2i(addr), p2i(_reserved.start()), p2i(_reserved.end()))); 3.11 + return (uint)(pointer_delta(addr, _reserved.start(), sizeof(uint8_t)) >> HeapRegion::LogOfHRGrainBytes); 3.12 +} 3.13 + 3.14 template <class T> 3.15 inline HeapRegion* 3.16 G1CollectedHeap::heap_region_containing(const T addr) const { 3.17 @@ -172,12 +179,11 @@ 3.18 return _cm->nextMarkBitMap()->isMarked((HeapWord *)obj); 3.19 } 3.20 3.21 - 3.22 // This is a fast test on whether a reference points into the 3.23 // collection set or not. Assume that the reference 3.24 // points into the heap. 3.25 -inline bool G1CollectedHeap::in_cset_fast_test(oop obj) { 3.26 - bool ret = _in_cset_fast_test.get_by_address((HeapWord*)obj); 3.27 +inline bool G1CollectedHeap::is_in_cset(oop obj) { 3.28 + bool ret = _in_cset_fast_test.is_in_cset((HeapWord*)obj); 3.29 // let's make sure the result is consistent with what the slower 3.30 // test returns 3.31 assert( ret || !obj_in_cs(obj), "sanity"); 3.32 @@ -185,6 +191,18 @@ 3.33 return ret; 3.34 } 3.35 3.36 +bool G1CollectedHeap::is_in_cset_or_humongous(const oop obj) { 3.37 + return _in_cset_fast_test.is_in_cset_or_humongous((HeapWord*)obj); 3.38 +} 3.39 + 3.40 +G1CollectedHeap::in_cset_state_t G1CollectedHeap::in_cset_state(const oop obj) { 3.41 + return _in_cset_fast_test.at((HeapWord*)obj); 3.42 +} 3.43 + 3.44 +void G1CollectedHeap::register_humongous_region_with_in_cset_fast_test(uint index) { 3.45 + _in_cset_fast_test.set_humongous(index); 3.46 +} 3.47 + 3.48 #ifndef PRODUCT 3.49 // Support for G1EvacuationFailureALot 3.50 3.51 @@ -290,4 +308,22 @@ 3.52 else return is_obj_ill(obj, hr); 3.53 } 3.54 3.55 +inline void G1CollectedHeap::set_humongous_is_live(oop obj) { 3.56 + uint region = addr_to_region((HeapWord*)obj); 3.57 + // We not only set the "live" flag in the humongous_is_live table, but also 3.58 + // reset the entry in the _in_cset_fast_test table so that subsequent references 3.59 + // to the same humongous object do not go into the slow path again. 3.60 + // This is racy, as multiple threads may at the same time enter here, but this 3.61 + // is benign. 3.62 + // During collection we only ever set the "live" flag, and only ever clear the 3.63 + // entry in the in_cset_fast_table. 3.64 + // We only ever evaluate the contents of these tables (in the VM thread) after 3.65 + // having synchronized the worker threads with the VM thread, or in the same 3.66 + // thread (i.e. within the VM thread). 3.67 + if (!_humongous_is_live.is_live(region)) { 3.68 + _humongous_is_live.set_live(region); 3.69 + _in_cset_fast_test.clear_humongous(region); 3.70 + } 3.71 +} 3.72 + 3.73 #endif // SHARE_VM_GC_IMPLEMENTATION_G1_G1COLLECTEDHEAP_INLINE_HPP
4.1 --- a/src/share/vm/gc_implementation/g1/g1GCPhaseTimes.cpp Mon Jul 21 10:00:31 2014 +0200 4.2 +++ b/src/share/vm/gc_implementation/g1/g1GCPhaseTimes.cpp Wed Jul 23 09:03:32 2014 +0200 4.3 @@ -254,6 +254,10 @@ 4.4 LineBuffer(level).append_and_print_cr("[%s: %.1lf ms]", str, value); 4.5 } 4.6 4.7 +void G1GCPhaseTimes::print_stats(int level, const char* str, size_t value) { 4.8 + LineBuffer(level).append_and_print_cr("[%s: "SIZE_FORMAT"]", str, value); 4.9 +} 4.10 + 4.11 void G1GCPhaseTimes::print_stats(int level, const char* str, double value, uint workers) { 4.12 LineBuffer(level).append_and_print_cr("[%s: %.1lf ms, GC Workers: " UINT32_FORMAT "]", str, value, workers); 4.13 } 4.14 @@ -356,6 +360,14 @@ 4.15 _last_redirty_logged_cards_processed_cards.print(3, "Redirtied Cards"); 4.16 } 4.17 } 4.18 + if (G1ReclaimDeadHumongousObjectsAtYoungGC) { 4.19 + print_stats(2, "Humongous Reclaim", _cur_fast_reclaim_humongous_time_ms); 4.20 + if (G1Log::finest()) { 4.21 + print_stats(3, "Humongous Total", _cur_fast_reclaim_humongous_total); 4.22 + print_stats(3, "Humongous Candidate", _cur_fast_reclaim_humongous_candidates); 4.23 + print_stats(3, "Humongous Reclaimed", _cur_fast_reclaim_humongous_reclaimed); 4.24 + } 4.25 + } 4.26 print_stats(2, "Free CSet", 4.27 (_recorded_young_free_cset_time_ms + 4.28 _recorded_non_young_free_cset_time_ms));
5.1 --- a/src/share/vm/gc_implementation/g1/g1GCPhaseTimes.hpp Mon Jul 21 10:00:31 2014 +0200 5.2 +++ b/src/share/vm/gc_implementation/g1/g1GCPhaseTimes.hpp Wed Jul 23 09:03:32 2014 +0200 5.3 @@ -157,11 +157,17 @@ 5.4 double _recorded_young_free_cset_time_ms; 5.5 double _recorded_non_young_free_cset_time_ms; 5.6 5.7 + double _cur_fast_reclaim_humongous_time_ms; 5.8 + size_t _cur_fast_reclaim_humongous_total; 5.9 + size_t _cur_fast_reclaim_humongous_candidates; 5.10 + size_t _cur_fast_reclaim_humongous_reclaimed; 5.11 + 5.12 double _cur_verify_before_time_ms; 5.13 double _cur_verify_after_time_ms; 5.14 5.15 // Helper methods for detailed logging 5.16 void print_stats(int level, const char* str, double value); 5.17 + void print_stats(int level, const char* str, size_t value); 5.18 void print_stats(int level, const char* str, double value, uint workers); 5.19 5.20 public: 5.21 @@ -282,6 +288,16 @@ 5.22 _recorded_non_young_free_cset_time_ms = time_ms; 5.23 } 5.24 5.25 + void record_fast_reclaim_humongous_stats(size_t total, size_t candidates) { 5.26 + _cur_fast_reclaim_humongous_total = total; 5.27 + _cur_fast_reclaim_humongous_candidates = candidates; 5.28 + } 5.29 + 5.30 + void record_fast_reclaim_humongous_time_ms(double value, size_t reclaimed) { 5.31 + _cur_fast_reclaim_humongous_time_ms = value; 5.32 + _cur_fast_reclaim_humongous_reclaimed = reclaimed; 5.33 + } 5.34 + 5.35 void record_young_cset_choice_time_ms(double time_ms) { 5.36 _recorded_young_cset_choice_time_ms = time_ms; 5.37 } 5.38 @@ -348,6 +364,10 @@ 5.39 return _recorded_non_young_free_cset_time_ms; 5.40 } 5.41 5.42 + double fast_reclaim_humongous_time_ms() { 5.43 + return _cur_fast_reclaim_humongous_time_ms; 5.44 + } 5.45 + 5.46 double average_last_update_rs_time() { 5.47 return _last_update_rs_times_ms.average(); 5.48 }
6.1 --- a/src/share/vm/gc_implementation/g1/g1OopClosures.inline.hpp Mon Jul 21 10:00:31 2014 +0200 6.2 +++ b/src/share/vm/gc_implementation/g1/g1OopClosures.inline.hpp Wed Jul 23 09:03:32 2014 +0200 6.3 @@ -44,7 +44,7 @@ 6.4 inline void FilterIntoCSClosure::do_oop_nv(T* p) { 6.5 T heap_oop = oopDesc::load_heap_oop(p); 6.6 if (!oopDesc::is_null(heap_oop) && 6.7 - _g1->obj_in_cs(oopDesc::decode_heap_oop_not_null(heap_oop))) { 6.8 + _g1->is_in_cset_or_humongous(oopDesc::decode_heap_oop_not_null(heap_oop))) { 6.9 _oc->do_oop(p); 6.10 } 6.11 } 6.12 @@ -67,7 +67,8 @@ 6.13 6.14 if (!oopDesc::is_null(heap_oop)) { 6.15 oop obj = oopDesc::decode_heap_oop_not_null(heap_oop); 6.16 - if (_g1->in_cset_fast_test(obj)) { 6.17 + G1CollectedHeap::in_cset_state_t state = _g1->in_cset_state(obj); 6.18 + if (state == G1CollectedHeap::InCSet) { 6.19 // We're not going to even bother checking whether the object is 6.20 // already forwarded or not, as this usually causes an immediate 6.21 // stall. We'll try to prefetch the object (for write, given that 6.22 @@ -86,6 +87,9 @@ 6.23 6.24 _par_scan_state->push_on_queue(p); 6.25 } else { 6.26 + if (state == G1CollectedHeap::IsHumongous) { 6.27 + _g1->set_humongous_is_live(obj); 6.28 + } 6.29 _par_scan_state->update_rs(_from, p, _worker_id); 6.30 } 6.31 } 6.32 @@ -97,12 +101,14 @@ 6.33 6.34 if (!oopDesc::is_null(heap_oop)) { 6.35 oop obj = oopDesc::decode_heap_oop_not_null(heap_oop); 6.36 - if (_g1->in_cset_fast_test(obj)) { 6.37 + if (_g1->is_in_cset_or_humongous(obj)) { 6.38 Prefetch::write(obj->mark_addr(), 0); 6.39 Prefetch::read(obj->mark_addr(), (HeapWordSize*2)); 6.40 6.41 // Place on the references queue 6.42 _par_scan_state->push_on_queue(p); 6.43 + } else { 6.44 + assert(!_g1->obj_in_cs(obj), "checking"); 6.45 } 6.46 } 6.47 }
7.1 --- a/src/share/vm/gc_implementation/g1/g1ParScanThreadState.inline.hpp Mon Jul 21 10:00:31 2014 +0200 7.2 +++ b/src/share/vm/gc_implementation/g1/g1ParScanThreadState.inline.hpp Wed Jul 23 09:03:32 2014 +0200 7.3 @@ -52,15 +52,20 @@ 7.4 // set, due to (benign) races in the claim mechanism during RSet scanning more 7.5 // than one thread might claim the same card. So the same card may be 7.6 // processed multiple times. So redo this check. 7.7 - if (_g1h->in_cset_fast_test(obj)) { 7.8 + G1CollectedHeap::in_cset_state_t in_cset_state = _g1h->in_cset_state(obj); 7.9 + if (in_cset_state == G1CollectedHeap::InCSet) { 7.10 oop forwardee; 7.11 if (obj->is_forwarded()) { 7.12 forwardee = obj->forwardee(); 7.13 } else { 7.14 forwardee = copy_to_survivor_space(obj); 7.15 } 7.16 - assert(forwardee != NULL, "forwardee should not be NULL"); 7.17 oopDesc::encode_store_heap_oop(p, forwardee); 7.18 + } else if (in_cset_state == G1CollectedHeap::IsHumongous) { 7.19 + _g1h->set_humongous_is_live(obj); 7.20 + } else { 7.21 + assert(in_cset_state == G1CollectedHeap::InNeither, 7.22 + err_msg("In_cset_state must be InNeither here, but is %d", in_cset_state)); 7.23 } 7.24 7.25 assert(obj != NULL, "Must be");
8.1 --- a/src/share/vm/gc_implementation/g1/g1_globals.hpp Mon Jul 21 10:00:31 2014 +0200 8.2 +++ b/src/share/vm/gc_implementation/g1/g1_globals.hpp Wed Jul 23 09:03:32 2014 +0200 8.3 @@ -289,6 +289,13 @@ 8.4 "The amount of code root chunks that should be kept at most " \ 8.5 "as percentage of already allocated.") \ 8.6 \ 8.7 + experimental(bool, G1ReclaimDeadHumongousObjectsAtYoungGC, true, \ 8.8 + "Try to reclaim dead large objects at every young GC.") \ 8.9 + \ 8.10 + experimental(bool, G1TraceReclaimDeadHumongousObjectsAtYoungGC, false, \ 8.11 + "Print some information about large object liveness " \ 8.12 + "at every young GC.") \ 8.13 + \ 8.14 experimental(uintx, G1OldCSetRegionThresholdPercent, 10, \ 8.15 "An upper bound for the number of old CSet regions expressed " \ 8.16 "as a percentage of the heap size.") \
9.1 --- a/src/share/vm/gc_implementation/g1/heapRegionRemSet.cpp Mon Jul 21 10:00:31 2014 +0200 9.2 +++ b/src/share/vm/gc_implementation/g1/heapRegionRemSet.cpp Wed Jul 23 09:03:32 2014 +0200 9.3 @@ -694,6 +694,9 @@ 9.4 clear_fcc(); 9.5 } 9.6 9.7 +bool OtherRegionsTable::is_empty() const { 9.8 + return occ_sparse() == 0 && occ_coarse() == 0 && _first_all_fine_prts == NULL; 9.9 +} 9.10 9.11 size_t OtherRegionsTable::occupied() const { 9.12 size_t sum = occ_fine();
10.1 --- a/src/share/vm/gc_implementation/g1/heapRegionRemSet.hpp Mon Jul 21 10:00:31 2014 +0200 10.2 +++ b/src/share/vm/gc_implementation/g1/heapRegionRemSet.hpp Wed Jul 23 09:03:32 2014 +0200 10.3 @@ -185,6 +185,9 @@ 10.4 // objects. 10.5 void scrub(CardTableModRefBS* ctbs, BitMap* region_bm, BitMap* card_bm); 10.6 10.7 + // Returns whether this remembered set (and all sub-sets) contain no entries. 10.8 + bool is_empty() const; 10.9 + 10.10 size_t occupied() const; 10.11 size_t occ_fine() const; 10.12 size_t occ_coarse() const; 10.13 @@ -269,6 +272,10 @@ 10.14 return _other_regions.hr(); 10.15 } 10.16 10.17 + bool is_empty() const { 10.18 + return (strong_code_roots_list_length() == 0) && _other_regions.is_empty(); 10.19 + } 10.20 + 10.21 size_t occupied() { 10.22 MutexLockerEx x(&_m, Mutex::_no_safepoint_check_flag); 10.23 return occupied_locked(); 10.24 @@ -375,7 +382,7 @@ 10.25 void strong_code_roots_do(CodeBlobClosure* blk) const; 10.26 10.27 // Returns the number of elements in the strong code roots list 10.28 - size_t strong_code_roots_list_length() { 10.29 + size_t strong_code_roots_list_length() const { 10.30 return _code_roots.length(); 10.31 } 10.32
11.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 11.2 +++ b/test/gc/g1/TestEagerReclaimHumongousRegions.java Wed Jul 23 09:03:32 2014 +0200 11.3 @@ -0,0 +1,98 @@ 11.4 +/* 11.5 + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. 11.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 11.7 + * 11.8 + * This code is free software; you can redistribute it and/or modify it 11.9 + * under the terms of the GNU General Public License version 2 only, as 11.10 + * published by the Free Software Foundation. 11.11 + * 11.12 + * This code is distributed in the hope that it will be useful, but WITHOUT 11.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 11.15 + * version 2 for more details (a copy is included in the LICENSE file that 11.16 + * accompanied this code). 11.17 + * 11.18 + * You should have received a copy of the GNU General Public License version 11.19 + * 2 along with this work; if not, write to the Free Software Foundation, 11.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 11.21 + * 11.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 11.23 + * or visit www.oracle.com if you need additional information or have any 11.24 + * questions. 11.25 + */ 11.26 + 11.27 +/* 11.28 + * @test TestEagerReclaimHumongousRegions 11.29 + * @bug 8027959 11.30 + * @summary Test to make sure that eager reclaim of humongous objects work. We simply try to fill 11.31 + * up the heap with humongous objects that should be eagerly reclaimable to avoid Full GC. 11.32 + * @key gc 11.33 + * @library /testlibrary 11.34 + */ 11.35 + 11.36 +import java.util.regex.Pattern; 11.37 +import java.util.regex.Matcher; 11.38 +import java.util.LinkedList; 11.39 + 11.40 +import com.oracle.java.testlibrary.OutputAnalyzer; 11.41 +import com.oracle.java.testlibrary.ProcessTools; 11.42 +import com.oracle.java.testlibrary.Asserts; 11.43 + 11.44 +class ReclaimRegionFast { 11.45 + public static final int M = 1024*1024; 11.46 + 11.47 + public static LinkedList<Object> garbageList = new LinkedList<Object>(); 11.48 + 11.49 + public static void genGarbage() { 11.50 + for (int i = 0; i < 32*1024; i++) { 11.51 + garbageList.add(new int[100]); 11.52 + } 11.53 + garbageList.clear(); 11.54 + } 11.55 + 11.56 + // A large object referenced by a static. 11.57 + static int[] filler = new int[10 * M]; 11.58 + 11.59 + public static void main(String[] args) { 11.60 + 11.61 + int[] large = new int[M]; 11.62 + 11.63 + Object ref_from_stack = large; 11.64 + 11.65 + for (int i = 0; i < 100; i++) { 11.66 + // A large object that will be reclaimed eagerly. 11.67 + large = new int[6*M]; 11.68 + genGarbage(); 11.69 + // Make sure that the compiler cannot completely remove 11.70 + // the allocation of the large object until here. 11.71 + System.out.println(large); 11.72 + } 11.73 + 11.74 + // Keep the reference to the first object alive. 11.75 + System.out.println(ref_from_stack); 11.76 + } 11.77 +} 11.78 + 11.79 +public class TestEagerReclaimHumongousRegions { 11.80 + public static void main(String[] args) throws Exception { 11.81 + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder( 11.82 + "-XX:+UseG1GC", 11.83 + "-Xms128M", 11.84 + "-Xmx128M", 11.85 + "-Xmn16M", 11.86 + "-XX:+PrintGC", 11.87 + ReclaimRegionFast.class.getName()); 11.88 + 11.89 + Pattern p = Pattern.compile("Full GC"); 11.90 + 11.91 + OutputAnalyzer output = new OutputAnalyzer(pb.start()); 11.92 + 11.93 + int found = 0; 11.94 + Matcher m = p.matcher(output.getStdout()); 11.95 + while (m.find()) { found++; } 11.96 + System.out.println("Issued " + found + " Full GCs"); 11.97 + 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"); 11.98 + 11.99 + output.shouldHaveExitValue(0); 11.100 + } 11.101 +}
12.1 --- a/test/gc/g1/TestGCLogMessages.java Mon Jul 21 10:00:31 2014 +0200 12.2 +++ b/test/gc/g1/TestGCLogMessages.java Wed Jul 23 09:03:32 2014 +0200 12.3 @@ -23,7 +23,7 @@ 12.4 12.5 /* 12.6 * @test TestGCLogMessages 12.7 - * @bug 8035406 8027295 8035398 8019342 12.8 + * @bug 8035406 8027295 8035398 8019342 8027959 12.9 * @summary Ensure that the PrintGCDetails output for a minor GC with G1 12.10 * includes the expected necessary messages. 12.11 * @key gc 12.12 @@ -54,6 +54,7 @@ 12.13 output.shouldNotContain("[String Dedup Fixup"); 12.14 output.shouldNotContain("[Young Free CSet"); 12.15 output.shouldNotContain("[Non-Young Free CSet"); 12.16 + output.shouldNotContain("[Humongous Reclaim"); 12.17 output.shouldHaveExitValue(0); 12.18 12.19 pb = ProcessTools.createJavaProcessBuilder("-XX:+UseG1GC", 12.20 @@ -71,6 +72,10 @@ 12.21 output.shouldContain("[String Dedup Fixup"); 12.22 output.shouldNotContain("[Young Free CSet"); 12.23 output.shouldNotContain("[Non-Young Free CSet"); 12.24 + output.shouldContain("[Humongous Reclaim"); 12.25 + output.shouldNotContain("[Humongous Total"); 12.26 + output.shouldNotContain("[Humongous Candidate"); 12.27 + output.shouldNotContain("[Humongous Reclaimed"); 12.28 output.shouldHaveExitValue(0); 12.29 12.30 pb = ProcessTools.createJavaProcessBuilder("-XX:+UseG1GC", 12.31 @@ -90,6 +95,10 @@ 12.32 output.shouldContain("[String Dedup Fixup"); 12.33 output.shouldContain("[Young Free CSet"); 12.34 output.shouldContain("[Non-Young Free CSet"); 12.35 + output.shouldContain("[Humongous Reclaim"); 12.36 + output.shouldContain("[Humongous Total"); 12.37 + output.shouldContain("[Humongous Candidate"); 12.38 + output.shouldContain("[Humongous Reclaimed"); 12.39 output.shouldHaveExitValue(0); 12.40 } 12.41