Skip to content

Commit 5856dc3

Browse files
author
Markus Grönlund
committed
8365199: Use a set instead of a list as the intermediary Klass* storage to reduce typeset processing
Reviewed-by: egahlin
1 parent fa2eb61 commit 5856dc3

File tree

11 files changed

+276
-175
lines changed

11 files changed

+276
-175
lines changed

src/hotspot/share/jfr/leakprofiler/checkpoint/objectSampleCheckpoint.cpp

Lines changed: 42 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -48,14 +48,6 @@
4848
#include "runtime/mutexLocker.hpp"
4949
#include "runtime/safepoint.hpp"
5050

51-
const unsigned int initial_size = 431;
52-
53-
static JfrCHeapTraceIdSet* c_heap_allocate_set(int size = initial_size) {
54-
return new JfrCHeapTraceIdSet(size);
55-
}
56-
57-
static JfrCHeapTraceIdSet* unloaded_thread_id_set = nullptr;
58-
5951
class ThreadIdExclusiveAccess : public StackObj {
6052
private:
6153
static Semaphore _mutex_semaphore;
@@ -66,19 +58,13 @@ class ThreadIdExclusiveAccess : public StackObj {
6658

6759
Semaphore ThreadIdExclusiveAccess::_mutex_semaphore(1);
6860

69-
static bool has_thread_exited(traceid tid) {
70-
assert(tid != 0, "invariant");
71-
if (unloaded_thread_id_set == nullptr) {
72-
return false;
73-
}
74-
ThreadIdExclusiveAccess lock;
75-
return unloaded_thread_id_set->contains(tid);
76-
}
61+
static const unsigned initial_set_size = 512;
62+
static JfrCHeapTraceIdSet* unloaded_thread_id_set = nullptr;
7763

7864
static void add_to_unloaded_thread_set(traceid tid) {
7965
ThreadIdExclusiveAccess lock;
8066
if (unloaded_thread_id_set == nullptr) {
81-
unloaded_thread_id_set = c_heap_allocate_set();
67+
unloaded_thread_id_set = new (mtTracing) JfrCHeapTraceIdSet(initial_set_size);
8268
}
8369
unloaded_thread_id_set->add(tid);
8470
}
@@ -193,12 +179,6 @@ inline void BlobCache::on_unlink(BlobEntry* entry) const {
193179
assert(entry != nullptr, "invariant");
194180
}
195181

196-
static JfrResourceAreaTraceIdSet* id_set = nullptr;
197-
198-
static void prepare_for_resolution() {
199-
id_set = new JfrResourceAreaTraceIdSet(initial_size);
200-
}
201-
202182
static bool stack_trace_precondition(const ObjectSample* sample) {
203183
assert(sample != nullptr, "invariant");
204184
return sample->has_stack_trace_id() && !sample->is_dead();
@@ -213,15 +193,18 @@ static void add_to_leakp_set(const ObjectSample* sample) {
213193
JfrTraceId::load_leakp(object->klass());
214194
}
215195

196+
static JfrResourceAreaTraceIdSet* resolution_set = nullptr;
197+
216198
class StackTraceBlobInstaller {
217199
private:
218200
BlobCache _cache;
219201
void install(ObjectSample* sample);
220202
const JfrStackTrace* resolve(const ObjectSample* sample) const;
221203
public:
222204
StackTraceBlobInstaller() : _cache(JfrOptionSet::old_object_queue_size()) {
223-
prepare_for_resolution();
205+
resolution_set = new JfrResourceAreaTraceIdSet(initial_set_size);
224206
}
207+
225208
void sample_do(ObjectSample* sample) {
226209
if (stack_trace_precondition(sample)) {
227210
add_to_leakp_set(sample);
@@ -314,8 +297,8 @@ static bool is_klass_unloaded(traceid klass_id) {
314297

315298
static bool is_processed(traceid method_id) {
316299
assert(method_id != 0, "invariant");
317-
assert(id_set != nullptr, "invariant");
318-
return !id_set->add(method_id);
300+
assert(resolution_set != nullptr, "invariant");
301+
return !resolution_set->add(method_id);
319302
}
320303

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

357340
static void write_thread_blob(const ObjectSample* sample, JfrCheckpointWriter& writer) {
358341
assert(sample->has_thread(), "invariant");
359-
if (sample->is_virtual_thread() || has_thread_exited(sample->thread_id())) {
342+
if (sample->is_virtual_thread() || sample->thread_exited()) {
360343
write_blob(sample->thread(), writer);
361344
}
362345
}
@@ -372,13 +355,13 @@ static inline bool should_write(const JfrStackTrace* stacktrace) {
372355
class LeakProfilerStackTraceWriter {
373356
private:
374357
JfrCheckpointWriter& _writer;
375-
int _count;
358+
unsigned _count;
376359
public:
377360
LeakProfilerStackTraceWriter(JfrCheckpointWriter& writer) : _writer(writer), _count(0) {
378361
assert(_stacktrace_id_set != nullptr, "invariant");
379362
}
380363

381-
int count() const { return _count; }
364+
unsigned count() const { return _count; }
382365

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

395378
JfrCheckpointWriter writer(thread);
396379
writer.write_type(TYPE_STACKTRACE);
397-
const int64_t count_offset = writer.reserve(sizeof(u4)); // Don't know how many yet
398-
380+
writer.write_count(_stacktrace_id_set->size());
399381
LeakProfilerStackTraceWriter lpstw(writer);
400382
JfrStackTraceRepository::iterate_leakprofiler(lpstw);
401383
assert(lpstw.count() == _stacktrace_id_set->size(), "invariant");
402-
writer.write_count(lpstw.count(), count_offset);
403384
}
404385

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

406+
static void check_if_thread_exited(const ObjectSample* sample) {
407+
assert(sample != nullptr, "invariant");
408+
if (sample->thread_exited() || unloaded_thread_id_set == nullptr) {
409+
return;
410+
}
411+
if (unloaded_thread_id_set->contains(sample->thread_id())) {
412+
sample->set_thread_exited();
413+
}
414+
}
415+
425416
class BlobWriter {
426417
private:
427418
const ObjectSampler* _sampler;
@@ -431,23 +422,36 @@ class BlobWriter {
431422
BlobWriter(const ObjectSampler* sampler, JfrCheckpointWriter& writer, jlong last_sweep) :
432423
_sampler(sampler), _writer(writer), _last_sweep(last_sweep) {}
433424
void sample_do(ObjectSample* sample) {
425+
check_if_thread_exited(sample);
434426
if (sample->is_alive_and_older_than(_last_sweep)) {
435427
write_blobs(sample, _writer);
436428
}
437429
}
438430
};
439431

432+
static void delete_unloaded_thread_id_set() {
433+
if (unloaded_thread_id_set != nullptr) {
434+
delete unloaded_thread_id_set;
435+
unloaded_thread_id_set = nullptr;
436+
}
437+
}
438+
440439
static void write_sample_blobs(const ObjectSampler* sampler, bool emit_all, Thread* thread) {
441440
// sample set is predicated on time of last sweep
442441
const jlong last_sweep = emit_all ? max_jlong : ObjectSampler::last_sweep();
443442
JfrCheckpointWriter writer(thread, false);
444443
BlobWriter cbw(sampler, writer, last_sweep);
444+
ThreadIdExclusiveAccess lock;
445445
iterate_samples(cbw, true);
446+
delete_unloaded_thread_id_set();
446447
}
447448

448-
static inline unsigned int set_size() {
449-
const unsigned int queue_size = static_cast<unsigned int>(JfrOptionSet::old_object_queue_size());
450-
return queue_size > initial_size ? queue_size : initial_size;
449+
static inline unsigned stacktrace_id_set_size() {
450+
unsigned queue_size = static_cast<unsigned>(JfrOptionSet::old_object_queue_size());
451+
if (!is_power_of_2(queue_size)) {
452+
queue_size = next_power_of_2(queue_size);
453+
}
454+
return queue_size > initial_set_size ? queue_size : initial_set_size;
451455
}
452456

453457
void ObjectSampleCheckpoint::write(const ObjectSampler* sampler, EdgeStore* edge_store, bool emit_all, Thread* thread) {
@@ -456,7 +460,9 @@ void ObjectSampleCheckpoint::write(const ObjectSampler* sampler, EdgeStore* edge
456460
assert(thread != nullptr, "invariant");
457461
{
458462
ResourceMark rm(thread);
459-
_stacktrace_id_set = new JfrResourceAreaTraceIdSet(set_size());
463+
const unsigned stacktrace_set_size = stacktrace_id_set_size();
464+
assert(is_power_of_2(stacktrace_set_size), "invariant");
465+
_stacktrace_id_set = new JfrResourceAreaTraceIdSet(stacktrace_set_size);
460466
write_sample_blobs(sampler, emit_all, thread);
461467
if (_stacktrace_id_set->is_nonempty()) {
462468
write_stacktraces(thread);

src/hotspot/share/jfr/leakprofiler/sampling/objectSample.hpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ class ObjectSample : public JfrCHeapObj {
5959
size_t _heap_used_at_last_gc;
6060
int _index;
6161
bool _virtual_thread;
62+
mutable bool _thread_exited;
6263

6364
void release_references() {
6465
_stacktrace.~JfrBlobHandle();
@@ -82,7 +83,8 @@ class ObjectSample : public JfrCHeapObj {
8283
_allocated(0),
8384
_heap_used_at_last_gc(0),
8485
_index(0),
85-
_virtual_thread(false) {}
86+
_virtual_thread(false),
87+
_thread_exited(false) {}
8688

8789
ObjectSample* next() const {
8890
return _next;
@@ -225,6 +227,15 @@ class ObjectSample : public JfrCHeapObj {
225227
_virtual_thread = true;
226228
}
227229

230+
bool thread_exited() const {
231+
return _thread_exited;
232+
}
233+
234+
void set_thread_exited() const {
235+
assert(!_thread_exited, "invariant");
236+
_thread_exited = true;
237+
}
238+
228239
const JfrBlobHandle& type_set() const {
229240
return _type_set;
230241
}

src/hotspot/share/jfr/recorder/checkpoint/jfrMetadataEvent.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,11 +64,11 @@ static void write_metadata_blob(JfrChunkWriter& chunkwriter, JavaThread* thread)
6464
chunkwriter.write_unbuffered(data_address, length);
6565
}
6666

67-
void JfrMetadataEvent::write(JfrChunkWriter& chunkwriter) {
67+
size_t JfrMetadataEvent::write(JfrChunkWriter& chunkwriter) {
6868
assert(chunkwriter.is_valid(), "invariant");
6969
check_internal_types();
7070
if (last_metadata_id == metadata_id && chunkwriter.has_metadata()) {
71-
return;
71+
return 0;
7272
}
7373
JavaThread* const jt = JavaThread::current();
7474
DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(jt));
@@ -87,6 +87,7 @@ void JfrMetadataEvent::write(JfrChunkWriter& chunkwriter) {
8787
chunkwriter.write_padded_at_offset((u4)size_written, metadata_offset);
8888
chunkwriter.set_last_metadata_offset(metadata_offset);
8989
last_metadata_id = metadata_id;
90+
return 1;
9091
}
9192

9293
void JfrMetadataEvent::update(jbyteArray metadata) {

src/hotspot/share/jfr/recorder/checkpoint/jfrMetadataEvent.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2016, 2022, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -36,7 +36,7 @@ class JfrChunkWriter;
3636
//
3737
class JfrMetadataEvent : AllStatic {
3838
public:
39-
static void write(JfrChunkWriter& writer);
39+
static size_t write(JfrChunkWriter& writer);
4040
static void update(jbyteArray metadata);
4141
};
4242

src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSet.cpp

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1048,19 +1048,15 @@ class MethodIteratorHost {
10481048
private:
10491049
MethodCallback _method_cb;
10501050
KlassCallback _klass_cb;
1051-
KlassUsedPredicate _klass_used_predicate;
1052-
MethodUsedPredicate _method_used_predicate;
10531051
MethodFlagPredicate<leakp> _method_flag_predicate;
10541052
public:
10551053
MethodIteratorHost(JfrCheckpointWriter* writer) :
10561054
_method_cb(writer, unloading(), false),
10571055
_klass_cb(writer, unloading(), false),
1058-
_klass_used_predicate(current_epoch()),
1059-
_method_used_predicate(current_epoch()),
10601056
_method_flag_predicate(current_epoch()) {}
10611057

10621058
bool operator()(KlassPtr klass) {
1063-
if (_method_used_predicate(klass)) {
1059+
if (klass->is_instance_klass()) {
10641060
const InstanceKlass* ik = InstanceKlass::cast(klass);
10651061
while (ik != nullptr) {
10661062
const int len = ik->methods()->length();
@@ -1075,7 +1071,7 @@ class MethodIteratorHost {
10751071
ik = ik->previous_versions();
10761072
}
10771073
}
1078-
return _klass_used_predicate(klass) ? _klass_cb(klass) : true;
1074+
return _klass_cb(klass);
10791075
}
10801076

10811077
int count() const { return _method_cb.count(); }
@@ -1280,10 +1276,11 @@ static void setup(JfrCheckpointWriter* writer, JfrCheckpointWriter* leakp_writer
12801276
_class_unload = class_unload;
12811277
_flushpoint = flushpoint;
12821278
if (_artifacts == nullptr) {
1283-
_artifacts = new JfrArtifactSet(class_unload);
1279+
_artifacts = new JfrArtifactSet(class_unload, previous_epoch());
12841280
} else {
1285-
_artifacts->initialize(class_unload);
1281+
_artifacts->initialize(class_unload, previous_epoch());
12861282
}
1283+
assert(current_epoch() || _leakp_writer != nullptr, "invariant");
12871284
assert(_artifacts != nullptr, "invariant");
12881285
assert(!_artifacts->has_klass_entries(), "invariant");
12891286
}

src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.cpp

Lines changed: 30 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -29,18 +29,23 @@
2929
#include "oops/oop.inline.hpp"
3030
#include "oops/symbol.hpp"
3131

32-
JfrArtifactSet::JfrArtifactSet(bool class_unload) : _symbol_table(nullptr),
33-
_klass_list(nullptr),
34-
_total_count(0),
35-
_class_unload(class_unload) {
36-
initialize(class_unload);
37-
assert(_klass_list != nullptr, "invariant");
32+
JfrArtifactSet::JfrArtifactSet(bool class_unload, bool previous_epoch) : _symbol_table(nullptr),
33+
_klass_set(nullptr),
34+
_klass_loader_set(nullptr),
35+
_klass_loader_leakp_set(nullptr),
36+
_total_count(0),
37+
_class_unload(class_unload) {
38+
initialize(class_unload, previous_epoch);
39+
assert(!previous_epoch || _klass_loader_leakp_set != nullptr, "invariant");
40+
assert(_klass_loader_set != nullptr, "invariant");
41+
assert(_klass_set != nullptr, "invariant");
3842
}
3943

40-
static const size_t initial_klass_list_size = 4096;
41-
const int initial_klass_loader_set_size = 64;
44+
static unsigned initial_klass_set_size = 4096;
45+
static unsigned initial_klass_loader_set_size = 64;
46+
static unsigned initial_klass_loader_leakp_set_size = 64;
4247

43-
void JfrArtifactSet::initialize(bool class_unload) {
48+
void JfrArtifactSet::initialize(bool class_unload, bool previous_epoch) {
4449
_class_unload = class_unload;
4550
if (_symbol_table == nullptr) {
4651
_symbol_table = JfrSymbolTable::create();
@@ -50,9 +55,11 @@ void JfrArtifactSet::initialize(bool class_unload) {
5055
_symbol_table->set_class_unload(class_unload);
5156
_total_count = 0;
5257
// Resource allocations. Keep in this allocation order.
53-
_klass_loader_leakp_set = new GrowableArray<const Klass*>(initial_klass_loader_set_size);
54-
_klass_loader_set = new GrowableArray<const Klass*>(initial_klass_loader_set_size);
55-
_klass_list = new GrowableArray<const Klass*>(initial_klass_list_size);
58+
if (previous_epoch) {
59+
_klass_loader_leakp_set = new JfrKlassSet(initial_klass_loader_leakp_set_size);
60+
}
61+
_klass_loader_set = new JfrKlassSet(initial_klass_loader_set_size);
62+
_klass_set = new JfrKlassSet(initial_klass_set_size);
5663
}
5764

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

95102
bool JfrArtifactSet::has_klass_entries() const {
96-
return _klass_list->is_nonempty();
97-
}
98-
99-
int JfrArtifactSet::entries() const {
100-
return _klass_list->length();
103+
return _klass_set->is_nonempty();
101104
}
102-
103-
static inline bool not_in_set(GrowableArray<const Klass*>* set, const Klass* k) {
105+
static inline bool not_in_set(JfrArtifactSet::JfrKlassSet* set, const Klass* k) {
104106
assert(set != nullptr, "invariant");
105107
assert(k != nullptr, "invariant");
106-
return !JfrMutablePredicate<const Klass*, compare_klasses>::test(set, k);
108+
return set->add(k);
107109
}
108110

109111
bool JfrArtifactSet::should_do_cld_klass(const Klass* k, bool leakp) {
@@ -116,16 +118,21 @@ bool JfrArtifactSet::should_do_cld_klass(const Klass* k, bool leakp) {
116118
void JfrArtifactSet::register_klass(const Klass* k) {
117119
assert(k != nullptr, "invariant");
118120
assert(IS_SERIALIZED(k), "invariant");
119-
assert(_klass_list != nullptr, "invariant");
120-
_klass_list->append(k);
121+
assert(_klass_set != nullptr, "invariant");
122+
_klass_set->add(k);
121123
}
122124

123125
size_t JfrArtifactSet::total_count() const {
126+
assert(_klass_set != nullptr, "invariant");
127+
initial_klass_set_size = MAX2(initial_klass_set_size, _klass_set->table_size());
128+
assert(_klass_loader_set != nullptr, "invariant");
129+
initial_klass_loader_set_size = MAX2(initial_klass_loader_set_size, _klass_loader_set->table_size());
124130
return _total_count;
125131
}
126132

127133
void JfrArtifactSet::increment_checkpoint_id() {
128134
assert(_symbol_table != nullptr, "invariant");
129135
_symbol_table->increment_checkpoint_id();
136+
assert(_klass_loader_leakp_set != nullptr, "invariant");
137+
initial_klass_loader_leakp_set_size = MAX2(initial_klass_loader_leakp_set_size, _klass_loader_leakp_set->table_size());
130138
}
131-

0 commit comments

Comments
 (0)