This commit is contained in:
tstuefe 2026-01-20 16:23:18 +01:00
parent 592606e9cf
commit be59b7dece
2 changed files with 115 additions and 83 deletions

View File

@ -61,6 +61,11 @@
char txt[1024]; \
stringStream ss(txt, sizeof(txt)); \
o->klass()->name()->print_value_on(&ss); \
if (o->is_objArray()) { \
const objArrayOop pointee_oa = (objArrayOop) o; \
const int array_len = pointee_oa->length(); \
ss.print(" [%d]", array_len); \
} \
printf("%s: " PTR_FORMAT " %s ", prefix, p2i(o), txt); \
printf("\n"); \
fflush(stdout); \
@ -72,6 +77,11 @@
char txt[1024]; \
stringStream ss(txt, sizeof(txt)); \
o->klass()->name()->print_value_on(&ss); \
if (o->is_objArray()) { \
const objArrayOop pointee_oa = (objArrayOop) o; \
const int array_len = pointee_oa->length(); \
ss.print(" [%d]", array_len); \
} \
printf("%s: " PTR_FORMAT " %s ", prefix, p2i(o), txt); \
printf(format, __VA_ARGS__); \
printf("\n"); \
@ -101,7 +111,9 @@ void DFSClosure::find_leaks_from_root_set(EdgeStore* edge_store,
TRC("SCANNING ROOTS");
// Mark root set, to avoid going sideways
// Mark root set, to avoid going sideways. The intent here is to prevent
// long reference chains that would be caused by tracing through multiple root
// objects.
DFSClosure dfs(edge_store, mark_bits, nullptr);
dfs._max_depth = 1;
RootSetClosure<DFSClosure> rs(&dfs);
@ -138,7 +150,10 @@ DFSClosure::DFSClosure(EdgeStore* edge_store, JFRBitSet* mark_bits, const Edge*
_max_depth(max_dfs_depth), _ignore_root_set(false),
// _probe_stack(1024, 4, max_probe_stack_elems),
_probe_stack(),
_current_item(nullptr)
_current_ref(UnifiedOopRef::encode_null()),
_current_pointee(nullptr),
_current_depth(0),
_current_chunkindex(0)
{
}
@ -150,10 +165,11 @@ DFSClosure::~DFSClosure() {
}
#endif // ASSERT
void DFSClosure::push_to_probe_stack(UnifiedOopRef ref, oop pointee, size_t depth, int chunkindex) {
void DFSClosure::probe_stack_push(UnifiedOopRef ref, oop pointee, size_t depth) {
assert(!ref.is_null(), "invariant");
if (pointee == nullptr) {
// Don't push null references
return;
}
@ -167,74 +183,100 @@ void DFSClosure::push_to_probe_stack(UnifiedOopRef ref, oop pointee, size_t dept
return;
}
ProbeStackItem item { ref, checked_cast<unsigned>(depth), chunkindex };
ProbeStackItem item { ref, checked_cast<unsigned>(depth), 0 };
_probe_stack.push(item);
TRCOOPFMT("pushed", pointee, "path depth %u, probestack depth %zu", item.depth, _probe_stack.size());
}
void DFSClosure::probe_stack_push_followup_chunk(UnifiedOopRef ref, oop pointee, size_t depth, int chunkindex) {
assert(!ref.is_null(), "invariant");
assert(pointee != nullptr, "invariant");
assert(chunkindex > 0, "invariant");
if (_probe_stack.is_full()) {
// Probe stack exhausted; see remarks about probe stack max depth above.
return;
}
ProbeStackItem item { ref, checked_cast<unsigned>(depth), chunkindex };
_probe_stack.push(item);
TRCOOPFMT("pushed", pointee, "path depth %u, probestack depth %zu, chunkindex %d (follow-up)", item.depth, _probe_stack.size(), chunkindex);
}
bool DFSClosure::probe_stack_pop() {
if (_probe_stack.is_empty()) {
_current_ref = UnifiedOopRef::encode_null();
_current_pointee = nullptr;
_current_depth = 0;
_current_chunkindex = 0;
return false;
}
ProbeStackItem item = _probe_stack.pop();
_current_ref = item.r;
assert(!_current_ref.is_null(), "invariant");
_current_depth = item.depth;
assert(_current_depth < _max_depth, "invariant");
_current_chunkindex = item.chunkindex;
assert(_current_chunkindex >= 0, "invariant");
_current_pointee = _current_ref.dereference();
TRCOOPFMT("popped", _current_pointee, "path depth %zu, probestack depth %zu, chunkindex %d%s", _current_depth, _probe_stack.size(), _current_chunkindex, _current_chunkindex > 0 ? " (followup)" : "");
return true;
}
void DFSClosure::handle_oop() {
assert(_current_item != nullptr, "Sanity");
assert(_current_item->chunkindex == 0, "Sanity");
const oop pointee = current_pointee();
const size_t depth = current_depth();
assert(depth < _max_depth, "Sanity (%zu)", depth);
TRCOOPFMT("popped", pointee, "path depth %zu, probestack depth %zu", depth, _probe_stack.size());
if (depth == 0 && _ignore_root_set) {
assert(pointee_was_visited(pointee), "We should have already visited roots");
_reference_stack[depth] = _current_item->r;
if (_current_depth == 0 && _ignore_root_set) {
assert(pointee_was_visited(_current_pointee), "We should have already visited roots");
_reference_stack[_current_depth] = _current_ref;
// continue since we want to process children, too
} else {
if (pointee_was_visited(pointee)) {
if (pointee_was_visited(_current_pointee)) {
return; // already processed
}
mark_pointee_as_visited(pointee);
_reference_stack[depth] = _current_item->r;
if (pointee_was_sampled(pointee)) {
TRC("=> SAMPLE OBJECT FOUND");
mark_pointee_as_visited(_current_pointee);
_reference_stack[_current_depth] = _current_ref;
if (pointee_was_sampled(_current_pointee)) {
TRC("=> SAMPLE OBJECT FOUND (handle_oop)");
add_chain();
}
}
// trace children if needed
if (depth == _max_depth - 1) {
if (_current_depth == _max_depth - 1) {
return; // stop following this chain
}
pointee->oop_iterate(this);
_current_pointee->oop_iterate(this);
}
void DFSClosure::handle_objarrayoop() {
assert(_current_item != nullptr, "Sanity");
ShouldNotReachHere();
const oop pointee = current_pointee();
const size_t depth = current_depth();
assert(depth < _max_depth, "Sanity");
const int chunkindex = _current_item->chunkindex;
assert(chunkindex >= 0, "Sanity");
if (current_depth() == 0 && _ignore_root_set) {
assert(pointee_was_visited(pointee), "We should have already visited roots");
_reference_stack[depth] = _current_item->r;
if (_current_depth == 0 && _ignore_root_set) {
assert(pointee_was_visited(_current_pointee), "We should have already visited roots");
_reference_stack[_current_depth] = _current_ref;
// continue since we want to process children, too
} else {
if (chunkindex == 0) {
if (_current_chunkindex == 0) {
// For the first chunk only, check, process and mark the array oop itself.
if (pointee_was_visited(pointee)) {
if (pointee_was_visited(_current_pointee)) {
return; // already processed
}
mark_pointee_as_visited(pointee);
_reference_stack[depth] = _current_item->r;
mark_pointee_as_visited(_current_pointee);
_reference_stack[_current_depth] = _current_ref;
if (pointee_was_sampled(pointee)) {
if (pointee_was_sampled(_current_pointee)) {
TRC("=> SAMPLE OBJECT FOUND (handle_objarrayoop)");
add_chain();
}
}
@ -242,62 +284,49 @@ ShouldNotReachHere();
// trace children if needed
if (depth == _max_depth - 1) {
if (_current_depth == _max_depth - 1) {
return; // stop following this chain
}
const objArrayOop pointee_oa = (objArrayOop) pointee;
const objArrayOop pointee_oa = (objArrayOop) _current_pointee;
const int array_len = pointee_oa->length();
const int begidx = chunkindex * array_chunk_size;
const int endidx = MIN2(array_len, (chunkindex + 1) * array_chunk_size);
if (array_len == 0) {
return;
}
const int begidx = _current_chunkindex * array_chunk_size;
const int endidx = MIN2(array_len, (_current_chunkindex + 1) * array_chunk_size);
assert(begidx < endidx, "invariant");
// Push follow-up chunk: same reference, same depth, next chunk index.
// Do this before pushing the child references to preserve depth-first
// traversal.
// Push follow-up chunk
if (endidx < array_len) {
push_to_probe_stack(_current_item->r, pointee, depth, chunkindex + 1);
probe_stack_push_followup_chunk(_current_ref, _current_pointee, _current_depth, _current_chunkindex + 1);
}
// push child references
if (begidx < endidx) {
pointee_oa->oop_iterate_range(this, begidx, endidx);
}
pointee_oa->oop_iterate_range(this, begidx, endidx);
}
void DFSClosure::drain_probe_stack() {
DEBUG_ONLY(unsigned last_depth = 0;)
while (!_probe_stack.is_empty() &&
!GranularTimer::is_finished()) {
while (probe_stack_pop() && !GranularTimer::is_finished()) {
const ProbeStackItem item = _probe_stack.pop();
assert(item.depth <= (last_depth + 1), "jumping nodes?");
// We should not dive downward more than 1 indirection.
assert(_current_depth <= (last_depth + 1), "invariant");
// anchor current item
_current_item = &item;
assert(!_current_item->r.is_null(), "invariant");
assert(current_pointee() != nullptr, "invariant");
//if (current_pointee()->is_objArray()) {
if (false) {
if (_current_pointee->is_objArray()) {
handle_objarrayoop();
} else {
handle_oop();
}
DEBUG_ONLY(last_depth = item.depth;)
// reset current item
_current_item = nullptr;
DEBUG_ONLY(last_depth = _current_depth;)
}
}
void DFSClosure::add_chain() {
const size_t depth = current_depth();
const size_t array_length = depth + 2;
const size_t array_length = _current_depth + 2;
ResourceMark rm;
Edge* const chain = NEW_RESOURCE_ARRAY(Edge, array_length);
@ -305,7 +334,7 @@ void DFSClosure::add_chain() {
if (UseNewCode) {
TRC("---- reference stack ----");
for (size_t i = 0; i <= depth; i++) {
for (size_t i = 0; i <= _current_depth; i++) {
const oop pointee = _reference_stack[i].dereference();
TRCOOP("", pointee);
}
@ -313,12 +342,12 @@ if (UseNewCode) {
}
// aggregate from depth-first search
for (size_t i = 0; i <= depth; i++) {
for (size_t i = 0; i <= _current_depth; i++) {
const size_t next = idx + 1;
const size_t d = depth - i;
const size_t d = _current_depth - i;
chain[idx++] = Edge(&chain[next], _reference_stack[d]);
}
assert(depth + 1 == idx, "invariant");
assert(_current_depth + 1 == idx, "invariant");
assert(array_length == idx + 1, "invariant");
// aggregate from breadth-first search
@ -334,20 +363,19 @@ void DFSClosure::do_oop(oop* ref) {
assert(ref != nullptr, "invariant");
assert(is_aligned(ref, HeapWordSize), "invariant");
const oop pointee = HeapAccess<AS_NO_KEEPALIVE>::oop_load(ref);
push_to_probe_stack(UnifiedOopRef::encode_in_heap(ref), pointee, current_depth() + 1, 0);
probe_stack_push(UnifiedOopRef::encode_in_heap(ref), pointee, _current_depth + 1);
}
void DFSClosure::do_oop(narrowOop* ref) {
assert(ref != nullptr, "invariant");
assert(is_aligned(ref, sizeof(narrowOop)), "invariant");
const oop pointee = HeapAccess<AS_NO_KEEPALIVE>::oop_load(ref);
push_to_probe_stack(UnifiedOopRef::encode_in_heap(ref), pointee, current_depth() + 1, 0);
probe_stack_push(UnifiedOopRef::encode_in_heap(ref), pointee, _current_depth + 1);
}
void DFSClosure::do_root(UnifiedOopRef ref) {
assert(!ref.is_null(), "invariant");
const oop pointee = ref.dereference();
assert(pointee != nullptr, "invariant");
assert(_current_item == nullptr, "invariant");
push_to_probe_stack(ref, pointee, 0, 0);
probe_stack_push(ref, pointee, 0);
}

View File

@ -61,9 +61,11 @@ class DFSClosure : public BasicOopIterateClosure {
};
Stack<ProbeStackItem, mtTracing> _probe_stack;
const ProbeStackItem* _current_item;
oop current_pointee() const { return _current_item->r.dereference(); }
size_t current_depth() const { return _current_item == nullptr ? 0 : _current_item->depth; }
// Walkstate
UnifiedOopRef _current_ref;
oop _current_pointee;
size_t _current_depth;
int _current_chunkindex;
bool pointee_was_visited(const oop pointee) const { return _mark_bits->is_marked(pointee); }
void mark_pointee_as_visited(const oop pointee) { _mark_bits->mark_obj(pointee); }
@ -73,7 +75,9 @@ class DFSClosure : public BasicOopIterateClosure {
void handle_oop();
void handle_objarrayoop();
void push_to_probe_stack(UnifiedOopRef ref, oop pointee, size_t depth, int chunkindex);
void probe_stack_push_followup_chunk(UnifiedOopRef ref, oop pointee, size_t depth, int chunkindex);
void probe_stack_push(UnifiedOopRef ref, oop pointee, size_t depth);
bool probe_stack_pop();
public:
virtual ReferenceIterationMode reference_iteration_mode() { return DO_FIELDS_EXCEPT_REFERENT; }