8379230: JFR: Do not store leak context edge idx in markWord

Reviewed-by: egahlin
This commit is contained in:
Markus Grönlund 2026-03-06 10:23:33 +00:00
parent ca479be747
commit 45fc141d29
2 changed files with 31 additions and 52 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2026, 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
@ -26,8 +26,10 @@
#include "jfr/leakprofiler/chains/edgeUtils.hpp"
#include "jfr/leakprofiler/sampling/objectSample.hpp"
#include "jfr/leakprofiler/utilities/unifiedOopRef.inline.hpp"
#include "jfr/recorder/service/jfrOptionSet.hpp"
#include "oops/oop.inline.hpp"
#include "runtime/safepoint.hpp"
#include "utilities/resizableHashTable.hpp"
StoredEdge::StoredEdge(const Edge* parent, UnifiedOopRef reference) : Edge(parent, reference), _gc_root_id(0), _skip_length(0) {}
@ -216,84 +218,62 @@ bool EdgeStore::put_edges(StoredEdge** previous, const Edge** current, size_t li
return nullptr == *current;
}
static GrowableArray<const StoredEdge*>* _leak_context_edges = nullptr;
typedef ResizeableHashTable<uintptr_t, const StoredEdge*, AnyObj::C_HEAP, mtTracing> SampleToLeakContextEdgeMap;
static SampleToLeakContextEdgeMap* _sample_to_leak_context_edge_map = nullptr;
EdgeStore::EdgeStore() : _edges(new EdgeHashTable(this)) {}
EdgeStore::~EdgeStore() {
assert(_edges != nullptr, "invariant");
delete _edges;
delete _leak_context_edges;
_leak_context_edges = nullptr;
delete _sample_to_leak_context_edge_map;
_sample_to_leak_context_edge_map = nullptr;
}
static int leak_context_edge_idx(const ObjectSample* sample) {
static const StoredEdge* leak_context_edge(const ObjectSample* sample) {
assert(sample != nullptr, "invariant");
return static_cast<int>(sample->object()->mark().value()) >> markWord::lock_bits;
assert(_sample_to_leak_context_edge_map != nullptr, "invariant");
const StoredEdge** edge = _sample_to_leak_context_edge_map->get(p2u(sample->object()));
return edge != nullptr ? *edge : nullptr;
}
bool EdgeStore::has_leak_context(const ObjectSample* sample) const {
const int idx = leak_context_edge_idx(sample);
if (idx == 0) {
return false;
}
assert(idx > 0, "invariant");
assert(_leak_context_edges != nullptr, "invariant");
assert(idx < _leak_context_edges->length(), "invariant");
assert(_leak_context_edges->at(idx) != nullptr, "invariant");
return true;
return _sample_to_leak_context_edge_map != nullptr && leak_context_edge(sample) != nullptr;
}
const StoredEdge* EdgeStore::get(const ObjectSample* sample) const {
assert(sample != nullptr, "invariant");
if (_leak_context_edges != nullptr) {
if (_sample_to_leak_context_edge_map != nullptr) {
assert(SafepointSynchronize::is_at_safepoint(), "invariant");
const int idx = leak_context_edge_idx(sample);
if (idx > 0) {
assert(idx < _leak_context_edges->length(), "invariant");
const StoredEdge* const edge =_leak_context_edges->at(idx);
assert(edge != nullptr, "invariant");
const StoredEdge* const edge = leak_context_edge(sample);
if (edge != nullptr) {
return edge;
}
}
return get(UnifiedOopRef::encode_in_native(sample->object_addr()));
}
#ifdef ASSERT
// max_idx to ensure idx fit in lower 32-bits of markword together with lock bits.
static constexpr const int max_idx = right_n_bits(32 - markWord::lock_bits);
static constexpr const unsigned max_map_size = max_jint >> 1;
static void store_idx_precondition(oop sample_object, int idx) {
assert(sample_object != nullptr, "invariant");
assert(sample_object->mark().is_marked(), "invariant");
assert(idx > 0, "invariant");
assert(idx <= max_idx, "invariant");
}
#endif
static void store_idx_in_markword(oop sample_object, int idx) {
DEBUG_ONLY(store_idx_precondition(sample_object, idx);)
const markWord idx_mark_word(sample_object->mark().value() | idx << markWord::lock_bits);
sample_object->set_mark(idx_mark_word);
assert(sample_object->mark().is_marked(), "must still be marked");
}
static const int initial_size = 64;
static int save(const StoredEdge* edge) {
assert(edge != nullptr, "invariant");
if (_leak_context_edges == nullptr) {
_leak_context_edges = new (mtTracing) GrowableArray<const StoredEdge*>(initial_size, mtTracing);
_leak_context_edges->append(nullptr); // next idx now at 1, for disambiguation in markword.
static inline unsigned map_size() {
assert(JfrOptionSet::old_object_queue_size() > 0, "invariant");
unsigned size = JfrOptionSet::old_object_queue_size();
size = round_up_power_of_2(size);
if (size < 1024) {
return 1024;
}
return _leak_context_edges->append(edge);
size <<= 1;
return size >= max_map_size ? max_map_size : size;
}
// We associate the leak context edge with the leak candidate object by saving the
// edge in an array and storing the array idx (shifted) into the markword of the candidate object.
static void associate_with_candidate(const StoredEdge* leak_context_edge) {
assert(leak_context_edge != nullptr, "invariant");
store_idx_in_markword(leak_context_edge->pointee(), save(leak_context_edge));
if (_sample_to_leak_context_edge_map == nullptr) {
const unsigned size = map_size();
_sample_to_leak_context_edge_map = new (mtTracing) SampleToLeakContextEdgeMap(size, size);
}
assert(_sample_to_leak_context_edge_map != nullptr, "invariant");
_sample_to_leak_context_edge_map->put(p2u(leak_context_edge->pointee()), leak_context_edge);
}
StoredEdge* EdgeStore::associate_leak_context_with_candidate(const Edge* edge) {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2014, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2026, 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
@ -39,7 +39,6 @@ class StoredEdge : public Edge {
size_t _skip_length;
public:
StoredEdge();
StoredEdge(const Edge* parent, UnifiedOopRef reference);
StoredEdge(const Edge& edge);
StoredEdge(const StoredEdge& edge);