--- /dev/null 2020-01-17 11:46:19.065201212 +0100 +++ new/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.cpp 2020-01-17 17:09:55.901131262 +0100 @@ -0,0 +1,332 @@ +/* + * Copyright (c) 2017, 2019, Red Hat, Inc. All rights reserved. + * + * 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. + * + */ + +#include "precompiled.hpp" +#include "code/codeCache.hpp" +#include "code/nmethod.hpp" +#include "gc/shenandoah/shenandoahHeap.inline.hpp" +#include "gc/shenandoah/shenandoahCodeRoots.hpp" +#include "gc/shenandoah/shenandoahUtils.hpp" +#include "memory/resourceArea.hpp" + +ShenandoahParallelCodeCacheIterator::ShenandoahParallelCodeCacheIterator(const GrowableArray* heaps) { + _length = heaps->length(); + _iters = NEW_C_HEAP_ARRAY(ShenandoahParallelCodeHeapIterator, _length, mtGC); + for (int h = 0; h < _length; h++) { + _iters[h] = ShenandoahParallelCodeHeapIterator(heaps->at(h)); + } +} + +ShenandoahParallelCodeCacheIterator::~ShenandoahParallelCodeCacheIterator() { + FREE_C_HEAP_ARRAY(ParallelCodeHeapIterator, _iters); +} + +void ShenandoahParallelCodeCacheIterator::parallel_blobs_do(CodeBlobClosure* f) { + for (int c = 0; c < _length; c++) { + _iters[c].parallel_blobs_do(f); + } +} + +ShenandoahParallelCodeHeapIterator::ShenandoahParallelCodeHeapIterator(CodeHeap* heap) : + _heap(heap), _claimed_idx(0), _finished(false) { +} + +void ShenandoahParallelCodeHeapIterator::parallel_blobs_do(CodeBlobClosure* f) { + assert(SafepointSynchronize::is_at_safepoint(), "Must be at safepoint"); + + /* + * Parallel code heap walk. + * + * This code makes all threads scan all code heaps, but only one thread would execute the + * closure on given blob. This is achieved by recording the "claimed" blocks: if a thread + * had claimed the block, it can process all blobs in it. Others have to fast-forward to + * next attempt without processing. + * + * Late threads would return immediately if iterator is finished. + */ + + if (_finished) { + return; + } + + int stride = 256; // educated guess + int stride_mask = stride - 1; + assert (is_power_of_2(stride), "sanity"); + + int count = 0; + bool process_block = true; + + for (CodeBlob *cb = CodeCache::first_blob(_heap); cb != NULL; cb = CodeCache::next_blob(_heap, cb)) { + int current = count++; + if ((current & stride_mask) == 0) { + process_block = (current >= _claimed_idx) && + (Atomic::cmpxchg(current + stride, &_claimed_idx, current) == current); + } + if (process_block) { + if (cb->is_alive()) { + f->do_code_blob(cb); +#ifdef ASSERT + if (cb->is_nmethod()) + Universe::heap()->verify_nmethod((nmethod*)cb); +#endif + } + } + } + + _finished = true; +} + +class ShenandoahNMethodOopDetector : public OopClosure { +private: + ResourceMark rm; // For growable array allocation below. + GrowableArray _oops; + +public: + ShenandoahNMethodOopDetector() : _oops(10) {}; + + void do_oop(oop* o) { + _oops.append(o); + } + void do_oop(narrowOop* o) { + fatal("NMethods should not have compressed oops embedded."); + } + + GrowableArray* oops() { + return &_oops; + } + + bool has_oops() { + return !_oops.is_empty(); + } +}; + +GrowableArray* ShenandoahCodeRoots::_recorded_nms; +ShenandoahLock ShenandoahCodeRoots::_recorded_nms_lock; + +void ShenandoahCodeRoots::initialize() { + _recorded_nms = new (ResourceObj::C_HEAP, mtGC) GrowableArray(100, true, mtGC); +} + +void ShenandoahCodeRoots::add_nmethod(nmethod* nm) { + switch (ShenandoahCodeRootsStyle) { + case 0: + case 1: + break; + case 2: { + assert_locked_or_safepoint(CodeCache_lock); + ShenandoahLocker locker(CodeCache_lock->owned_by_self() ? NULL : &_recorded_nms_lock); + + ShenandoahNMethodOopDetector detector; + nm->oops_do(&detector); + + if (detector.has_oops()) { + ShenandoahNMethod* nmr = new ShenandoahNMethod(nm, detector.oops()); + nmr->assert_alive_and_correct(); + int idx = _recorded_nms->find(nm, ShenandoahNMethod::find_with_nmethod); + if (idx != -1) { + ShenandoahNMethod* old = _recorded_nms->at(idx); + _recorded_nms->at_put(idx, nmr); + delete old; + } else { + _recorded_nms->append(nmr); + } + } + break; + } + default: + ShouldNotReachHere(); + } +}; + +void ShenandoahCodeRoots::remove_nmethod(nmethod* nm) { + switch (ShenandoahCodeRootsStyle) { + case 0: + case 1: { + break; + } + case 2: { + assert_locked_or_safepoint(CodeCache_lock); + ShenandoahLocker locker(CodeCache_lock->owned_by_self() ? NULL : &_recorded_nms_lock); + + ShenandoahNMethodOopDetector detector; + nm->oops_do(&detector, /* allow_zombie = */ true); + + if (detector.has_oops()) { + int idx = _recorded_nms->find(nm, ShenandoahNMethod::find_with_nmethod); + assert(idx != -1, "nmethod " PTR_FORMAT " should be registered", p2i(nm)); + ShenandoahNMethod* old = _recorded_nms->at(idx); + old->assert_same_oops(detector.oops()); + _recorded_nms->delete_at(idx); + delete old; + } + break; + } + default: + ShouldNotReachHere(); + } +} + +ShenandoahCodeRootsIterator::ShenandoahCodeRootsIterator() : + _heap(ShenandoahHeap::heap()), + _par_iterator(CodeCache::heaps()), + _claimed(0) { + assert(SafepointSynchronize::is_at_safepoint(), "Must be at safepoint"); + assert(!Thread::current()->is_Worker_thread(), "Should not be acquired by workers"); + switch (ShenandoahCodeRootsStyle) { + case 0: + case 1: { + // No need to do anything here + break; + } + case 2: { + CodeCache_lock->lock_without_safepoint_check(); + break; + } + default: + ShouldNotReachHere(); + } +} + +ShenandoahCodeRootsIterator::~ShenandoahCodeRootsIterator() { + switch (ShenandoahCodeRootsStyle) { + case 0: + case 1: { + // No need to do anything here + break; + } + case 2: { + CodeCache_lock->unlock(); + break; + } + default: + ShouldNotReachHere(); + } +} + +template +void ShenandoahCodeRootsIterator::dispatch_parallel_blobs_do(CodeBlobClosure *f) { + switch (ShenandoahCodeRootsStyle) { + case 0: { + if (_seq_claimed.try_set()) { + CodeCache::blobs_do(f); + } + break; + } + case 1: { + _par_iterator.parallel_blobs_do(f); + break; + } + case 2: { + ShenandoahCodeRootsIterator::fast_parallel_blobs_do(f); + break; + } + default: + ShouldNotReachHere(); + } +} + +void ShenandoahAllCodeRootsIterator::possibly_parallel_blobs_do(CodeBlobClosure *f) { + ShenandoahCodeRootsIterator::dispatch_parallel_blobs_do(f); +} + +void ShenandoahCsetCodeRootsIterator::possibly_parallel_blobs_do(CodeBlobClosure *f) { + ShenandoahCodeRootsIterator::dispatch_parallel_blobs_do(f); +} + +template +void ShenandoahCodeRootsIterator::fast_parallel_blobs_do(CodeBlobClosure *f) { + assert(SafepointSynchronize::is_at_safepoint(), "Must be at safepoint"); + + size_t stride = 256; // educated guess + + GrowableArray* list = ShenandoahCodeRoots::_recorded_nms; + + size_t max = (size_t)list->length(); + while (_claimed < max) { + size_t cur = Atomic::add(stride, &_claimed) - stride; + size_t start = cur; + size_t end = MIN2(cur + stride, max); + if (start >= max) break; + + for (size_t idx = start; idx < end; idx++) { + ShenandoahNMethod* nmr = list->at((int) idx); + nmr->assert_alive_and_correct(); + + if (CSET_FILTER && !nmr->has_cset_oops(_heap)) { + continue; + } + + f->do_code_blob(nmr->nm()); + } + } +} + +ShenandoahNMethod::ShenandoahNMethod(nmethod* nm, GrowableArray* oops) { + _nm = nm; + _oops = NEW_C_HEAP_ARRAY(oop*, oops->length(), mtGC); + _oops_count = oops->length(); + for (int c = 0; c < _oops_count; c++) { + _oops[c] = oops->at(c); + } +} + +ShenandoahNMethod::~ShenandoahNMethod() { + if (_oops != NULL) { + FREE_C_HEAP_ARRAY(oop*, _oops); + } +} + +bool ShenandoahNMethod::has_cset_oops(ShenandoahHeap *heap) { + for (int c = 0; c < _oops_count; c++) { + oop o = RawAccess<>::oop_load(_oops[c]); + if (heap->in_collection_set(o)) { + return true; + } + } + return false; +} + +#ifdef ASSERT +void ShenandoahNMethod::assert_alive_and_correct() { + assert(_nm->is_alive(), "only alive nmethods here"); + assert(_oops_count > 0, "should have filtered nmethods without oops before"); + ShenandoahHeap* heap = ShenandoahHeap::heap(); + for (int c = 0; c < _oops_count; c++) { + oop *loc = _oops[c]; + assert(_nm->code_contains((address) loc) || _nm->oops_contains(loc), "nmethod should contain the oop*"); + oop o = RawAccess<>::oop_load(loc); + shenandoah_assert_correct_except(loc, o, + o == NULL || + heap->is_full_gc_move_in_progress() || + (VMThread::vm_operation() != NULL) && (VMThread::vm_operation()->type() == VM_Operation::VMOp_HeapWalkOperation) + ); + } +} + +void ShenandoahNMethod::assert_same_oops(GrowableArray* oops) { + assert(_oops_count == oops->length(), "should have the same number of oop*"); + for (int c = 0; c < _oops_count; c++) { + assert(_oops[c] == oops->at(c), "should be the same oop*"); + } +} +#endif