Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,6 @@
#include "runtime/mutexLocker.hpp"
#include "runtime/safepoint.hpp"

const unsigned int initial_size = 431;

static JfrCHeapTraceIdSet* c_heap_allocate_set(int size = initial_size) {
return new JfrCHeapTraceIdSet(size);
}

static JfrCHeapTraceIdSet* unloaded_thread_id_set = nullptr;

class ThreadIdExclusiveAccess : public StackObj {
private:
static Semaphore _mutex_semaphore;
Expand All @@ -66,19 +58,13 @@ class ThreadIdExclusiveAccess : public StackObj {

Semaphore ThreadIdExclusiveAccess::_mutex_semaphore(1);

static bool has_thread_exited(traceid tid) {
assert(tid != 0, "invariant");
if (unloaded_thread_id_set == nullptr) {
return false;
}
ThreadIdExclusiveAccess lock;
return unloaded_thread_id_set->contains(tid);
}
static const unsigned initial_set_size = 512;
static JfrCHeapTraceIdSet* unloaded_thread_id_set = nullptr;

static void add_to_unloaded_thread_set(traceid tid) {
ThreadIdExclusiveAccess lock;
if (unloaded_thread_id_set == nullptr) {
unloaded_thread_id_set = c_heap_allocate_set();
unloaded_thread_id_set = new (mtTracing) JfrCHeapTraceIdSet(initial_set_size);
}
unloaded_thread_id_set->add(tid);
}
Expand Down Expand Up @@ -193,12 +179,6 @@ inline void BlobCache::on_unlink(BlobEntry* entry) const {
assert(entry != nullptr, "invariant");
}

static JfrResourceAreaTraceIdSet* id_set = nullptr;

static void prepare_for_resolution() {
id_set = new JfrResourceAreaTraceIdSet(initial_size);
}

static bool stack_trace_precondition(const ObjectSample* sample) {
assert(sample != nullptr, "invariant");
return sample->has_stack_trace_id() && !sample->is_dead();
Expand All @@ -213,15 +193,18 @@ static void add_to_leakp_set(const ObjectSample* sample) {
JfrTraceId::load_leakp(object->klass());
}

static JfrResourceAreaTraceIdSet* resolution_set = nullptr;

class StackTraceBlobInstaller {
private:
BlobCache _cache;
void install(ObjectSample* sample);
const JfrStackTrace* resolve(const ObjectSample* sample) const;
public:
StackTraceBlobInstaller() : _cache(JfrOptionSet::old_object_queue_size()) {
prepare_for_resolution();
resolution_set = new JfrResourceAreaTraceIdSet(initial_set_size);
}

void sample_do(ObjectSample* sample) {
if (stack_trace_precondition(sample)) {
add_to_leakp_set(sample);
Expand Down Expand Up @@ -314,8 +297,8 @@ static bool is_klass_unloaded(traceid klass_id) {

static bool is_processed(traceid method_id) {
assert(method_id != 0, "invariant");
assert(id_set != nullptr, "invariant");
return !id_set->add(method_id);
assert(resolution_set != nullptr, "invariant");
return !resolution_set->add(method_id);
}

void ObjectSampleCheckpoint::add_to_leakp_set(const InstanceKlass* ik, traceid method_id) {
Expand Down Expand Up @@ -356,7 +339,7 @@ static void write_type_set_blob(const ObjectSample* sample, JfrCheckpointWriter&

static void write_thread_blob(const ObjectSample* sample, JfrCheckpointWriter& writer) {
assert(sample->has_thread(), "invariant");
if (sample->is_virtual_thread() || has_thread_exited(sample->thread_id())) {
if (sample->is_virtual_thread() || sample->thread_exited()) {
write_blob(sample->thread(), writer);
}
}
Expand All @@ -372,13 +355,13 @@ static inline bool should_write(const JfrStackTrace* stacktrace) {
class LeakProfilerStackTraceWriter {
private:
JfrCheckpointWriter& _writer;
int _count;
unsigned _count;
public:
LeakProfilerStackTraceWriter(JfrCheckpointWriter& writer) : _writer(writer), _count(0) {
assert(_stacktrace_id_set != nullptr, "invariant");
}

int count() const { return _count; }
unsigned count() const { return _count; }

void operator()(const JfrStackTrace* stacktrace) {
if (should_write(stacktrace)) {
Expand All @@ -394,12 +377,10 @@ void ObjectSampleCheckpoint::write_stacktraces(Thread* thread) {

JfrCheckpointWriter writer(thread);
writer.write_type(TYPE_STACKTRACE);
const int64_t count_offset = writer.reserve(sizeof(u4)); // Don't know how many yet

writer.write_count(_stacktrace_id_set->size());
LeakProfilerStackTraceWriter lpstw(writer);
JfrStackTraceRepository::iterate_leakprofiler(lpstw);
assert(lpstw.count() == _stacktrace_id_set->size(), "invariant");
writer.write_count(lpstw.count(), count_offset);
}

static void write_stacktrace_blob(const ObjectSample* sample, JfrCheckpointWriter& writer) {
Expand All @@ -422,6 +403,16 @@ static void write_blobs(const ObjectSample* sample, JfrCheckpointWriter& writer)
write_type_set_blob(sample, writer);
}

static void check_if_thread_exited(const ObjectSample* sample) {
assert(sample != nullptr, "invariant");
if (sample->thread_exited() || unloaded_thread_id_set == nullptr) {
return;
}
if (unloaded_thread_id_set->contains(sample->thread_id())) {
sample->set_thread_exited();
}
}

class BlobWriter {
private:
const ObjectSampler* _sampler;
Expand All @@ -431,23 +422,36 @@ class BlobWriter {
BlobWriter(const ObjectSampler* sampler, JfrCheckpointWriter& writer, jlong last_sweep) :
_sampler(sampler), _writer(writer), _last_sweep(last_sweep) {}
void sample_do(ObjectSample* sample) {
check_if_thread_exited(sample);
if (sample->is_alive_and_older_than(_last_sweep)) {
write_blobs(sample, _writer);
}
}
};

static void delete_unloaded_thread_id_set() {
if (unloaded_thread_id_set != nullptr) {
delete unloaded_thread_id_set;
unloaded_thread_id_set = nullptr;
}
}

static void write_sample_blobs(const ObjectSampler* sampler, bool emit_all, Thread* thread) {
// sample set is predicated on time of last sweep
const jlong last_sweep = emit_all ? max_jlong : ObjectSampler::last_sweep();
JfrCheckpointWriter writer(thread, false);
BlobWriter cbw(sampler, writer, last_sweep);
ThreadIdExclusiveAccess lock;
iterate_samples(cbw, true);
delete_unloaded_thread_id_set();
}

static inline unsigned int set_size() {
const unsigned int queue_size = static_cast<unsigned int>(JfrOptionSet::old_object_queue_size());
return queue_size > initial_size ? queue_size : initial_size;
static inline unsigned stacktrace_id_set_size() {
unsigned queue_size = static_cast<unsigned>(JfrOptionSet::old_object_queue_size());
if (!is_power_of_2(queue_size)) {
queue_size = next_power_of_2(queue_size);
}
return queue_size > initial_set_size ? queue_size : initial_set_size;
}

void ObjectSampleCheckpoint::write(const ObjectSampler* sampler, EdgeStore* edge_store, bool emit_all, Thread* thread) {
Expand All @@ -456,7 +460,9 @@ void ObjectSampleCheckpoint::write(const ObjectSampler* sampler, EdgeStore* edge
assert(thread != nullptr, "invariant");
{
ResourceMark rm(thread);
_stacktrace_id_set = new JfrResourceAreaTraceIdSet(set_size());
const unsigned stacktrace_set_size = stacktrace_id_set_size();
assert(is_power_of_2(stacktrace_set_size), "invariant");
_stacktrace_id_set = new JfrResourceAreaTraceIdSet(stacktrace_set_size);
write_sample_blobs(sampler, emit_all, thread);
if (_stacktrace_id_set->is_nonempty()) {
write_stacktraces(thread);
Expand Down
13 changes: 12 additions & 1 deletion src/hotspot/share/jfr/leakprofiler/sampling/objectSample.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ class ObjectSample : public JfrCHeapObj {
size_t _heap_used_at_last_gc;
int _index;
bool _virtual_thread;
mutable bool _thread_exited;

void release_references() {
_stacktrace.~JfrBlobHandle();
Expand All @@ -82,7 +83,8 @@ class ObjectSample : public JfrCHeapObj {
_allocated(0),
_heap_used_at_last_gc(0),
_index(0),
_virtual_thread(false) {}
_virtual_thread(false),
_thread_exited(false) {}

ObjectSample* next() const {
return _next;
Expand Down Expand Up @@ -225,6 +227,15 @@ class ObjectSample : public JfrCHeapObj {
_virtual_thread = true;
}

bool thread_exited() const {
return _thread_exited;
}

void set_thread_exited() const {
assert(!_thread_exited, "invariant");
_thread_exited = true;
}

const JfrBlobHandle& type_set() const {
return _type_set;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,11 @@ static void write_metadata_blob(JfrChunkWriter& chunkwriter, JavaThread* thread)
chunkwriter.write_unbuffered(data_address, length);
}

void JfrMetadataEvent::write(JfrChunkWriter& chunkwriter) {
size_t JfrMetadataEvent::write(JfrChunkWriter& chunkwriter) {
assert(chunkwriter.is_valid(), "invariant");
check_internal_types();
if (last_metadata_id == metadata_id && chunkwriter.has_metadata()) {
return;
return 0;
}
JavaThread* const jt = JavaThread::current();
DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(jt));
Expand All @@ -87,6 +87,7 @@ void JfrMetadataEvent::write(JfrChunkWriter& chunkwriter) {
chunkwriter.write_padded_at_offset((u4)size_written, metadata_offset);
chunkwriter.set_last_metadata_offset(metadata_offset);
last_metadata_id = metadata_id;
return 1;
}

void JfrMetadataEvent::update(jbyteArray metadata) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 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
Expand Down Expand Up @@ -36,7 +36,7 @@ class JfrChunkWriter;
//
class JfrMetadataEvent : AllStatic {
public:
static void write(JfrChunkWriter& writer);
static size_t write(JfrChunkWriter& writer);
static void update(jbyteArray metadata);
};

Expand Down
13 changes: 5 additions & 8 deletions src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1048,19 +1048,15 @@ class MethodIteratorHost {
private:
MethodCallback _method_cb;
KlassCallback _klass_cb;
KlassUsedPredicate _klass_used_predicate;
MethodUsedPredicate _method_used_predicate;
MethodFlagPredicate<leakp> _method_flag_predicate;
public:
MethodIteratorHost(JfrCheckpointWriter* writer) :
_method_cb(writer, unloading(), false),
_klass_cb(writer, unloading(), false),
_klass_used_predicate(current_epoch()),
_method_used_predicate(current_epoch()),
_method_flag_predicate(current_epoch()) {}

bool operator()(KlassPtr klass) {
if (_method_used_predicate(klass)) {
if (klass->is_instance_klass()) {
const InstanceKlass* ik = InstanceKlass::cast(klass);
while (ik != nullptr) {
const int len = ik->methods()->length();
Expand All @@ -1075,7 +1071,7 @@ class MethodIteratorHost {
ik = ik->previous_versions();
}
}
return _klass_used_predicate(klass) ? _klass_cb(klass) : true;
return _klass_cb(klass);
}

int count() const { return _method_cb.count(); }
Expand Down Expand Up @@ -1280,10 +1276,11 @@ static void setup(JfrCheckpointWriter* writer, JfrCheckpointWriter* leakp_writer
_class_unload = class_unload;
_flushpoint = flushpoint;
if (_artifacts == nullptr) {
_artifacts = new JfrArtifactSet(class_unload);
_artifacts = new JfrArtifactSet(class_unload, previous_epoch());
} else {
_artifacts->initialize(class_unload);
_artifacts->initialize(class_unload, previous_epoch());
}
assert(current_epoch() || _leakp_writer != nullptr, "invariant");
assert(_artifacts != nullptr, "invariant");
assert(!_artifacts->has_klass_entries(), "invariant");
}
Expand Down
53 changes: 30 additions & 23 deletions src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,18 +29,23 @@
#include "oops/oop.inline.hpp"
#include "oops/symbol.hpp"

JfrArtifactSet::JfrArtifactSet(bool class_unload) : _symbol_table(nullptr),
_klass_list(nullptr),
_total_count(0),
_class_unload(class_unload) {
initialize(class_unload);
assert(_klass_list != nullptr, "invariant");
JfrArtifactSet::JfrArtifactSet(bool class_unload, bool previous_epoch) : _symbol_table(nullptr),
_klass_set(nullptr),
_klass_loader_set(nullptr),
_klass_loader_leakp_set(nullptr),
_total_count(0),
_class_unload(class_unload) {
initialize(class_unload, previous_epoch);
assert(!previous_epoch || _klass_loader_leakp_set != nullptr, "invariant");
assert(_klass_loader_set != nullptr, "invariant");
assert(_klass_set != nullptr, "invariant");
}

static const size_t initial_klass_list_size = 4096;
const int initial_klass_loader_set_size = 64;
static unsigned initial_klass_set_size = 4096;
static unsigned initial_klass_loader_set_size = 64;
static unsigned initial_klass_loader_leakp_set_size = 64;

void JfrArtifactSet::initialize(bool class_unload) {
void JfrArtifactSet::initialize(bool class_unload, bool previous_epoch) {
_class_unload = class_unload;
if (_symbol_table == nullptr) {
_symbol_table = JfrSymbolTable::create();
Expand All @@ -50,9 +55,11 @@ void JfrArtifactSet::initialize(bool class_unload) {
_symbol_table->set_class_unload(class_unload);
_total_count = 0;
// Resource allocations. Keep in this allocation order.
_klass_loader_leakp_set = new GrowableArray<const Klass*>(initial_klass_loader_set_size);
_klass_loader_set = new GrowableArray<const Klass*>(initial_klass_loader_set_size);
_klass_list = new GrowableArray<const Klass*>(initial_klass_list_size);
if (previous_epoch) {
_klass_loader_leakp_set = new JfrKlassSet(initial_klass_loader_leakp_set_size);
}
_klass_loader_set = new JfrKlassSet(initial_klass_loader_set_size);
_klass_set = new JfrKlassSet(initial_klass_set_size);
}

void JfrArtifactSet::clear() {
Expand Down Expand Up @@ -93,17 +100,12 @@ traceid JfrArtifactSet::mark(uintptr_t hash, const char* const str, bool leakp)
}

bool JfrArtifactSet::has_klass_entries() const {
return _klass_list->is_nonempty();
}

int JfrArtifactSet::entries() const {
return _klass_list->length();
return _klass_set->is_nonempty();
}

static inline bool not_in_set(GrowableArray<const Klass*>* set, const Klass* k) {
static inline bool not_in_set(JfrArtifactSet::JfrKlassSet* set, const Klass* k) {
assert(set != nullptr, "invariant");
assert(k != nullptr, "invariant");
return !JfrMutablePredicate<const Klass*, compare_klasses>::test(set, k);
return set->add(k);
}

bool JfrArtifactSet::should_do_cld_klass(const Klass* k, bool leakp) {
Expand All @@ -116,16 +118,21 @@ bool JfrArtifactSet::should_do_cld_klass(const Klass* k, bool leakp) {
void JfrArtifactSet::register_klass(const Klass* k) {
assert(k != nullptr, "invariant");
assert(IS_SERIALIZED(k), "invariant");
assert(_klass_list != nullptr, "invariant");
_klass_list->append(k);
assert(_klass_set != nullptr, "invariant");
_klass_set->add(k);
}

size_t JfrArtifactSet::total_count() const {
assert(_klass_set != nullptr, "invariant");
initial_klass_set_size = MAX2(initial_klass_set_size, _klass_set->table_size());
assert(_klass_loader_set != nullptr, "invariant");
initial_klass_loader_set_size = MAX2(initial_klass_loader_set_size, _klass_loader_set->table_size());
return _total_count;
}

void JfrArtifactSet::increment_checkpoint_id() {
assert(_symbol_table != nullptr, "invariant");
_symbol_table->increment_checkpoint_id();
assert(_klass_loader_leakp_set != nullptr, "invariant");
initial_klass_loader_leakp_set_size = MAX2(initial_klass_loader_leakp_set_size, _klass_loader_leakp_set->table_size());
}

Loading