| /* | |
| * Copyright (c) 2017, 2025, 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. | |
| */ | |
| #include "classfile/classLoaderData.hpp" | |
| #include "classfile/classLoaderDataGraph.hpp" | |
| #include "gc/shared/barrierSet.hpp" | |
| #include "gc/shared/barrierSetNMethod.hpp" | |
| #include "gc/shared/gc_globals.hpp" | |
| #include "gc/shared/taskqueue.inline.hpp" | |
| #include "gc/z/zAddress.inline.hpp" | |
| #include "gc/z/zCollectedHeap.hpp" | |
| #include "gc/z/zGenerationId.hpp" | |
| #include "gc/z/zGlobals.hpp" | |
| #include "gc/z/zGranuleMap.inline.hpp" | |
| #include "gc/z/zHeap.inline.hpp" | |
| #include "gc/z/zHeapIterator.hpp" | |
| #include "gc/z/zLock.inline.hpp" | |
| #include "gc/z/zNMethod.hpp" | |
| #include "memory/iterator.inline.hpp" | |
| #include "utilities/bitMap.inline.hpp" | |
| class ZHeapIteratorBitMap : public CHeapObj<mtGC> { | |
| private: | |
| CHeapBitMap _bitmap; | |
| public: | |
| ZHeapIteratorBitMap(size_t size_in_bits) | |
| : _bitmap(size_in_bits, mtGC) {} | |
| bool try_set_bit(size_t index) { | |
| return _bitmap.par_set_bit(index); | |
| } | |
| }; | |
| class ZHeapIteratorContext { | |
| private: | |
| ObjectClosure* const _object_cl; | |
| OopFieldClosure* const _field_cl; | |
| const uint _worker_id; | |
| ZHeapIteratorQueue* const _queue; | |
| ZHeapIteratorArrayChunkQueue* const _array_chunk_queue; | |
| public: | |
| ZHeapIteratorContext(ObjectClosure* object_cl, | |
| OopFieldClosure* field_cl, | |
| uint worker_id, | |
| ZHeapIteratorQueue* queue, | |
| ZHeapIteratorArrayChunkQueue* array_chunk_queue) | |
| : _object_cl(object_cl), | |
| _field_cl(field_cl), | |
| _worker_id(worker_id), | |
| _queue(queue), | |
| _array_chunk_queue(array_chunk_queue) {} | |
| uint worker_id() const { | |
| return _worker_id; | |
| } | |
| void visit_field(oop base, oop* p) const { | |
| if (_field_cl != nullptr) { | |
| _field_cl->do_field(base, p); | |
| } | |
| } | |
| void visit_object(oop obj) const { | |
| _object_cl->do_object(obj); | |
| } | |
| void push(oop obj) const { | |
| _queue->push(obj); | |
| } | |
| void push_array_chunk(const ObjArrayTask& array_chunk) const { | |
| _array_chunk_queue->push(array_chunk); | |
| } | |
| bool pop(oop& obj) const { | |
| return _queue->pop_overflow(obj) || _queue->pop_local(obj); | |
| } | |
| bool pop_array_chunk(ObjArrayTask& array_chunk) const { | |
| return _array_chunk_queue->pop_overflow(array_chunk) || _array_chunk_queue->pop_local(array_chunk); | |
| } | |
| bool is_drained() const { | |
| return _queue->is_empty() && _array_chunk_queue->is_empty(); | |
| } | |
| }; | |
| template <bool Weak> | |
| class ZHeapIteratorColoredRootOopClosure : public OopClosure { | |
| private: | |
| ZHeapIterator* const _iter; | |
| const ZHeapIteratorContext& _context; | |
| oop load_oop(oop* p) { | |
| if (Weak) { | |
| return NativeAccess<AS_NO_KEEPALIVE | ON_PHANTOM_OOP_REF>::oop_load(p); | |
| } | |
| return NativeAccess<AS_NO_KEEPALIVE>::oop_load(p); | |
| } | |
| public: | |
| ZHeapIteratorColoredRootOopClosure(ZHeapIterator* iter, | |
| const ZHeapIteratorContext& context) | |
| : _iter(iter), | |
| _context(context) {} | |
| virtual void do_oop(oop* p) { | |
| _context.visit_field(nullptr, p); | |
| const oop obj = load_oop(p); | |
| _iter->mark_visit_and_push(_context, obj); | |
| } | |
| virtual void do_oop(narrowOop* p) { | |
| ShouldNotReachHere(); | |
| } | |
| }; | |
| class ZHeapIteratorUncoloredRootOopClosure : public OopClosure { | |
| private: | |
| ZHeapIterator* const _iter; | |
| const ZHeapIteratorContext& _context; | |
| oop load_oop(oop* p) { | |
| const oop o = Atomic::load(p); | |
| check_is_valid_zaddress(o); | |
| return RawAccess<>::oop_load(p); | |
| } | |
| public: | |
| ZHeapIteratorUncoloredRootOopClosure(ZHeapIterator* iter, | |
| const ZHeapIteratorContext& context) | |
| : _iter(iter), | |
| _context(context) {} | |
| virtual void do_oop(oop* p) { | |
| _context.visit_field(nullptr, p); | |
| const oop obj = load_oop(p); | |
| _iter->mark_visit_and_push(_context, obj); | |
| } | |
| virtual void do_oop(narrowOop* p) { | |
| ShouldNotReachHere(); | |
| } | |
| }; | |
| class ZHeapIteratorCLDOopClosure : public OopClosure { | |
| private: | |
| ZHeapIterator* const _iter; | |
| const ZHeapIteratorContext& _context; | |
| oop load_oop(oop* p) { | |
| assert(!ZCollectedHeap::heap()->is_in(p), "Should not be in heap"); | |
| return NativeAccess<AS_NO_KEEPALIVE>::oop_load(p); | |
| } | |
| public: | |
| ZHeapIteratorCLDOopClosure(ZHeapIterator* iter, | |
| const ZHeapIteratorContext& context) | |
| : _iter(iter), | |
| _context(context) {} | |
| virtual void do_oop(oop* p) { | |
| const oop obj = load_oop(p); | |
| _iter->mark_visit_and_push(_context, obj); | |
| } | |
| virtual void do_oop(narrowOop* p) { | |
| ShouldNotReachHere(); | |
| } | |
| }; | |
| template <bool VisitReferents> | |
| class ZHeapIteratorOopClosure : public OopIterateClosure { | |
| private: | |
| ZHeapIterator* const _iter; | |
| const ZHeapIteratorContext& _context; | |
| const oop _base; | |
| oop load_oop(oop* p) { | |
| assert(ZCollectedHeap::heap()->is_in(p), "Should be in heap"); | |
| if (VisitReferents) { | |
| return HeapAccess<AS_NO_KEEPALIVE | ON_UNKNOWN_OOP_REF>::oop_load_at(_base, (ptrdiff_t)_base->field_offset(p)); | |
| } | |
| return HeapAccess<AS_NO_KEEPALIVE>::oop_load(p); | |
| } | |
| public: | |
| ZHeapIteratorOopClosure(ZHeapIterator* iter, | |
| const ZHeapIteratorContext& context, | |
| oop base) | |
| : OopIterateClosure(), | |
| _iter(iter), | |
| _context(context), | |
| _base(base) {} | |
| virtual ReferenceIterationMode reference_iteration_mode() { | |
| return VisitReferents ? DO_FIELDS : DO_FIELDS_EXCEPT_REFERENT; | |
| } | |
| virtual void do_oop(oop* p) { | |
| _context.visit_field(_base, p); | |
| const oop obj = load_oop(p); | |
| _iter->mark_visit_and_push(_context, obj); | |
| } | |
| virtual void do_oop(narrowOop* p) { | |
| ShouldNotReachHere(); | |
| } | |
| virtual bool do_metadata() { | |
| return true; | |
| } | |
| virtual void do_klass(Klass* k) { | |
| ClassLoaderData* const cld = k->class_loader_data(); | |
| ZHeapIteratorOopClosure::do_cld(cld); | |
| } | |
| virtual void do_cld(ClassLoaderData* cld) { | |
| ZHeapIteratorCLDOopClosure cl(_iter, _context); | |
| cld->oops_do(&cl, ClassLoaderData::_claim_other); | |
| } | |
| // Don't follow loom stack metadata; it's already followed in other ways through CLDs | |
| virtual void do_nmethod(nmethod* nm) {} | |
| virtual void do_method(Method* m) {} | |
| }; | |
| ZHeapIterator::ZHeapIterator(uint nworkers, | |
| bool visit_weaks, | |
| bool for_verify) | |
| : _visit_weaks(visit_weaks), | |
| _for_verify(for_verify), | |
| _bitmaps(ZAddressOffsetMax), | |
| _bitmaps_lock(), | |
| _queues(nworkers), | |
| _array_chunk_queues(nworkers), | |
| _roots_colored(ZGenerationIdOptional::none), | |
| _roots_uncolored(ZGenerationIdOptional::none), | |
| _roots_weak_colored(ZGenerationIdOptional::none), | |
| _terminator(nworkers, &_queues) { | |
| // Create queues | |
| for (uint i = 0; i < _queues.size(); i++) { | |
| ZHeapIteratorQueue* const queue = new ZHeapIteratorQueue(); | |
| _queues.register_queue(i, queue); | |
| } | |
| // Create array chunk queues | |
| for (uint i = 0; i < _array_chunk_queues.size(); i++) { | |
| ZHeapIteratorArrayChunkQueue* const array_chunk_queue = new ZHeapIteratorArrayChunkQueue(); | |
| _array_chunk_queues.register_queue(i, array_chunk_queue); | |
| } | |
| } | |
| ZHeapIterator::~ZHeapIterator() { | |
| // Destroy bitmaps | |
| ZHeapIteratorBitMapsIterator iter(&_bitmaps); | |
| for (ZHeapIteratorBitMap* bitmap; iter.next(&bitmap);) { | |
| delete bitmap; | |
| } | |
| // Destroy array chunk queues | |
| for (uint i = 0; i < _array_chunk_queues.size(); i++) { | |
| delete _array_chunk_queues.queue(i); | |
| } | |
| // Destroy queues | |
| for (uint i = 0; i < _queues.size(); i++) { | |
| delete _queues.queue(i); | |
| } | |
| // Clear claimed CLD bits | |
| ClassLoaderDataGraph::clear_claimed_marks(ClassLoaderData::_claim_other); | |
| } | |
| static size_t object_index_max() { | |
| return ZGranuleSize >> ZObjectAlignmentSmallShift; | |
| } | |
| static size_t object_index(oop obj) { | |
| const zaddress addr = to_zaddress(obj); | |
| const zoffset offset = ZAddress::offset(addr); | |
| const uintptr_t mask = ZGranuleSize - 1; | |
| return (untype(offset) & mask) >> ZObjectAlignmentSmallShift; | |
| } | |
| ZHeapIteratorBitMap* ZHeapIterator::object_bitmap(oop obj) { | |
| const zoffset offset = ZAddress::offset(to_zaddress(obj)); | |
| ZHeapIteratorBitMap* bitmap = _bitmaps.get_acquire(offset); | |
| if (bitmap == nullptr) { | |
| ZLocker<ZLock> locker(&_bitmaps_lock); | |
| bitmap = _bitmaps.get(offset); | |
| if (bitmap == nullptr) { | |
| // Install new bitmap | |
| bitmap = new ZHeapIteratorBitMap(object_index_max()); | |
| _bitmaps.release_put(offset, bitmap); | |
| } | |
| } | |
| return bitmap; | |
| } | |
| bool ZHeapIterator::should_visit_object_at_mark() const { | |
| // Verify wants to visit objects as soon as they are found. | |
| return _for_verify; | |
| } | |
| bool ZHeapIterator::should_visit_object_at_follow() const { | |
| // Non-verify code needs to be careful and visit the objects | |
| // during the follow stage, where we've completed the root | |
| // iteration. This prevents lock-ordering problems between | |
| // the root iterator and the visit closures. | |
| return !_for_verify; | |
| } | |
| bool ZHeapIterator::mark_object(oop obj) { | |
| if (obj == nullptr) { | |
| return false; | |
| } | |
| ZHeapIteratorBitMap* const bitmap = object_bitmap(obj); | |
| const size_t index = object_index(obj); | |
| return bitmap->try_set_bit(index); | |
| } | |
| typedef ClaimingCLDToOopClosure<ClassLoaderData::_claim_other> ZHeapIteratorCLDClosure; | |
| class ZHeapIteratorNMethodClosure : public NMethodClosure { | |
| private: | |
| OopClosure* const _cl; | |
| BarrierSetNMethod* const _bs_nm; | |
| public: | |
| ZHeapIteratorNMethodClosure(OopClosure* cl) | |
| : _cl(cl), | |
| _bs_nm(BarrierSet::barrier_set()->barrier_set_nmethod()) {} | |
| virtual void do_nmethod(nmethod* nm) { | |
| // If ClassUnloading is turned off, all nmethods are considered strong, | |
| // not only those on the call stacks. The heap iteration might happen | |
| // before the concurrent processign of the code cache, make sure that | |
| // all nmethods have been processed before visiting the oops. | |
| _bs_nm->nmethod_entry_barrier(nm); | |
| ZNMethod::nmethod_oops_do(nm, _cl); | |
| } | |
| }; | |
| class ZHeapIteratorThreadClosure : public ThreadClosure { | |
| private: | |
| OopClosure* const _cl; | |
| NMethodClosure* const _nm_cl; | |
| public: | |
| ZHeapIteratorThreadClosure(OopClosure* cl, NMethodClosure* nm_cl) | |
| : _cl(cl), | |
| _nm_cl(nm_cl) {} | |
| void do_thread(Thread* thread) { | |
| thread->oops_do(_cl, _nm_cl); | |
| } | |
| }; | |
| void ZHeapIterator::push_strong_roots(const ZHeapIteratorContext& context) { | |
| { | |
| ZHeapIteratorColoredRootOopClosure<false /* Weak */> cl(this, context); | |
| ZHeapIteratorCLDClosure cld_cl(&cl); | |
| _roots_colored.apply(&cl, | |
| &cld_cl); | |
| } | |
| { | |
| ZHeapIteratorUncoloredRootOopClosure cl(this, context); | |
| ZHeapIteratorNMethodClosure nm_cl(&cl); | |
| ZHeapIteratorThreadClosure thread_cl(&cl, &nm_cl); | |
| _roots_uncolored.apply(&thread_cl, | |
| &nm_cl); | |
| } | |
| } | |
| void ZHeapIterator::push_weak_roots(const ZHeapIteratorContext& context) { | |
| ZHeapIteratorColoredRootOopClosure<true /* Weak */> cl(this, context); | |
| _roots_weak_colored.apply(&cl); | |
| } | |
| template <bool VisitWeaks> | |
| void ZHeapIterator::push_roots(const ZHeapIteratorContext& context) { | |
| push_strong_roots(context); | |
| if (VisitWeaks) { | |
| push_weak_roots(context); | |
| } | |
| } | |
| void ZHeapIterator::mark_visit_and_push(const ZHeapIteratorContext& context, oop obj) { | |
| if (mark_object(obj)) { | |
| if (should_visit_object_at_mark()) { | |
| context.visit_object(obj); | |
| } | |
| context.push(obj); | |
| } | |
| } | |
| template <bool VisitReferents> | |
| void ZHeapIterator::follow_object(const ZHeapIteratorContext& context, oop obj) { | |
| ZHeapIteratorOopClosure<VisitReferents> cl(this, context, obj); | |
| ZIterator::oop_iterate(obj, &cl); | |
| } | |
| void ZHeapIterator::follow_array(const ZHeapIteratorContext& context, oop obj) { | |
| // Follow klass | |
| ZHeapIteratorOopClosure<false /* VisitReferents */> cl(this, context, obj); | |
| cl.do_klass(obj->klass()); | |
| // Push array chunk | |
| context.push_array_chunk(ObjArrayTask(obj, 0 /* index */)); | |
| } | |
| void ZHeapIterator::follow_array_chunk(const ZHeapIteratorContext& context, const ObjArrayTask& array) { | |
| const objArrayOop obj = objArrayOop(array.obj()); | |
| const int length = obj->length(); | |
| const int start = array.index(); | |
| const int stride = MIN2<int>(length - start, (int)ObjArrayMarkingStride); | |
| const int end = start + stride; | |
| // Push remaining array chunk first | |
| if (end < length) { | |
| context.push_array_chunk(ObjArrayTask(obj, end)); | |
| } | |
| // Follow array chunk | |
| ZHeapIteratorOopClosure<false /* VisitReferents */> cl(this, context, obj); | |
| ZIterator::oop_iterate_range(obj, &cl, start, end); | |
| } | |
| template <bool VisitWeaks> | |
| void ZHeapIterator::follow(const ZHeapIteratorContext& context, oop obj) { | |
| // Follow | |
| if (obj->is_objArray()) { | |
| follow_array(context, obj); | |
| } else { | |
| follow_object<VisitWeaks>(context, obj); | |
| } | |
| } | |
| template <bool VisitWeaks> | |
| void ZHeapIterator::visit_and_follow(const ZHeapIteratorContext& context, oop obj) { | |
| if (should_visit_object_at_follow()) { | |
| context.visit_object(obj); | |
| } | |
| follow<VisitWeaks>(context, obj); | |
| } | |
| template <bool VisitWeaks> | |
| void ZHeapIterator::drain(const ZHeapIteratorContext& context) { | |
| ObjArrayTask array; | |
| oop obj; | |
| do { | |
| while (context.pop(obj)) { | |
| visit_and_follow<VisitWeaks>(context, obj); | |
| } | |
| if (context.pop_array_chunk(array)) { | |
| follow_array_chunk(context, array); | |
| } | |
| } while (!context.is_drained()); | |
| } | |
| template <bool VisitWeaks> | |
| void ZHeapIterator::steal(const ZHeapIteratorContext& context) { | |
| ObjArrayTask array; | |
| oop obj; | |
| if (steal_array_chunk(context, array)) { | |
| follow_array_chunk(context, array); | |
| } else if (steal(context, obj)) { | |
| visit_and_follow<VisitWeaks>(context, obj); | |
| } | |
| } | |
| bool ZHeapIterator::steal(const ZHeapIteratorContext& context, oop& obj) { | |
| return _queues.steal(context.worker_id(), obj); | |
| } | |
| bool ZHeapIterator::steal_array_chunk(const ZHeapIteratorContext& context, ObjArrayTask& array) { | |
| return _array_chunk_queues.steal(context.worker_id(), array); | |
| } | |
| template <bool VisitWeaks> | |
| void ZHeapIterator::drain_and_steal(const ZHeapIteratorContext& context) { | |
| do { | |
| drain<VisitWeaks>(context); | |
| steal<VisitWeaks>(context); | |
| } while (!context.is_drained() || !_terminator.offer_termination()); | |
| } | |
| template <bool VisitWeaks> | |
| void ZHeapIterator::object_iterate_inner(const ZHeapIteratorContext& context) { | |
| push_roots<VisitWeaks>(context); | |
| drain_and_steal<VisitWeaks>(context); | |
| } | |
| void ZHeapIterator::object_iterate(ObjectClosure* object_cl, uint worker_id) { | |
| object_and_field_iterate(object_cl, nullptr /* field_cl */, worker_id); | |
| } | |
| void ZHeapIterator::object_and_field_iterate(ObjectClosure* object_cl, OopFieldClosure* field_cl, uint worker_id) { | |
| const ZHeapIteratorContext context(object_cl, | |
| field_cl, | |
| worker_id, | |
| _queues.queue(worker_id), | |
| _array_chunk_queues.queue(worker_id)); | |
| if (_visit_weaks) { | |
| object_iterate_inner<true /* VisitWeaks */>(context); | |
| } else { | |
| object_iterate_inner<false /* VisitWeaks */>(context); | |
| } | |
| } |