1 /* 2 * Copyright (c) 2017, Red Hat, Inc. and/or its affiliates. 3 * 4 * This code is free software; you can redistribute it and/or modify it 5 * under the terms of the GNU General Public License version 2 only, as 6 * published by the Free Software Foundation. 7 * 8 * This code is distributed in the hope that it will be useful, but WITHOUT 9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 11 * version 2 for more details (a copy is included in the LICENSE file that 12 * accompanied this code). 13 * 14 * You should have received a copy of the GNU General Public License version 15 * 2 along with this work; if not, write to the Free Software Foundation, 16 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 17 * 18 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 19 * or visit www.oracle.com if you need additional information or have any 20 * questions. 21 * 22 */ 23 24 #include "gc/shared/gcTraceTime.inline.hpp" 25 #include "gc/shared/workgroup.hpp" 26 #include "gc/shared/taskqueue.inline.hpp" 27 #include "gc/shenandoah/shenandoahBarrierSet.hpp" 28 #include "gc/shenandoah/shenandoahCollectionSet.hpp" 29 #include "gc/shenandoah/shenandoahFreeSet.hpp" 30 #include "gc/shenandoah/shenandoahHeapRegionSet.hpp" 31 #include "gc/shenandoah/shenandoahHeap.inline.hpp" 32 #include "gc/shenandoah/shenandoahPartialGC.hpp" 33 #include "gc/shenandoah/shenandoahRootProcessor.hpp" 34 #include "gc/shenandoah/shenandoahTaskqueue.hpp" 35 #include "memory/iterator.hpp" 36 37 class PartialEvacuateUpdateRootsClosure : public OopClosure { 38 ShenandoahPartialGC* _partial_gc; 39 Thread* _thread; 40 SCMObjToScanQueue* _queue; 41 private: 42 template <class T> 43 void do_oop_work(T* p) { _partial_gc->process_oop(p, _thread, _queue, false); } 44 public: 45 PartialEvacuateUpdateRootsClosure(SCMObjToScanQueue* q) : 46 _partial_gc(ShenandoahHeap::heap()->partial_gc()), 47 _thread(Thread::current()), _queue(q) {} 48 void do_oop(oop* p) { 49 assert(! ShenandoahHeap::heap()->is_in_reserved(p), "sanity"); 50 do_oop_work(p); 51 } 52 void do_oop(narrowOop* p) { do_oop_work(p); } 53 }; 54 55 class PartialEvacuateUpdateHeapClosure : public ExtendedOopClosure { 56 ShenandoahPartialGC* _partial_gc; 57 Thread* _thread; 58 SCMObjToScanQueue* _queue; 59 private: 60 template <class T> 61 void do_oop_work(T* p) { 62 _partial_gc->process_oop(p, _thread, _queue, true); 63 } 64 public: 65 PartialEvacuateUpdateHeapClosure(SCMObjToScanQueue* q) : 66 _partial_gc(ShenandoahHeap::heap()->partial_gc()), 67 _thread(Thread::current()), _queue(q) {} 68 void do_oop(oop* p) { do_oop_work(p); } 69 void do_oop(narrowOop* p) { do_oop_work(p); } 70 }; 71 72 class ShenandoahPartialCollectionTask : public AbstractGangTask { 73 private: 74 ShenandoahRootProcessor* _rp; 75 ShenandoahHeapRegionSet* _root_regions; 76 ShenandoahHeap* _heap; 77 public: 78 ShenandoahPartialCollectionTask(ShenandoahRootProcessor* rp, 79 ShenandoahHeapRegionSet* root_regions) : 80 AbstractGangTask("Shenandoah Partial Collection"), 81 _rp(rp), _root_regions(root_regions), 82 _heap(ShenandoahHeap::heap()) {} 83 84 void work(uint worker_id) { 85 ShenandoahConnectionMatrix* matrix = _heap->connection_matrix(); 86 SCMObjToScanQueueSet* queues = _heap->partial_gc()->task_queues(); 87 SCMObjToScanQueue* q = queues->queue(worker_id); 88 { 89 // First process ordinary GC roots. 90 PartialEvacuateUpdateRootsClosure roots_cl(q); 91 CLDToOopClosure cld_cl(&roots_cl); 92 MarkingCodeBlobClosure code_cl(&roots_cl, CodeBlobToOopClosure::FixRelocations); 93 _rp->process_all_roots(&roots_cl, &roots_cl, &cld_cl, &code_cl, worker_id); 94 } 95 drain_queue(worker_id); 96 if (_heap->cancelled_concgc()) { q->set_empty(); return; } 97 { 98 // Then process root regions. 99 PartialEvacuateUpdateHeapClosure cl(q); 100 ShenandoahHeapRegion* r = _root_regions->claim_next(); 101 while (r != NULL) { 102 // We rebuild the outbound matrix for all roots, thus we can clear them before. 103 // This makes the matrix more precise. 104 matrix->clear_region_outbound(r->region_number()); 105 106 assert(r->is_root(), "must be root region"); 107 r->oop_iterate(&cl); 108 r->set_root(false); 109 drain_queue(worker_id); 110 if (_heap->cancelled_concgc()) { q->set_empty(); return; } 111 r = _root_regions->claim_next(); 112 } 113 } 114 } 115 116 void drain_queue(uint worker_id) { 117 SCMObjToScanQueueSet* queues = _heap->partial_gc()->task_queues(); 118 SCMObjToScanQueue* q = queues->queue(worker_id); 119 PartialEvacuateUpdateHeapClosure cl(q); 120 // Empty queue if necessary. 121 int seed = 17; 122 SCMTask task; 123 while ((q->pop_buffer(task) || 124 q->pop_local(task) || 125 q->pop_overflow(task)) && 126 !_heap->cancelled_concgc()) { 127 oop obj = task.obj(); 128 assert(! oopDesc::is_null(obj), "must not be null"); 129 obj->oop_iterate(&cl); 130 } 131 } 132 133 }; 134 135 ShenandoahPartialGC::ShenandoahPartialGC(ShenandoahHeap* heap, uint max_regions) : 136 _heap(heap), 137 _root_regions(new ShenandoahHeapRegionSet(max_regions)), 138 _task_queues(new SCMObjToScanQueueSet(heap->max_workers())) { 139 140 uint num_queues = heap->max_workers(); 141 for (uint i = 0; i < num_queues; ++i) { 142 SCMObjToScanQueue* task_queue = new SCMObjToScanQueue(); 143 task_queue->initialize(); 144 _task_queues->register_queue(i, task_queue); 145 } 146 147 } 148 149 ShenandoahHeapRegionSet* ShenandoahPartialGC::root_regions() { 150 return _root_regions; 151 } 152 153 void ShenandoahPartialGC::prepare() { 154 _heap->collection_set()->clear(); 155 assert(_heap->collection_set()->count() == 0, "collection set not clear"); 156 157 _heap->ensure_parsability(true); 158 159 ShenandoahConnectionMatrix* matrix = _heap->connection_matrix(); 160 ShenandoahHeapRegionSet* regions = _heap->regions(); 161 ShenandoahCollectionSet* collection_set = _heap->collection_set(); 162 ShenandoahFreeSet* free_set = _heap->free_regions(); 163 free_set->clear(); 164 _root_regions->clear(); 165 assert(_root_regions->count() == 0, "must be cleared"); 166 uint num_regions = _heap->num_regions(); 167 168 // First pass: find collection set. 169 for (uint to_idx = 0; to_idx < num_regions; to_idx++) { 170 ShenandoahHeapRegion* region = regions->get(to_idx); 171 region->set_root(false); 172 guarantee(! region->is_root(), "must not be root here"); 173 if (region->is_humongous() || region->is_empty() || region->is_pinned()) continue; 174 assert(! _heap->region_in_collection_set(to_idx), "must not be in cset yet"); 175 uint num_incoming = 0; 176 for (uint from_idx = 0; from_idx < num_regions; from_idx++) { 177 if (matrix->is_connected(from_idx, to_idx)) { 178 num_incoming++; 179 } 180 } 181 if (num_incoming < ShenandoahPartialInboundThreshold) { 182 collection_set->add_region(region); 183 _heap->set_region_in_collection_set(to_idx, true); 184 } 185 } 186 // Second pass: find all root regions. 187 uint num_cset = collection_set->count(); 188 for (uint idx = 0; idx < num_cset; idx++) { 189 ShenandoahHeapRegion* region = collection_set->get(idx); 190 uint to_idx = region->region_number(); 191 assert(! region->is_humongous() && ! region->is_empty() && ! region->is_pinned(), "sanity"); 192 assert(_heap->region_in_collection_set(to_idx), "more sanity"); 193 for (uint from_idx = 0; from_idx < num_regions; from_idx++) { 194 if (matrix->is_connected(from_idx, to_idx)) { 195 ShenandoahHeapRegion* r = regions->get(from_idx); 196 if (! r->is_root() && ! _heap->region_in_collection_set(from_idx)) { 197 _root_regions->add_region(r); 198 r->set_root(true); 199 } 200 } 201 } 202 } 203 // Final pass: free regions. 204 for (uint to_idx = 0; to_idx < num_regions; to_idx++) { 205 ShenandoahHeapRegion* region = regions->get(to_idx); 206 if (! region->is_humongous() && 207 ! region->is_pinned() && 208 ! region->is_root() && 209 ! _heap->in_collection_set(region)) { 210 211 free_set->add_region(region); 212 } 213 } 214 log_debug(gc, ergo)("got "SIZE_FORMAT" cset regions", collection_set->count()); 215 log_debug(gc, ergo)("got "SIZE_FORMAT" root regions", _root_regions->count()); 216 } 217 218 void ShenandoahPartialGC::do_partial_collection() { 219 220 _heap->gc_timer()->register_gc_start(); 221 ShenandoahCollectorPolicy* policy = _heap->shenandoahPolicy(); 222 policy->record_phase_start(ShenandoahCollectorPolicy::partial_gc); 223 224 { 225 GCTraceTime(Info, gc) time("Pause Partial", _heap->gc_timer(), GCCause::_no_gc, true); 226 227 if (ShenandoahVerify || VerifyShenandoahMatrix) { 228 log_debug(gc, ergo)("Verifying before partial collection"); 229 _heap->verify_heap_reachable_at_safepoint(); 230 log_debug(gc, ergo)("Verification passed"); 231 } 232 233 COMPILER2_PRESENT(DerivedPointerTable::clear()); 234 235 { 236 ClassLoaderDataGraph::clear_claimed_marks(); 237 ShenandoahRootProcessor rp(_heap, _heap->workers()->active_workers()); 238 ShenandoahPartialCollectionTask partial_task(&rp, _root_regions); 239 _heap->workers()->run_task(&partial_task); 240 } 241 242 COMPILER2_PRESENT(DerivedPointerTable::update_pointers()); 243 244 if (! _heap->cancelled_concgc()) { 245 if (ShenandoahVerify || VerifyShenandoahMatrix) { 246 log_debug(gc, ergo)("Verifying after partial collection"); 247 _heap->verify_heap_reachable_at_safepoint(); 248 log_debug(gc, ergo)("Verification passed"); 249 } 250 251 ShenandoahHeap::ShenandoahHeapLock heap_lock(_heap); 252 uint num_cset = _heap->collection_set()->count(); 253 for (uint i = 0; i < num_cset; i++) { 254 ShenandoahHeapRegion* r = _heap->collection_set()->get(i); 255 _heap->decrease_used(r->used()); 256 r->recycle(); 257 _heap->free_regions()->add_region(r); 258 } 259 260 reset(); 261 } 262 } 263 264 policy->record_phase_end(ShenandoahCollectorPolicy::partial_gc); 265 _heap->gc_timer()->register_gc_end(); 266 } 267 268 void ShenandoahPartialGC::reset() { 269 _heap->collection_set()->clear(); 270 _heap->clear_cset_fast_test(); 271 _root_regions->clear(); 272 } 273 274 template <class T> 275 void ShenandoahPartialGC::process_oop(T* p, Thread* thread, SCMObjToScanQueue* queue, bool update_matrix) { 276 T o = oopDesc::load_heap_oop(p); 277 if (! oopDesc::is_null(o)) { 278 oop obj = oopDesc::decode_heap_oop_not_null(o); 279 if (_heap->in_collection_set(obj)) { 280 oop forw = ShenandoahBarrierSet::resolve_oop_static_not_null(obj); 281 if (oopDesc::unsafe_equals(obj, forw)) { 282 forw = _heap->evacuate_object(obj, thread); 283 } 284 assert(! oopDesc::unsafe_equals(obj, forw) || _heap->cancelled_concgc(), "must be evacuated"); 285 // TODO: Only the thread that succeeds *evacuating* the object should need to 286 // update the matrix and push the evacuated object to its queue. This would also 287 // enable to only have one CAS (the one in evacuate_object()) and use simple 288 // store for updating the ref. 289 oop oldval = _heap->atomic_compare_exchange_oop(forw, p, obj); 290 if (oopDesc::unsafe_equals(obj, oldval)) { 291 assert(forw->is_oop(), "sanity"); 292 bool succeeded = queue->push(SCMTask(forw)); 293 assert(succeeded, "must succeed to push to task queue"); 294 } else { 295 assert(oopDesc::unsafe_equals(oldval, forw), "other thread must have punched the same forwarded oop"); 296 } 297 obj = forw; // For matrix update below. 298 } 299 // TODO: Make this templated 300 if (update_matrix) { 301 #ifdef ASSERT 302 oop forw = ShenandoahBarrierSet::resolve_oop_static_not_null(obj); 303 assert(oopDesc::unsafe_equals(obj, forw) || _heap->cancelled_concgc(), "must not be evacuated"); 304 #endif 305 uint from_idx = _heap->heap_region_index_containing(p); 306 uint to_idx = _heap->heap_region_index_containing(obj); 307 _heap->connection_matrix()->set_connected(from_idx, to_idx, true); 308 } 309 } 310 } 311 312 bool ShenandoahPartialGC::is_in_root_region(oop obj) { 313 // TODO: make this very fast!! 314 ShenandoahHeapRegion* r = _heap->heap_region_containing(obj); 315 return _root_regions->contains(r); 316 } 317 318 SCMObjToScanQueueSet* ShenandoahPartialGC::task_queues() { 319 return _task_queues; 320 }