1 /* 2 * Copyright (c) 2017, 2019, Red Hat, Inc. All rights reserved. 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 "precompiled.hpp" 25 #include "code/codeCache.hpp" 26 #include "code/nmethod.hpp" 27 #include "gc_implementation/shenandoah/shenandoahHeap.inline.hpp" 28 #include "gc_implementation/shenandoah/shenandoahCodeRoots.hpp" 29 #include "memory/resourceArea.hpp" 30 31 ShenandoahParallelCodeCacheIterator::ShenandoahParallelCodeCacheIterator() : 32 _claimed_idx(0), _finished(false) { 33 } 34 35 void ShenandoahParallelCodeCacheIterator::parallel_blobs_do(CodeBlobClosure* f) { 36 assert(SafepointSynchronize::is_at_safepoint(), "Must be at safepoint"); 37 38 /* 39 * Parallel code heap walk. 40 * 41 * This code makes all threads scan all code heaps, but only one thread would execute the 42 * closure on given blob. This is achieved by recording the "claimed" blocks: if a thread 43 * had claimed the block, it can process all blobs in it. Others have to fast-forward to 44 * next attempt without processing. 45 * 46 * Late threads would return immediately if iterator is finished. 47 */ 48 49 if (_finished) { 50 return; 51 } 52 53 int stride = 256; // educated guess 54 int stride_mask = stride - 1; 55 assert (is_power_of_2(stride), "sanity"); 56 57 int count = 0; 58 bool process_block = true; 59 60 for (CodeBlob *cb = CodeCache::first(); cb != NULL; cb = CodeCache::next(cb)) { 61 int current = count++; 62 if ((current & stride_mask) == 0) { 63 process_block = (current >= _claimed_idx) && 64 (Atomic::cmpxchg(current + stride, &_claimed_idx, current) == current); 65 } 66 if (process_block) { 67 if (cb->is_alive()) { 68 f->do_code_blob(cb); 69 #ifdef ASSERT 70 if (cb->is_nmethod()) 71 ((nmethod*)cb)->verify_scavenge_root_oops(); 72 #endif 73 } 74 } 75 } 76 77 _finished = true; 78 } 79 80 class ShenandoahNMethodOopDetector : public OopClosure { 81 private: 82 ResourceMark rm; // For growable array allocation below. 83 GrowableArray<oop*> _oops; 84 85 public: 86 ShenandoahNMethodOopDetector() : _oops(10) {}; 87 88 void do_oop(oop* o) { 89 _oops.append(o); 90 } 91 92 void do_oop(narrowOop* o) { 93 fatal("NMethods should not have compressed oops embedded."); 94 } 95 96 GrowableArray<oop*>* oops() { 97 return &_oops; 98 } 99 100 bool has_oops() { 101 return !_oops.is_empty(); 102 } 103 }; 104 105 ShenandoahCodeRoots::PaddedLock ShenandoahCodeRoots::_recorded_nms_lock; 106 GrowableArray<ShenandoahNMethod*>* ShenandoahCodeRoots::_recorded_nms; 107 108 void ShenandoahCodeRoots::initialize() { 109 _recorded_nms_lock._lock = 0; 110 _recorded_nms = new (ResourceObj::C_HEAP, mtGC) GrowableArray<ShenandoahNMethod*>(100, true, mtGC); 111 } 112 113 void ShenandoahCodeRoots::add_nmethod(nmethod* nm) { 114 switch (ShenandoahCodeRootsStyle) { 115 case 0: 116 case 1: 117 break; 118 case 2: { 119 ShenandoahNMethodOopDetector detector; 120 nm->oops_do(&detector); 121 122 if (detector.has_oops()) { 123 ShenandoahNMethod* nmr = new ShenandoahNMethod(nm, detector.oops()); 124 nmr->assert_alive_and_correct(); 125 126 ShenandoahCodeRootsLock lock(true); 127 128 int idx = _recorded_nms->find(nm, ShenandoahNMethod::find_with_nmethod); 129 if (idx != -1) { 130 ShenandoahNMethod* old = _recorded_nms->at(idx); 131 _recorded_nms->at_put(idx, nmr); 132 delete old; 133 } else { 134 _recorded_nms->append(nmr); 135 } 136 } 137 break; 138 } 139 default: 140 ShouldNotReachHere(); 141 } 142 }; 143 144 void ShenandoahCodeRoots::remove_nmethod(nmethod* nm) { 145 switch (ShenandoahCodeRootsStyle) { 146 case 0: 147 case 1: { 148 break; 149 } 150 case 2: { 151 ShenandoahNMethodOopDetector detector; 152 nm->oops_do(&detector, /* allow_zombie = */ true); 153 154 if (detector.has_oops()) { 155 ShenandoahCodeRootsLock lock(true); 156 157 int idx = _recorded_nms->find(nm, ShenandoahNMethod::find_with_nmethod); 158 assert(idx != -1, err_msg("nmethod " PTR_FORMAT " should be registered", p2i(nm))); 159 ShenandoahNMethod* old = _recorded_nms->at(idx); 160 old->assert_same_oops(detector.oops()); 161 _recorded_nms->delete_at(idx); 162 delete old; 163 } 164 break; 165 } 166 default: 167 ShouldNotReachHere(); 168 } 169 } 170 171 ShenandoahCodeRootsIterator::ShenandoahCodeRootsIterator() : 172 _heap(ShenandoahHeap::heap()), 173 _claimed(0) { 174 assert(SafepointSynchronize::is_at_safepoint(), "Must be at safepoint"); 175 assert(!Thread::current()->is_Worker_thread(), "Should not be acquired by workers"); 176 switch (ShenandoahCodeRootsStyle) { 177 case 0: 178 case 1: 179 break; 180 case 2: 181 ShenandoahCodeRoots::acquire_lock(false); 182 break; 183 default: 184 ShouldNotReachHere(); 185 } 186 } 187 188 ShenandoahCodeRootsIterator::~ShenandoahCodeRootsIterator() { 189 switch (ShenandoahCodeRootsStyle) { 190 case 0: 191 case 1: { 192 // No need to do anything here 193 break; 194 } 195 case 2: { 196 ShenandoahCodeRoots::release_lock(false); 197 break; 198 } 199 default: 200 ShouldNotReachHere(); 201 } 202 } 203 204 template<bool CSET_FILTER> 205 void ShenandoahCodeRootsIterator::dispatch_parallel_blobs_do(CodeBlobClosure *f) { 206 switch (ShenandoahCodeRootsStyle) { 207 case 0: { 208 if (_seq_claimed.try_set()) { 209 CodeCache::blobs_do(f); 210 } 211 break; 212 } 213 case 1: { 214 _par_iterator.parallel_blobs_do(f); 215 break; 216 } 217 case 2: { 218 ShenandoahCodeRootsIterator::fast_parallel_blobs_do<CSET_FILTER>(f); 219 break; 220 } 221 default: 222 ShouldNotReachHere(); 223 } 224 } 225 226 ShenandoahAllCodeRootsIterator ShenandoahCodeRoots::iterator() { 227 return ShenandoahAllCodeRootsIterator(); 228 } 229 230 ShenandoahCsetCodeRootsIterator ShenandoahCodeRoots::cset_iterator() { 231 return ShenandoahCsetCodeRootsIterator(); 232 } 233 234 void ShenandoahAllCodeRootsIterator::possibly_parallel_blobs_do(CodeBlobClosure *f) { 235 ShenandoahCodeRootsIterator::dispatch_parallel_blobs_do<false>(f); 236 } 237 238 void ShenandoahCsetCodeRootsIterator::possibly_parallel_blobs_do(CodeBlobClosure *f) { 239 ShenandoahCodeRootsIterator::dispatch_parallel_blobs_do<true>(f); 240 } 241 242 template <bool CSET_FILTER> 243 void ShenandoahCodeRootsIterator::fast_parallel_blobs_do(CodeBlobClosure *f) { 244 assert(SafepointSynchronize::is_at_safepoint(), "Must be at safepoint"); 245 246 size_t stride = 256; // educated guess 247 248 GrowableArray<ShenandoahNMethod*>* list = ShenandoahCodeRoots::_recorded_nms; 249 250 jlong max = list->length(); 251 while (_claimed < max) { 252 size_t cur = (size_t)Atomic::add(stride, &_claimed); // Note: in JDK 8, add() returns old value 253 size_t start = cur; 254 size_t end = MIN2(cur + stride, (size_t) max); 255 if (start >= (size_t)max) break; 256 257 for (size_t idx = start; idx < end; idx++) { 258 ShenandoahNMethod* nmr = list->at((int) idx); 259 nmr->assert_alive_and_correct(); 260 261 if (CSET_FILTER && !nmr->has_cset_oops(_heap)) { 262 continue; 263 } 264 265 f->do_code_blob(nmr->nm()); 266 } 267 } 268 } 269 270 ShenandoahNMethod::ShenandoahNMethod(nmethod* nm, GrowableArray<oop*>* oops) { 271 _nm = nm; 272 _oops = NEW_C_HEAP_ARRAY(oop*, oops->length(), mtGC); 273 _oops_count = oops->length(); 274 for (int c = 0; c < _oops_count; c++) { 275 _oops[c] = oops->at(c); 276 } 277 } 278 279 ShenandoahNMethod::~ShenandoahNMethod() { 280 if (_oops != NULL) { 281 FREE_C_HEAP_ARRAY(oop*, _oops, mtGC); 282 } 283 } 284 285 bool ShenandoahNMethod::has_cset_oops(ShenandoahHeap *heap) { 286 for (int c = 0; c < _oops_count; c++) { 287 oop o = oopDesc::load_heap_oop(_oops[c]); 288 if (heap->in_collection_set(o)) { 289 return true; 290 } 291 } 292 return false; 293 } 294 295 #ifdef ASSERT 296 void ShenandoahNMethod::assert_alive_and_correct() { 297 assert(_nm->is_alive(), "only alive nmethods here"); 298 assert(_oops_count > 0, "should have filtered nmethods without oops before"); 299 ShenandoahHeap* heap = ShenandoahHeap::heap(); 300 for (int c = 0; c < _oops_count; c++) { 301 oop *loc = _oops[c]; 302 assert(_nm->code_contains((address)loc) || _nm->oops_contains(loc), "nmethod should contain the oop*"); 303 oop o = oopDesc::load_heap_oop(loc); 304 shenandoah_assert_correct_except(loc, o, o == NULL || heap->is_full_gc_move_in_progress()); 305 } 306 } 307 308 void ShenandoahNMethod::assert_same_oops(GrowableArray<oop*>* oops) { 309 assert(_oops_count == oops->length(), "should have the same number of oop*"); 310 for (int c = 0; c < _oops_count; c++) { 311 assert(_oops[c] == oops->at(c), "should be the same oop*"); 312 } 313 } 314 #endif