blob: 63bede6143ba1092994fe1c4a2dfebb0373727eb [file]
/*
* 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);
}
}