Skip to content

[Draft] Test/shen oc #26433

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 55 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
3da9f89
Testing ObjectCount event with Shenandoah collector
pf0n Jul 9, 2025
ec5ad1a
Created ObjectCount event tests for G1Full and ParallelOld
pf0n Jul 9, 2025
354deee
Initiated GC for ObjectCount testing
pf0n Jul 9, 2025
359caff
Removed unnecessary duplicate calls to ObjectCountEventVerifier in Ob…
pf0n Jul 10, 2025
adea2fc
ObjectCount event test with g1 full
pf0n Jul 14, 2025
c535645
Separated ObjectCount and ObjectCountAfterGC
pf0n Jul 15, 2025
a135b2d
Minimize code duplication by adding templates to ObjectCountEvent sender
pf0n Jul 16, 2025
4826229
ObjectCount data will be emitted if ObjectCountAfterGC is sending data
pf0n Jul 16, 2025
00b15d6
Delete src/hotspot/share/gc/shared/objectCountAfterGCEventSender.cpp
pf0n Jul 16, 2025
461b71c
Delete src/hotspot/share/gc/shared/objectCountAfterGCEventSender.hpp
pf0n Jul 16, 2025
33aa1da
Delete src/hotspot/share/gc/shared/objectCountEventSender.cpp
pf0n Jul 16, 2025
cb368f6
Delete src/hotspot/share/gc/shared/objectCountEventSender.hpp
pf0n Jul 16, 2025
3d05ece
Added object counting to Shenandoah
pf0n Jul 17, 2025
523ec03
Merge branch 'test/shen-oc' of https://github.com/pf0n/jdk into test/…
pf0n Jul 17, 2025
82bb883
Templates for ObjectCountEventSenderClosure
pf0n Jul 17, 2025
405e0d9
Updated file for objectCountEventSenderTemplate.hpp
pf0n Jul 17, 2025
d1a1e6b
Updated header gaurds for objectCountEventSenderTemplate.hpp
pf0n Jul 17, 2025
a868e9b
Removed whitespace changes
pf0n Jul 18, 2025
16eee14
Attempt to fix assert error
pf0n Jul 18, 2025
e4747f2
Removed iostream in objectCountEventSenderTemplate
pf0n Jul 18, 2025
0a9c094
Add template for the method that reports object count and updated res…
pf0n Jul 18, 2025
b88dce2
Reverting back to old commit
pf0n Jul 21, 2025
5f912be
Uncommented report_object_count_after_gc
pf0n Jul 21, 2025
08fc38c
Added KlassInfoTable to ObjectCount class
pf0n Jul 21, 2025
4c02ed1
Restored objectCountEventSender
pf0n Jul 22, 2025
520f534
Efficient implementation of ObjectCountAfterGC
pf0n Jul 22, 2025
b3b5706
New closure for object counting
pf0n Jul 22, 2025
5f77ceb
Delete src/hotspot/share/gc/shared/objectCountEventSenderTemplate.hpp
pf0n Jul 22, 2025
5466e0e
Atomic operations implemented in record_instance and merge_entry
pf0n Jul 22, 2025
afe3f12
Merge branch 'test/shen-oc' of https://github.com/pf0n/jdk into test/…
pf0n Jul 23, 2025
6c6e966
Included objectCountEventSender.hpp in objectCountClosure.cpp
pf0n Jul 23, 2025
57d4ea0
Added clearing entries method for KlassInfoTable
pf0n Jul 23, 2025
1c371b2
Removed inheritance from objectCountClosure
pf0n Jul 23, 2025
b1d8145
Templatized should_send_event in ObjectCountClosure
pf0n Jul 23, 2025
24523e7
Added include services gaurd to should_send_event()
pf0n Jul 23, 2025
dd3cd66
Attempt to fix incomplete type error
pf0n Jul 23, 2025
69f0813
Trying to fix forward declaration of 'class Mutex' error
pf0n Jul 23, 2025
eff8b16
Fixed assert error in debug build
pf0n Jul 24, 2025
5bf562b
Attempt to remove to unnecessary include for header files
pf0n Jul 24, 2025
f3957f0
Removed memory/heapInspection.hpp
pf0n Jul 24, 2025
9ab1d45
Changed the report_object_count function to call the get_table method…
pf0n Jul 24, 2025
0d5b154
Trying to fix debug build errors
pf0n Jul 24, 2025
8319955
Trying to fix incomplete type error for 'Mutex'
pf0n Jul 24, 2025
046be80
Included runtime/mutex in object count closure files
pf0n Jul 24, 2025
016196b
Trying to fix build error
pf0n Jul 24, 2025
35043ee
Trying to fix forward declaration of 'class KlassInfoTable'
pf0n Jul 24, 2025
3b3ed70
Added runtime/mutex to objectCountClosure
pf0n Jul 24, 2025
fd0418c
trying to fix debug errors
pf0n Jul 24, 2025
97028a7
Added include services to objectCountClosure.cpp
pf0n Jul 24, 2025
5de6cc9
Added extra check to see if ObjectCountAfterGC is enabled in report_o…
pf0n Jul 24, 2025
0d2a8a8
Removed universe.hpp
pf0n Jul 25, 2025
15859c1
Removed mutex header file in objectCountClosure files
pf0n Jul 25, 2025
f555bfa
Readded mutex header file in objectCountClosure files
pf0n Jul 25, 2025
a794546
Removed unnecessary atomic operations in merge_entry
pf0n Jul 25, 2025
bbc5ff0
Added comments for public methods in ObjectCountClosure
pf0n Jul 25, 2025
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
23 changes: 19 additions & 4 deletions src/hotspot/share/gc/shared/gcTrace.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
#include "gc/shared/gcId.hpp"
#include "gc/shared/gcTimer.hpp"
#include "gc/shared/gcTrace.hpp"
#include "jfr/jfrEvents.hpp"
#include "gc/shared/objectCountClosure.hpp"
#include "gc/shared/objectCountEventSender.hpp"
#include "gc/shared/referenceProcessorStats.hpp"
#include "memory/heapInspection.hpp"
Expand Down Expand Up @@ -74,6 +76,8 @@ void GCTracer::report_gc_reference_stats(const ReferenceProcessorStats& rps) con
}

#if INCLUDE_SERVICES

template <typename Event>
class ObjectCountEventSenderClosure : public KlassInfoClosure {
const double _size_threshold_percentage;
const size_t _total_size_in_words;
Expand All @@ -88,7 +92,7 @@ class ObjectCountEventSenderClosure : public KlassInfoClosure {

virtual void do_cinfo(KlassInfoEntry* entry) {
if (should_send_event(entry)) {
ObjectCountEventSender::send(entry, _timestamp);
ObjectCountEventSender::send<Event>(entry, _timestamp);
}
}

Expand All @@ -99,21 +103,32 @@ class ObjectCountEventSenderClosure : public KlassInfoClosure {
}
};

void GCTracer::report_object_count() {
KlassInfoTable* cit = ObjectCountClosure::get_table();
if (cit == nullptr || !ObjectCountEventSender::should_send_event<EventObjectCountAfterGC>()) {
return;
}

ObjectCountEventSenderClosure<EventObjectCountAfterGC> event_sender(cit->size_of_instances_in_words(), Ticks::now());
cit->iterate(&event_sender);
ObjectCountClosure::reset_table();
}

void GCTracer::report_object_count_after_gc(BoolObjectClosure* is_alive_cl, WorkerThreads* workers) {
assert(is_alive_cl != nullptr, "Must supply function to check liveness");

if (ObjectCountEventSender::should_send_event()) {
if (ObjectCountEventSender::should_send_event<EventObjectCountAfterGC>()) {
ResourceMark rm;

KlassInfoTable cit(false);
if (!cit.allocation_failed()) {
HeapInspection hi;
hi.populate_table(&cit, is_alive_cl, workers);
ObjectCountEventSenderClosure event_sender(cit.size_of_instances_in_words(), Ticks::now());
ObjectCountEventSenderClosure<EventObjectCountAfterGC> event_sender(cit.size_of_instances_in_words(), Ticks::now());
cit.iterate(&event_sender);
}
}
}

#endif // INCLUDE_SERVICES

void GCTracer::report_gc_heap_summary(GCWhen::Type when, const GCHeapSummary& heap_summary) const {
Expand Down
2 changes: 2 additions & 0 deletions src/hotspot/share/gc/shared/gcTrace.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ class GCTracer {
void report_gc_heap_summary(GCWhen::Type when, const GCHeapSummary& heap_summary) const;
void report_metaspace_summary(GCWhen::Type when, const MetaspaceSummary& metaspace_summary) const;
void report_gc_reference_stats(const ReferenceProcessorStats& rp) const;
// Sends event data to the ObjectCountAfterGC event
void report_object_count() NOT_SERVICES_RETURN;
void report_object_count_after_gc(BoolObjectClosure* object_filter, WorkerThreads* workers) NOT_SERVICES_RETURN;
void report_cpu_time_event(double user_time, double system_time, double real_time) const;

Expand Down
49 changes: 49 additions & 0 deletions src/hotspot/share/gc/shared/objectCountClosure.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@

#include "gc/shared/objectCountClosure.hpp"
#include "gc/shared/objectCountEventSender.hpp"
#include "jfr/jfrEvents.hpp"
#include "memory/heapInspection.hpp"
#include "utilities/macros.hpp"
#include "utilities/ticks.hpp"

#if INCLUDE_SERVICES

KlassInfoTable* ObjectCountClosure::cit = nullptr;

void ObjectCountClosure::reset_table() {
if (!check_table_exists()) {
return;
}
cit->clear_entries();
}


bool ObjectCountClosure::check_table_exists() {
if (cit == nullptr) {
static KlassInfoTable temp_table(false);
cit = &temp_table;
}
return !cit->allocation_failed();
}

bool ObjectCountClosure::record_object(oop o) {
if (!check_table_exists()) {
return false;
}
return cit->record_instance(o);
}

KlassInfoTable* ObjectCountClosure::get_table() {
return check_table_exists() ? cit : nullptr;
}


template <class Event>
bool ObjectCountClosure::should_send_event() {
return ObjectCountEventSender::should_send_event<Event>();
}

template bool ObjectCountClosure::should_send_event<EventObjectCount>();
template bool ObjectCountClosure::should_send_event<EventObjectCountAfterGC>();

#endif // INCLUDE_SERVICES
34 changes: 34 additions & 0 deletions src/hotspot/share/gc/shared/objectCountClosure.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#ifndef SHARE_GC_SHARED_OBJECTCOUNTCLOSURE_HPP
#define SHARE_GC_SHARED_OBJECTCOUNTCLOSURE_HPP

#include "gc/shared/gcId.hpp"
#include "gc/shared/objectCountEventSender.hpp"
#include "jfr/jfrEvents.hpp"
#include "memory/allStatic.hpp"
#include "runtime/mutex.hpp"
#include "memory/heapInspection.hpp"
#include "utilities/macros.hpp"
#include "utilities/ticks.hpp"

class KlassInfoEntry;
class Klass;

class ObjectCountClosure : public AllStatic {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wouldn't make it a static class.
Extend ObjectClosure instead, which is a StackObj.

(This ties into yr use of static methods below as well.)

We can discuss later this afternoon if the feedback in this review isn't clear.

static KlassInfoTable* cit;

public:
// Return false if allocation of KlassInfoTable failed.
static bool check_table_exists();
// Return false if object could not be recorded in the KlassInfoTable.
static bool record_object(oop o);
// Returns the KlassInfoTable if it exists, otherwise returns nullptr.
static KlassInfoTable* get_table();
// Clear entries of the KlassInfoTable
static void reset_table();

// Returns true if event is enabled
template <class Event>
static bool should_send_event();
};

#endif // SHARE_GC_SHARED_OBJECTCOUNTCLOSURE_HPP
20 changes: 17 additions & 3 deletions src/hotspot/share/gc/shared/objectCountEventSender.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,16 @@
#include "gc/shared/gcId.hpp"
#include "gc/shared/objectCountEventSender.hpp"
#include "jfr/jfrEvents.hpp"
#include "runtime/mutex.hpp"
#include "memory/heapInspection.hpp"
#include "utilities/macros.hpp"
#include "utilities/ticks.hpp"
#if INCLUDE_SERVICES

template <class Event>
bool ObjectCountEventSender::should_send_event() {
#if INCLUDE_JFR
return _should_send_requestable_event || EventObjectCountAfterGC::is_enabled();
return _should_send_requestable_event || Event::is_enabled();
#else
return false;
#endif // INCLUDE_JFR
Expand Down Expand Up @@ -63,13 +65,25 @@ void ObjectCountEventSender::send_event_if_enabled(Klass* klass, jlong count, ju
}
}


template <class Event>
void ObjectCountEventSender::send(const KlassInfoEntry* entry, const Ticks& timestamp) {
Klass* klass = entry->klass();
jlong count = entry->count();
julong total_size = entry->words() * BytesPerWord;

send_event_if_enabled<EventObjectCount>(klass, count, total_size, timestamp);
send_event_if_enabled<EventObjectCountAfterGC>(klass, count, total_size, timestamp);
send_event_if_enabled<Event>(klass, count, total_size, timestamp);
// If sending ObjectCountAfterGCEvent, check if ObjectCount is enabled and send event data to ObjectCount
// If sending ObjectCountEvent, do not send send ObjectCountAfterGCEvent
if (std::is_same<Event, EventObjectCountAfterGC>::value && ObjectCountEventSender::should_send_event<EventObjectCount>()) {
send_event_if_enabled<EventObjectCount>(klass, count, total_size, timestamp);
}
}

template bool ObjectCountEventSender::should_send_event<EventObjectCount>();
template bool ObjectCountEventSender::should_send_event<EventObjectCountAfterGC>();

template void ObjectCountEventSender::send<EventObjectCount>(const KlassInfoEntry*, const Ticks&);
template void ObjectCountEventSender::send<EventObjectCountAfterGC>(const KlassInfoEntry*, const Ticks&);

#endif // INCLUDE_SERVICES
7 changes: 5 additions & 2 deletions src/hotspot/share/gc/shared/objectCountEventSender.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
#ifndef SHARE_GC_SHARED_OBJECTCOUNTEVENTSENDER_HPP
#define SHARE_GC_SHARED_OBJECTCOUNTEVENTSENDER_HPP

#include "gc/shared/gcTrace.hpp"
#include "memory/allStatic.hpp"
#include "utilities/globalDefinitions.hpp"
#include "utilities/macros.hpp"
Expand All @@ -39,17 +38,21 @@ class Klass;
class ObjectCountEventSender : public AllStatic {
static bool _should_send_requestable_event;

template <typename T>
template <typename Event>
static void send_event_if_enabled(Klass* klass, jlong count, julong size, const Ticks& timestamp);

public:
static void enable_requestable_event();
static void disable_requestable_event();

template <class Event>
static void send(const KlassInfoEntry* entry, const Ticks& timestamp);

template <class Event>
static bool should_send_event();
};


#endif // INCLUDE_SERVICES

#endif // SHARE_GC_SHARED_OBJECTCOUNTEVENTSENDER_HPP
7 changes: 7 additions & 0 deletions src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,15 @@
#include "gc/shared/barrierSetNMethod.hpp"
#include "gc/shared/collectorCounters.hpp"
#include "gc/shared/continuationGCSupport.inline.hpp"
#include "gc/shared/gcTrace.hpp"
#include "gc/shenandoah/shenandoahBreakpoint.hpp"
#include "gc/shenandoah/shenandoahClosures.inline.hpp"
#include "gc/shenandoah/shenandoahCollectorPolicy.hpp"
#include "gc/shenandoah/shenandoahConcurrentGC.hpp"
#include "gc/shenandoah/shenandoahFreeSet.hpp"
#include "gc/shenandoah/shenandoahGeneration.hpp"
#include "gc/shenandoah/shenandoahGenerationalHeap.hpp"
#include "gc/shenandoah/shenandoahHeap.hpp"
#include "gc/shenandoah/shenandoahLock.hpp"
#include "gc/shenandoah/shenandoahMark.inline.hpp"
#include "gc/shenandoah/shenandoahMonitoringSupport.hpp"
Expand Down Expand Up @@ -770,6 +772,11 @@ void ShenandoahConcurrentGC::op_final_mark() {
heap->verifier()->verify_roots_no_forwarded();
}

{
heap->tracer()->report_object_count();
}


Comment on lines +775 to +779
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As discussed, move this out of the safepoint.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At the call-site, assert that we aren't at a safepoint.
Also add an interlock status on the global klass info table whether it's unpopulated, being populated, populated, being emitted, and emission completed; manage the status as you start the heap walk/recording, when the walk ends, when you start emitting the events, and when the events have been emitted, adding appropriate assertions to check the states of the machine as you being each phase, and change the state when that phase ends.

This would help you when you do the concurrent ObjectCount events, and also manage potential for interference with GC's and ObjectCountAfterGC. Make sure to do this state recording before you start working on the concurrent ObjectCount recording.

if (!heap->cancelled_gc()) {
_mark.finish_mark();
assert(!heap->cancelled_gc(), "STW mark cannot OOM");
Expand Down
5 changes: 5 additions & 0 deletions src/hotspot/share/gc/shenandoah/shenandoahMark.inline.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
#include "runtime/prefetch.inline.hpp"
#include "utilities/devirtualizer.inline.hpp"
#include "utilities/powerOfTwo.hpp"
#include "gc/shared/objectCountClosure.hpp"

template <StringDedupMode STRING_DEDUP>
void ShenandoahMark::dedup_string(oop obj, StringDedup::Requests* const req) {
Expand Down Expand Up @@ -367,6 +368,10 @@ inline void ShenandoahMark::mark_ref(ShenandoahObjToScanQueue* q,
if (marked) {
bool pushed = q->push(ShenandoahMarkTask(obj, skip_live, weak));
assert(pushed, "overflow queue should always succeed pushing");
bool should_record = ObjectCountClosure::should_send_event<EventObjectCountAfterGC>();
if (should_record) {
ObjectCountClosure::record_object(obj);
}
}
}

Expand Down
17 changes: 14 additions & 3 deletions src/hotspot/share/memory/heapInspection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -217,9 +217,10 @@ bool KlassInfoTable::record_instance(const oop obj) {
// elt may be null if it's a new klass for which we
// could not allocate space for a new entry in the hashtable.
if (elt != nullptr) {
elt->set_count(elt->count() + 1);
elt->set_words(elt->words() + obj->size());
_size_of_instances_in_words += obj->size();
elt->atomic_inc_count();
size_t obj_size = obj->size();
elt->atomic_add_words(obj_size);
Atomic::add(&_size_of_instances_in_words, obj_size);
return true;
} else {
return false;
Expand Down Expand Up @@ -265,6 +266,16 @@ class KlassInfoTableMergeClosure : public KlassInfoClosure {
bool success() { return _success; }
};

void KlassInfoTable::clear_entries() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that in this case you are doing a walk of the table again after emitting the events to clear it.

It makes sense instead to clear the entries as you emit the events from a specific entry. That way you avoid the extra walk.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It makes sense instead to clear the entries as you emit the events from a specific entry. That way you avoid the extra walk.

Good idea.

if (_buckets != nullptr) {
for (int index = 0; index < _num_buckets; index++) {
_buckets[index].empty();
_buckets[index].initialize();
}
_size_of_instances_in_words = 0;
}
}

// merge from table
bool KlassInfoTable::merge(KlassInfoTable* table) {
KlassInfoTableMergeClosure closure(this);
Expand Down
7 changes: 6 additions & 1 deletion src/hotspot/share/memory/heapInspection.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include "oops/objArrayOop.hpp"
#include "oops/oop.hpp"
#include "oops/annotations.hpp"
#include "runtime/atomic.hpp"
#include "utilities/macros.hpp"

class ParallelObjectIterator;
Expand Down Expand Up @@ -71,8 +72,11 @@ class KlassInfoEntry: public CHeapObj<mtInternal> {
Klass* klass() const { return _klass; }
uint64_t count() const { return _instance_count; }
void set_count(uint64_t ct) { _instance_count = ct; }
void atomic_inc_count() { Atomic::inc(&_instance_count); }
void atomic_add_count(uint64_t add) { Atomic::add(&_instance_count, add); }
size_t words() const { return _instance_words; }
void set_words(size_t wds) { _instance_words = wds; }
void atomic_add_words(size_t add) { Atomic::add(&_instance_words, add); }
void set_index(int64_t index) { _index = index; }
int64_t index() const { return _index; }
GrowableArray<KlassInfoEntry*>* subclasses() const { return _subclasses; }
Expand Down Expand Up @@ -126,7 +130,8 @@ class KlassInfoTable: public StackObj {
size_t size_of_instances_in_words() const;
bool merge(KlassInfoTable* table);
bool merge_entry(const KlassInfoEntry* cie);

// Clears entries in the table
void clear_entries();
friend class KlassInfoHisto;
friend class KlassHierarchy;
};
Expand Down
Loading