diff --git a/src/hotspot/share/gc/shared/gcTrace.cpp b/src/hotspot/share/gc/shared/gcTrace.cpp index bad9c707b1e51..9ea62b59b5248 100644 --- a/src/hotspot/share/gc/shared/gcTrace.cpp +++ b/src/hotspot/share/gc/shared/gcTrace.cpp @@ -27,8 +27,10 @@ #include "gc/shared/gcId.hpp" #include "gc/shared/gcTimer.hpp" #include "gc/shared/gcTrace.hpp" +#include "gc/shared/gcTrace.inline.hpp" #include "gc/shared/objectCountEventSender.hpp" #include "gc/shared/referenceProcessorStats.hpp" +#include "jfr/jfrEvents.hpp" #include "memory/heapInspection.hpp" #include "memory/resourceArea.hpp" #include "runtime/os.hpp" @@ -74,46 +76,22 @@ void GCTracer::report_gc_reference_stats(const ReferenceProcessorStats& rps) con } #if INCLUDE_SERVICES -class ObjectCountEventSenderClosure : public KlassInfoClosure { - const double _size_threshold_percentage; - const size_t _total_size_in_words; - const Ticks _timestamp; - - public: - ObjectCountEventSenderClosure(size_t total_size_in_words, const Ticks& timestamp) : - _size_threshold_percentage(ObjectCountCutOffPercent / 100), - _total_size_in_words(total_size_in_words), - _timestamp(timestamp) - {} - - virtual void do_cinfo(KlassInfoEntry* entry) { - if (should_send_event(entry)) { - ObjectCountEventSender::send(entry, _timestamp); - } - } - - private: - bool should_send_event(const KlassInfoEntry* entry) const { - double percentage_of_heap = ((double) entry->words()) / _total_size_in_words; - return percentage_of_heap >= _size_threshold_percentage; - } -}; 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()) { 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 event_sender(cit.size_of_instances_in_words(), Ticks::now(), &cit); cit.iterate(&event_sender); } } } + #endif // INCLUDE_SERVICES void GCTracer::report_gc_heap_summary(GCWhen::Type when, const GCHeapSummary& heap_summary) const { diff --git a/src/hotspot/share/gc/shared/gcTrace.hpp b/src/hotspot/share/gc/shared/gcTrace.hpp index 6a47e54090f88..115b618795597 100644 --- a/src/hotspot/share/gc/shared/gcTrace.hpp +++ b/src/hotspot/share/gc/shared/gcTrace.hpp @@ -103,6 +103,11 @@ 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 ObjectCount and/or ObjectCountAfterGC event + template + void report_object_count(T* heap) 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; diff --git a/src/hotspot/share/gc/shared/gcTrace.inline.hpp b/src/hotspot/share/gc/shared/gcTrace.inline.hpp new file mode 100644 index 0000000000000..26441aba6d5a4 --- /dev/null +++ b/src/hotspot/share/gc/shared/gcTrace.inline.hpp @@ -0,0 +1,54 @@ +#ifndef SHARE_GC_SHARED_GCTRACE_INLINE_HPP +#define SHARE_GC_SHARED_GCTRACE_INLINE_HPP + +#include "gc/shared/gcTrace.hpp" +#include "gc/shared/objectCountEventSender.inline.hpp" +#include "jfr/jfrEvents.hpp" +#include "memory/heapInspection.hpp" +#include "utilities/macros.hpp" + +#if INCLUDE_SERVICES + +template +class ObjectCountEventSenderClosure : public KlassInfoClosure { + const double _size_threshold_percentage; + size_t _total_size_in_words; + const Ticks _timestamp; + KlassInfoTable* _cit; + + public: + ObjectCountEventSenderClosure(size_t total_size_in_words, const Ticks& timestamp, KlassInfoTable* cit) : + _size_threshold_percentage(ObjectCountCutOffPercent / 100), + _total_size_in_words(total_size_in_words), + _timestamp(timestamp), + _cit(cit) + {} + + virtual void do_cinfo(KlassInfoEntry* entry) { + if (should_send_event(entry)) { + ObjectCountEventSender::send(entry, _timestamp); + _cit->delete_entry(entry, &_total_size_in_words); + } + } + + private: + bool should_send_event(const KlassInfoEntry* entry) const { + double percentage_of_heap = ((double) entry->words()) / _total_size_in_words; + return percentage_of_heap >= _size_threshold_percentage; + } +}; + +template +void GCTracer::report_object_count(T* heap) { + KlassInfoTable* cit = heap->get_cit(); + + if (cit == nullptr || !ObjectCountEventSender::should_send_event()) { + return; + } + ObjectCountEventSenderClosure event_sender(cit->size_of_instances_in_words(), Ticks::now(), cit); + cit->iterate(&event_sender); +} + +#endif // INCLUDE_SERVICES + +#endif // SHARE_GC_SHARED_GCTRACE_INLINE_HPP diff --git a/src/hotspot/share/gc/shared/objectCountEventSender.cpp b/src/hotspot/share/gc/shared/objectCountEventSender.cpp index a28c52e5c901d..5056bbdabd447 100644 --- a/src/hotspot/share/gc/shared/objectCountEventSender.cpp +++ b/src/hotspot/share/gc/shared/objectCountEventSender.cpp @@ -22,54 +22,10 @@ * */ - -#include "gc/shared/gcId.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 -bool ObjectCountEventSender::should_send_event() { -#if INCLUDE_JFR - return _should_send_requestable_event || EventObjectCountAfterGC::is_enabled(); -#else - return false; -#endif // INCLUDE_JFR -} +#if INCLUDE_SERVICES bool ObjectCountEventSender::_should_send_requestable_event = false; -void ObjectCountEventSender::enable_requestable_event() { - _should_send_requestable_event = true; -} - -void ObjectCountEventSender::disable_requestable_event() { - _should_send_requestable_event = false; -} - -template -void ObjectCountEventSender::send_event_if_enabled(Klass* klass, jlong count, julong size, const Ticks& timestamp) { - T event(UNTIMED); - if (event.should_commit()) { - event.set_starttime(timestamp); - event.set_endtime(timestamp); - event.set_gcId(GCId::current()); - event.set_objectClass(klass); - event.set_count(count); - event.set_totalSize(size); - event.commit(); - } -} - -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(klass, count, total_size, timestamp); - send_event_if_enabled(klass, count, total_size, timestamp); -} - #endif // INCLUDE_SERVICES diff --git a/src/hotspot/share/gc/shared/objectCountEventSender.hpp b/src/hotspot/share/gc/shared/objectCountEventSender.hpp index 115fbfdaf7d3a..f1177dd424b85 100644 --- a/src/hotspot/share/gc/shared/objectCountEventSender.hpp +++ b/src/hotspot/share/gc/shared/objectCountEventSender.hpp @@ -25,12 +25,12 @@ #ifndef SHARE_GC_SHARED_OBJECTCOUNTEVENTSENDER_HPP #define SHARE_GC_SHARED_OBJECTCOUNTEVENTSENDER_HPP -#include "gc/shared/gcTrace.hpp" +#include "jfr/jfrEvents.hpp" #include "memory/allStatic.hpp" +#include "memory/heapInspection.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/macros.hpp" #include "utilities/ticks.hpp" - #if INCLUDE_SERVICES class KlassInfoEntry; @@ -39,17 +39,21 @@ class Klass; class ObjectCountEventSender : public AllStatic { static bool _should_send_requestable_event; - template + template 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(); + static inline void enable_requestable_event(); + static inline void disable_requestable_event(); + template static void send(const KlassInfoEntry* entry, const Ticks& timestamp); + + template static bool should_send_event(); }; + #endif // INCLUDE_SERVICES #endif // SHARE_GC_SHARED_OBJECTCOUNTEVENTSENDER_HPP diff --git a/src/hotspot/share/gc/shared/objectCountEventSender.inline.hpp b/src/hotspot/share/gc/shared/objectCountEventSender.inline.hpp new file mode 100644 index 0000000000000..d709326b92bc4 --- /dev/null +++ b/src/hotspot/share/gc/shared/objectCountEventSender.inline.hpp @@ -0,0 +1,55 @@ +#ifndef SHARE_GC_SHARED_OBJECTCOUNTEVENTSENDER_INLINE_HPP +#define SHARE_GC_SHARED_OBJECTCOUNTEVENTSENDER_INLINE_HPP + +#include "gc/shared/objectCountEventSender.hpp" + +#if INCLUDE_SERVICES + +inline void ObjectCountEventSender::enable_requestable_event() { + ObjectCountEventSender::_should_send_requestable_event = true; +} + +inline void ObjectCountEventSender::disable_requestable_event() { + ObjectCountEventSender::_should_send_requestable_event = false; +} + +template +bool ObjectCountEventSender::should_send_event() { +#if INCLUDE_JFR + return _should_send_requestable_event || Event::is_enabled(); +#else + return false; +#endif // INCLUDE_JFR +} + +template +void ObjectCountEventSender::send_event_if_enabled(Klass* klass, jlong count, julong size, const Ticks& timestamp) { + T event(UNTIMED); + if (event.should_commit()) { + event.set_starttime(timestamp); + event.set_endtime(timestamp); + event.set_gcId(GCId::current()); + event.set_objectClass(klass); + event.set_count(count); + event.set_totalSize(size); + event.commit(); + } +} + +template +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(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::value && ObjectCountEventSender::should_send_event()) { + send_event_if_enabled(klass, count, total_size, timestamp); + } +} + +#endif // INCLUDE_SERVICES + +#endif // SHARE_GC_SHARED_OBJECTCOUNTEVENTSENDER_INLINE_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahClosures.hpp b/src/hotspot/share/gc/shenandoah/shenandoahClosures.hpp index 0c223ee3128be..f7793f543af7b 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahClosures.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahClosures.hpp @@ -27,6 +27,7 @@ #include "code/nmethod.hpp" #include "gc/shared/stringdedup/stringDedup.hpp" #include "gc/shenandoah/shenandoahGenerationType.hpp" +#include "gc/shenandoah/shenandoahObjectCountClosure.hpp" #include "gc/shenandoah/shenandoahTaskqueue.hpp" #include "memory/iterator.hpp" #include "runtime/javaThread.hpp" @@ -73,7 +74,8 @@ class ShenandoahMarkRefsSuperClosure : public ShenandoahSuperClosure { protected: template - void work(T *p); + // Return true if object was not previously marked by another thread + bool work(T *p); public: inline ShenandoahMarkRefsSuperClosure(ShenandoahObjToScanQueue* q, ShenandoahReferenceProcessor* rp, ShenandoahObjToScanQueue* old_q); @@ -106,6 +108,28 @@ class ShenandoahMarkRefsClosure : public ShenandoahMarkRefsSuperClosure { virtual void do_oop(oop* p) { do_oop_work(p); } }; +template +class ShenandoahMarkRefsAndCountClosure : public ShenandoahMarkRefsSuperClosure { +private: + ShenandoahObjectCountClosure* _count; + + template + inline void do_oop_work(T* p) { + // Count newly marked strong references to avoid double counting + bool newly_marked = work(p); + if (newly_marked) { + _count->do_oop(p); + } + } + +public: + ShenandoahMarkRefsAndCountClosure(ShenandoahObjToScanQueue* q, ShenandoahReferenceProcessor* rp, ShenandoahObjToScanQueue* old_q, ShenandoahObjectCountClosure* count) : + ShenandoahMarkRefsSuperClosure(q, rp, old_q), _count(count) {}; + + virtual void do_oop(narrowOop* p) { do_oop_work(p); } + virtual void do_oop(oop* p) { do_oop_work(p); } +}; + class ShenandoahForwardedIsAliveClosure : public BoolObjectClosure { private: ShenandoahMarkingContext* const _mark_context; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahClosures.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahClosures.inline.hpp index 725e4e6e3e9f9..cf642f762b6f8 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahClosures.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahClosures.inline.hpp @@ -77,8 +77,8 @@ ShenandoahMarkRefsSuperClosure::ShenandoahMarkRefsSuperClosure(ShenandoahObjToSc _weak(false) {} template -inline void ShenandoahMarkRefsSuperClosure::work(T* p) { - ShenandoahMark::mark_through_ref(p, _queue, _old_queue, _mark_context, _weak); +inline bool ShenandoahMarkRefsSuperClosure::work(T* p) { + return ShenandoahMark::mark_through_ref(p, _queue, _old_queue, _mark_context, _weak); } ShenandoahForwardedIsAliveClosure::ShenandoahForwardedIsAliveClosure() : diff --git a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp index 81154aff9f0ad..dbd917e887358 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp @@ -28,6 +28,7 @@ #include "gc/shared/barrierSetNMethod.hpp" #include "gc/shared/collectorCounters.hpp" #include "gc/shared/continuationGCSupport.inline.hpp" +#include "gc/shared/gcTrace.inline.hpp" #include "gc/shenandoah/shenandoahBreakpoint.hpp" #include "gc/shenandoah/shenandoahClosures.inline.hpp" #include "gc/shenandoah/shenandoahCollectorPolicy.hpp" @@ -314,6 +315,10 @@ void ShenandoahConcurrentGC::vmop_entry_final_mark() { heap->try_inject_alloc_failure(); VM_ShenandoahFinalMarkStartEvac op(this); VMThread::execute(&op); // jump to entry_final_mark under safepoint + + // Do not report object count during a safepoint + assert(!ShenandoahSafepoint::is_at_shenandoah_safepoint(), "Should not be at safepoint"); + heap->tracer()->report_object_count(heap); } void ShenandoahConcurrentGC::vmop_entry_init_update_refs() { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.cpp b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.cpp index facba2236be81..2623ecba372cc 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.cpp @@ -23,7 +23,7 @@ * */ - +#include "gc/shared/objectCountEventSender.inline.hpp" #include "gc/shared/satbMarkQueue.hpp" #include "gc/shared/strongRootsScope.hpp" #include "gc/shared/taskTerminator.hpp" @@ -33,6 +33,7 @@ #include "gc/shenandoah/shenandoahGeneration.hpp" #include "gc/shenandoah/shenandoahHeap.inline.hpp" #include "gc/shenandoah/shenandoahMark.inline.hpp" +#include "gc/shenandoah/shenandoahObjectCountClosure.hpp" #include "gc/shenandoah/shenandoahPhaseTimings.hpp" #include "gc/shenandoah/shenandoahReferenceProcessor.hpp" #include "gc/shenandoah/shenandoahRootProcessor.inline.hpp" @@ -45,6 +46,7 @@ #include "runtime/continuation.hpp" #include "runtime/threads.hpp" + template class ShenandoahConcurrentMarkingTask : public WorkerTask { private: @@ -170,8 +172,20 @@ void ShenandoahMarkConcurrentRootsTask::work(uint worker_id) { ShenandoahObjToScanQueue* q = _queue_set->queue(worker_id); ShenandoahObjToScanQueue* old_q = (_old_queue_set == nullptr) ? nullptr : _old_queue_set->queue(worker_id); - ShenandoahMarkRefsClosure cl(q, _rp, old_q); - _root_scanner.roots_do(&cl, worker_id); + + // Use object counting closure if ObjectCountAfterGC event is enabled + bool object_count_enabled = ObjectCountEventSender::should_send_event(); + if (object_count_enabled) { + KlassInfoTable* const main_cit = ShenandoahHeap::heap()->get_cit(); + KlassInfoTable local_cit(false); + ShenandoahObjectCountClosure _count(&local_cit); + ShenandoahMarkRefsAndCountClosure cl(q, _rp, old_q, &_count); + _root_scanner.roots_do(&cl, worker_id); + _count.merge_tables(main_cit); + } else { + ShenandoahMarkRefsClosure cl(q, _rp, old_q); + _root_scanner.roots_do(&cl, worker_id); + } } void ShenandoahConcurrentMark::mark_concurrent_roots() { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp b/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp index 0c80b800ac5c3..d99835ef29aff 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp @@ -57,6 +57,12 @@ void ShenandoahControlThread::run_service() { ShenandoahCollectorPolicy* const policy = heap->shenandoah_policy(); ShenandoahHeuristics* const heuristics = heap->heuristics(); + + // KlassInfoTable is a StackObj that shouldn't be heap-allocated. + // Use a static instance in the control thread to persist through GC cycles. + static KlassInfoTable temp_cit(false); + heap->set_cit(&temp_cit); + while (!should_terminate()) { const GCCause::Cause cancelled_cause = heap->cancelled_cause(); if (cancelled_cause == GCCause::_shenandoah_stop_vm) { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp index 2baf7e25cbabb..2a891342f66f0 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp @@ -533,6 +533,7 @@ ShenandoahHeap::ShenandoahHeap(ShenandoahCollectorPolicy* policy) : _active_generation(nullptr), _initial_size(0), _committed(0), + _cit(nullptr), _max_workers(MAX3(ConcGCThreads, ParallelGCThreads, 1U)), _workers(nullptr), _safepoint_workers(nullptr), diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp index bec1235e94140..c04176117d381 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp @@ -43,6 +43,7 @@ #include "gc/shenandoah/shenandoahPadding.hpp" #include "gc/shenandoah/shenandoahSharedVariables.hpp" #include "gc/shenandoah/shenandoahUnload.hpp" +#include "memory/heapInspection.hpp" #include "memory/metaspace.hpp" #include "services/memoryManager.hpp" #include "utilities/globalDefinitions.hpp" @@ -228,7 +229,8 @@ class ShenandoahHeap : public CollectedHeap { shenandoah_padding(0); volatile size_t _committed; shenandoah_padding(1); - + + KlassInfoTable* _cit; void increase_used(const ShenandoahAllocRequest& req); public: @@ -252,6 +254,12 @@ class ShenandoahHeap : public CollectedHeap { void set_soft_max_capacity(size_t v); + // Create Shenandoah's KlassInfoTable + inline void set_cit(KlassInfoTable* cit); + + // Return Shenandoah's KlassInfoTable + inline KlassInfoTable* get_cit(); + // ---------- Periodic Tasks // public: diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp index cf9d808f7ce8f..4d74437802f56 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp @@ -46,6 +46,7 @@ #include "gc/shenandoah/shenandoahMarkingContext.inline.hpp" #include "gc/shenandoah/shenandoahThreadLocalData.hpp" #include "gc/shenandoah/shenandoahWorkGroup.hpp" +#include "memory/heapInspection.hpp" #include "oops/compressedOops.inline.hpp" #include "oops/oop.inline.hpp" #include "runtime/atomic.hpp" @@ -647,4 +648,12 @@ inline ShenandoahMarkingContext* ShenandoahHeap::marking_context() const { return _marking_context; } +inline void ShenandoahHeap::set_cit(KlassInfoTable* cit) { + _cit = cit; +} + +inline KlassInfoTable* ShenandoahHeap::get_cit() { + return _cit; +} + #endif // SHARE_GC_SHENANDOAH_SHENANDOAHHEAP_INLINE_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMark.cpp b/src/hotspot/share/gc/shenandoah/shenandoahMark.cpp index 2a4149ee44dc4..3b8dcc43d3759 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahMark.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahMark.cpp @@ -24,7 +24,7 @@ */ - +#include "gc/shared/objectCountEventSender.inline.hpp" #include "gc/shenandoah/shenandoahBarrierSet.hpp" #include "gc/shenandoah/shenandoahClosures.inline.hpp" #include "gc/shenandoah/shenandoahGeneration.hpp" @@ -69,9 +69,21 @@ void ShenandoahMark::mark_loop_prework(uint w, TaskTerminator *t, ShenandoahRefe Closure cl(q, rp, old_q); mark_loop_work(&cl, ld, w, t, req); } else { - using Closure = ShenandoahMarkRefsClosure; - Closure cl(q, rp, old_q); - mark_loop_work(&cl, ld, w, t, req); + // Use object counting closure if ObjectCountAfterGC event is enabled + bool object_count_enabled = ObjectCountEventSender::should_send_event(); + if (object_count_enabled) { + KlassInfoTable* const main_cit = ShenandoahHeap::heap()->get_cit(); + KlassInfoTable local_cit(false); + ShenandoahObjectCountClosure _count(&local_cit); + using Closure = ShenandoahMarkRefsAndCountClosure; + Closure cl(q, rp, old_q, &_count); + mark_loop_work(&cl, ld, w, t, req); + _count.merge_tables(main_cit); + } else { + using Closure = ShenandoahMarkRefsClosure; + Closure cl(q, rp, old_q); + mark_loop_work(&cl, ld, w, t, req); + } } heap->flush_liveness_cache(w); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMark.hpp b/src/hotspot/share/gc/shenandoah/shenandoahMark.hpp index 4aef14f2c9aba..5675bfc3f5fa5 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahMark.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahMark.hpp @@ -57,7 +57,7 @@ class ShenandoahMark: public StackObj { public: template - static inline void mark_through_ref(T* p, ShenandoahObjToScanQueue* q, ShenandoahObjToScanQueue* old_q, ShenandoahMarkingContext* const mark_context, bool weak); + static inline bool mark_through_ref(T* p, ShenandoahObjToScanQueue* q, ShenandoahObjToScanQueue* old_q, ShenandoahMarkingContext* const mark_context, bool weak); // Loom support void start_mark(); @@ -99,9 +99,9 @@ class ShenandoahMark: public StackObj { static bool in_generation(ShenandoahHeap* const heap, oop obj); template - static void mark_non_generational_ref(T *p, ShenandoahObjToScanQueue* q, ShenandoahMarkingContext* const mark_context, bool weak); + static bool mark_non_generational_ref(T *p, ShenandoahObjToScanQueue* q, ShenandoahMarkingContext* const mark_context, bool weak); - static void mark_ref(ShenandoahObjToScanQueue* q, + static bool mark_ref(ShenandoahObjToScanQueue* q, ShenandoahMarkingContext* const mark_context, bool weak, oop obj); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMark.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahMark.inline.hpp index 2dc0813e51354..eb7aabe393641 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahMark.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahMark.inline.hpp @@ -293,10 +293,11 @@ bool ShenandoahMark::in_generation(ShenandoahHeap* const heap, oop obj) { } template -inline void ShenandoahMark::mark_through_ref(T *p, ShenandoahObjToScanQueue* q, ShenandoahObjToScanQueue* old_q, ShenandoahMarkingContext* const mark_context, bool weak) { +inline bool ShenandoahMark::mark_through_ref(T *p, ShenandoahObjToScanQueue* q, ShenandoahObjToScanQueue* old_q, ShenandoahMarkingContext* const mark_context, bool weak) { // Note: This is a very hot code path, so the code should be conditional on GENERATION template // parameter where possible, in order to generate the most efficient code. + bool strongly_marked = false; T o = RawAccess<>::oop_load(p); if (!CompressedOops::is_null(o)) { oop obj = CompressedOops::decode_not_null(o); @@ -305,7 +306,7 @@ inline void ShenandoahMark::mark_through_ref(T *p, ShenandoahObjToScanQueue* q, shenandoah_assert_not_forwarded(p, obj); shenandoah_assert_not_in_cset_except(p, obj, heap->cancelled_gc()); if (in_generation(heap, obj)) { - mark_ref(q, mark_context, weak, obj); + strongly_marked = mark_ref(q, mark_context, weak, obj); shenandoah_assert_marked(p, obj); if (GENERATION == YOUNG && heap->is_in_old(p)) { // Mark card as dirty because remembered set scanning still finds interesting pointer. @@ -316,7 +317,7 @@ inline void ShenandoahMark::mark_through_ref(T *p, ShenandoahObjToScanQueue* q, } } else if (old_q != nullptr) { // Young mark, bootstrapping old_q or concurrent with old_q marking. - mark_ref(old_q, mark_context, weak, obj); + strongly_marked = mark_ref(old_q, mark_context, weak, obj); shenandoah_assert_marked(p, obj); } else if (GENERATION == OLD) { // Old mark, found a young pointer. @@ -326,48 +327,54 @@ inline void ShenandoahMark::mark_through_ref(T *p, ShenandoahObjToScanQueue* q, } } } + return strongly_marked; } template<> -inline void ShenandoahMark::mark_through_ref(oop *p, ShenandoahObjToScanQueue* q, ShenandoahObjToScanQueue* old_q, ShenandoahMarkingContext* const mark_context, bool weak) { - mark_non_generational_ref(p, q, mark_context, weak); +inline bool ShenandoahMark::mark_through_ref(oop *p, ShenandoahObjToScanQueue* q, ShenandoahObjToScanQueue* old_q, ShenandoahMarkingContext* const mark_context, bool weak) { + return mark_non_generational_ref(p, q, mark_context, weak); } template<> -inline void ShenandoahMark::mark_through_ref(narrowOop *p, ShenandoahObjToScanQueue* q, ShenandoahObjToScanQueue* old_q, ShenandoahMarkingContext* const mark_context, bool weak) { - mark_non_generational_ref(p, q, mark_context, weak); +inline bool ShenandoahMark::mark_through_ref(narrowOop *p, ShenandoahObjToScanQueue* q, ShenandoahObjToScanQueue* old_q, ShenandoahMarkingContext* const mark_context, bool weak) { + return mark_non_generational_ref(p, q, mark_context, weak); } template -inline void ShenandoahMark::mark_non_generational_ref(T* p, ShenandoahObjToScanQueue* q, +inline bool ShenandoahMark::mark_non_generational_ref(T* p, ShenandoahObjToScanQueue* q, ShenandoahMarkingContext* const mark_context, bool weak) { oop o = RawAccess<>::oop_load(p); + bool strongly_marked = false; if (!CompressedOops::is_null(o)) { oop obj = CompressedOops::decode_not_null(o); shenandoah_assert_not_forwarded(p, obj); shenandoah_assert_not_in_cset_except(p, obj, ShenandoahHeap::heap()->cancelled_gc()); - mark_ref(q, mark_context, weak, obj); + strongly_marked = mark_ref(q, mark_context, weak, obj); shenandoah_assert_marked(p, obj); } + return strongly_marked; } -inline void ShenandoahMark::mark_ref(ShenandoahObjToScanQueue* q, +inline bool ShenandoahMark::mark_ref(ShenandoahObjToScanQueue* q, ShenandoahMarkingContext* const mark_context, bool weak, oop obj) { bool skip_live = false; bool marked; + bool strongly_marked = false; if (weak) { marked = mark_context->mark_weak(obj); } else { marked = mark_context->mark_strong(obj, /* was_upgraded = */ skip_live); + strongly_marked = marked; } if (marked) { bool pushed = q->push(ShenandoahMarkTask(obj, skip_live, weak)); assert(pushed, "overflow queue should always succeed pushing"); } + return strongly_marked; } ShenandoahObjToScanQueueSet* ShenandoahMark::task_queues() const { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahObjectCountClosure.cpp b/src/hotspot/share/gc/shenandoah/shenandoahObjectCountClosure.cpp new file mode 100644 index 0000000000000..697cc84c48a72 --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/shenandoahObjectCountClosure.cpp @@ -0,0 +1,18 @@ +#include "gc/shenandoah/shenandoahObjectCountClosure.hpp" +#include "runtime/mutex.hpp" + +Mutex* ShenandoahObjectCountClosure::get_mutex() { + static Mutex mutex(Mutex::nosafepoint, "ShenandoahObjectCountMerge"); + return &mutex; +} + +void ShenandoahObjectCountClosure::merge_tables(KlassInfoTable* main_cit) { + if (main_cit == nullptr || _cit == nullptr) { + return; + } + + Mutex* mutex = get_mutex(); + MutexLocker x(mutex, Mutex::_no_safepoint_check_flag); + bool success = main_cit->merge(_cit); + assert(success, "Failed to merge thread-local table"); +} diff --git a/src/hotspot/share/gc/shenandoah/shenandoahObjectCountClosure.hpp b/src/hotspot/share/gc/shenandoah/shenandoahObjectCountClosure.hpp new file mode 100644 index 0000000000000..bfae98d59e11e --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/shenandoahObjectCountClosure.hpp @@ -0,0 +1,35 @@ +#ifndef SHARE_GC_SHENANDOAH_SHENANDOAHOBJECTCOUNTCLOSURE_HPP +#define SHARE_GC_SHENANDOAH_SHENANDOAHOBJECTCOUNTCLOSURE_HPP + +#include "memory/heapInspection.hpp" +#include "oops/access.hpp" +#include "oops/compressedOops.inline.hpp" +#include "oops/oop.inline.hpp" + +class ShenandoahObjectCountClosure { +private: + KlassInfoTable* _cit; + + template + inline void do_oop_work(T* p) { + T o = RawAccess<>::oop_load(p); + oop obj = CompressedOops::decode_not_null(o); + _cit->record_instance(obj); + } + +public: + ShenandoahObjectCountClosure(KlassInfoTable* cit) : _cit(cit) {} + // Record the object's instance in the KlassInfoTable + inline void do_oop(narrowOop* o) { do_oop_work(o); } + // Record the object's instance in the KlassInfoTable + inline void do_oop(oop* o) { do_oop_work(o); } + inline KlassInfoTable* get_table() { return _cit; } + + // Merges the heap's KlassInfoTable with the thread's KlassInfoTable + void merge_tables(KlassInfoTable* main_cit); + +private: + Mutex* get_mutex(); +}; + +#endif // SHARE_GC_SHENANDOAH_SHENANDOAHOBJECTCOUNTCLOSURE_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahSTWMark.cpp b/src/hotspot/share/gc/shenandoah/shenandoahSTWMark.cpp index c2bfea664fdcf..613e2f44edb76 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahSTWMark.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahSTWMark.cpp @@ -24,7 +24,7 @@ */ - +#include "gc/shared/objectCountEventSender.inline.hpp" #include "gc/shared/strongRootsScope.hpp" #include "gc/shared/taskTerminator.hpp" #include "gc/shared/workerThread.hpp" @@ -32,6 +32,7 @@ #include "gc/shenandoah/shenandoahGeneration.hpp" #include "gc/shenandoah/shenandoahGenerationType.hpp" #include "gc/shenandoah/shenandoahMark.inline.hpp" +#include "gc/shenandoah/shenandoahObjectCountClosure.hpp" #include "gc/shenandoah/shenandoahReferenceProcessor.hpp" #include "gc/shenandoah/shenandoahRootProcessor.inline.hpp" #include "gc/shenandoah/shenandoahSTWMark.hpp" @@ -122,8 +123,19 @@ void ShenandoahSTWMark::mark_roots(uint worker_id) { auto queue = task_queues()->queue(worker_id); switch (_generation->type()) { case NON_GEN: { - ShenandoahMarkRefsClosure init_mark(queue, rp, nullptr); - _root_scanner.roots_do(&init_mark, worker_id); + // Use object counting closure if ObjectCountAfterGC event is enabled + bool object_count_enabled = ObjectCountEventSender::should_send_event(); + if (object_count_enabled) { + KlassInfoTable* const main_cit = ShenandoahHeap::heap()->get_cit(); + KlassInfoTable local_cit(false); + ShenandoahObjectCountClosure _count(&local_cit); + ShenandoahMarkRefsAndCountClosure init_mark(queue, rp, nullptr, &_count); + _root_scanner.roots_do(&init_mark, worker_id); + _count.merge_tables(main_cit); + } else { + ShenandoahMarkRefsClosure init_mark(queue, rp, nullptr); + _root_scanner.roots_do(&init_mark, worker_id); + } break; } case GLOBAL: { diff --git a/src/hotspot/share/jfr/periodic/jfrPeriodic.cpp b/src/hotspot/share/jfr/periodic/jfrPeriodic.cpp index 0be1c32728c08..0bb3237a743c7 100644 --- a/src/hotspot/share/jfr/periodic/jfrPeriodic.cpp +++ b/src/hotspot/share/jfr/periodic/jfrPeriodic.cpp @@ -33,7 +33,7 @@ #include "gc/shared/gcConfiguration.hpp" #include "gc/shared/gcTrace.hpp" #include "gc/shared/gcVMOperations.hpp" -#include "gc/shared/objectCountEventSender.hpp" +#include "gc/shared/objectCountEventSender.inline.hpp" #include "jfr/jfrEvents.hpp" #include "jfr/periodic/jfrCompilerQueueUtilization.hpp" #include "jfr/periodic/jfrFinalizerStatisticsEvent.hpp" diff --git a/src/hotspot/share/memory/heapInspection.cpp b/src/hotspot/share/memory/heapInspection.cpp index 867ccc6106d8e..3350104ceaf6a 100644 --- a/src/hotspot/share/memory/heapInspection.cpp +++ b/src/hotspot/share/memory/heapInspection.cpp @@ -140,8 +140,11 @@ KlassInfoEntry* KlassInfoBucket::lookup(Klass* const k) { void KlassInfoBucket::iterate(KlassInfoClosure* cic) { KlassInfoEntry* elt = _list; while (elt != nullptr) { + // The remove_from_list method will delete elt if ObjectCountEventSenderClosure decides to send the event + // If the event is sent, we should save the next entry of the bucket and define elt as that next entry, since could be deleted + KlassInfoEntry* next = elt->next(); cic->do_cinfo(elt); - elt = elt->next(); + elt = next; } } @@ -155,6 +158,30 @@ void KlassInfoBucket::empty() { } } +void KlassInfoBucket::remove_from_list(KlassInfoEntry*& entry) { + if (_list == entry) { + KlassInfoEntry* next = _list->next(); + _list = next; + delete entry; + entry = nullptr; + return; + } + + KlassInfoEntry* current = _list; + while (current != nullptr && current->next() != entry) { + current = current->next(); + } + + if (current != nullptr && current->next() == entry) { + KlassInfoEntry* next = entry->next(); + current->set_next(next); + delete entry; + entry = nullptr; + } + + assert(entry == nullptr, "Entry was not deleted"); +} + class KlassInfoTable::AllClassesFinder : public LockedClassesDo { KlassInfoTable *_table; public: @@ -253,6 +280,13 @@ bool KlassInfoTable::merge_entry(const KlassInfoEntry* cie) { return false; } +void KlassInfoTable::delete_entry(KlassInfoEntry* entry, size_t* total_table_size) { + uint idx = hash(entry->klass()) % _num_buckets; + size_t total_entry_size = entry->words(); + _buckets[idx].remove_from_list(entry); + *total_table_size -= total_entry_size; +} + class KlassInfoTableMergeClosure : public KlassInfoClosure { private: KlassInfoTable* _dest; diff --git a/src/hotspot/share/memory/heapInspection.hpp b/src/hotspot/share/memory/heapInspection.hpp index ca9a46c9140bd..5058e29a8dacc 100644 --- a/src/hotspot/share/memory/heapInspection.hpp +++ b/src/hotspot/share/memory/heapInspection.hpp @@ -30,6 +30,7 @@ #include "oops/objArrayOop.hpp" #include "oops/oop.hpp" #include "oops/annotations.hpp" +#include "runtime/mutex.hpp" #include "utilities/macros.hpp" class ParallelObjectIterator; @@ -66,6 +67,7 @@ class KlassInfoEntry: public CHeapObj { _do_print(false), _subclasses(nullptr) {} ~KlassInfoEntry(); + void set_next(KlassInfoEntry* next) { _next = next; } KlassInfoEntry* next() const { return _next; } bool is_equal(const Klass* k) { return k == _klass; } Klass* klass() const { return _klass; } @@ -99,6 +101,9 @@ class KlassInfoBucket: public CHeapObj { KlassInfoEntry* lookup(Klass* k); void initialize() { _list = nullptr; } void empty(); + // Remove from the bucket list, and delete `entry`. + // `entry` must exist in the list. + void remove_from_list(KlassInfoEntry*& entry); void iterate(KlassInfoClosure* cic); }; @@ -126,7 +131,8 @@ class KlassInfoTable: public StackObj { size_t size_of_instances_in_words() const; bool merge(KlassInfoTable* table); bool merge_entry(const KlassInfoEntry* cie); - + // Deletes the KlassInfoEntry in the list + void delete_entry(KlassInfoEntry* entry, size_t* total_table_size); friend class KlassInfoHisto; friend class KlassHierarchy; }; diff --git a/test/jdk/jdk/jfr/event/gc/objectcount/ObjectCountEvent.java b/test/jdk/jdk/jfr/event/gc/objectcount/ObjectCountEvent.java new file mode 100644 index 0000000000000..30d9e141d8285 --- /dev/null +++ b/test/jdk/jdk/jfr/event/gc/objectcount/ObjectCountEvent.java @@ -0,0 +1,67 @@ +package jdk.jfr.event.gc.objectcount; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; + +public class ObjectCountEvent { + private static final String objectCountEventPath = EventNames.ObjectCount; + private static final String gcEventPath = EventNames.GarbageCollection; + private static final String heapSummaryEventPath = EventNames.GCHeapSummary; + + public static void test(String gcName) throws Exception { + Recording recording = new Recording(); + recording.enable(objectCountEventPath); + recording.enable(gcEventPath); + recording.enable(heapSummaryEventPath); + + ObjectCountEventVerifier.createTestData(); + recording.start(); + + Path tempFile = Path.of("temp.jfr"); + recording.dump(tempFile); // Forces chunk rotation + System.gc(); + recording.stop(); + + Files.deleteIfExists(tempFile); + + System.out.println("gcName=" + gcName); + List events = Events.fromRecording(recording); + for (RecordedEvent event : events) { + System.out.println("Event: " + event); + } + + Optional gcEvent = events.stream() + .filter(e -> isMySystemGc(e, gcName)) + .findFirst(); + + List objCountEvents = events.stream() + .filter(e -> Events.isEventType(e, objectCountEventPath)) + .collect(Collectors.toList()); + Asserts.assertFalse(objCountEvents.isEmpty(), "No objCountEvents"); + + Optional heapSummaryEvent = events.stream() + .filter(e -> Events.isEventType(e, heapSummaryEventPath)) + .filter(e -> "After GC".equals(Events.assertField(e, "when").getValue())) + .findFirst(); + Asserts.assertTrue(heapSummaryEvent.isPresent(), "No heapSummary"); + System.out.println("Found heapSummaryEvent: " + heapSummaryEvent.get()); + + Events.assertField(heapSummaryEvent.get(), "heapUsed").atLeast(0L).getValue(); + ObjectCountEventVerifier.verify(objCountEvents); + } + + private static boolean isMySystemGc(RecordedEvent event, String gcName) { + return Events.isEventType(event, gcEventPath) && + gcName.equals(Events.assertField(event, "name").getValue()) && + "System.gc()".equals(Events.assertField(event, "cause").getValue()); + } +} diff --git a/test/jdk/jdk/jfr/event/gc/objectcount/TestObjectCountAfterGCEventWithShenandoah.java b/test/jdk/jdk/jfr/event/gc/objectcount/TestObjectCountAfterGCEventWithShenandoah.java new file mode 100644 index 0000000000000..d99bf8d536b97 --- /dev/null +++ b/test/jdk/jdk/jfr/event/gc/objectcount/TestObjectCountAfterGCEventWithShenandoah.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2013, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.jfr.event.gc.objectcount; +import jdk.test.lib.jfr.GCHelper; + +/** + * @test + * @requires vm.flagless + * @requires vm.hasJFR + * @requires (vm.gc == "Shenandoah" | vm.gc == null) + * & vm.opt.ExplicitGCInvokesConcurrent != true + * @library /test/lib /test/jdk + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -XX:+UseShenandoahGC -XX:MarkSweepDeadRatio=0 -XX:-UseCompressedOops -XX:-UseCompressedClassPointers -XX:+IgnoreUnrecognizedVMOptions jdk.jfr.event.gc.objectcount.TestObjectCountAfterGCEventWithShenandoah + */ +public class TestObjectCountAfterGCEventWithShenandoah { + public static void main(String[] args) throws Exception { + ObjectCountAfterGCEvent.test(GCHelper.gcShenandoah); + } +} diff --git a/test/jdk/jdk/jfr/event/gc/objectcount/TestObjectCountEventWithG1FullCollection.java b/test/jdk/jdk/jfr/event/gc/objectcount/TestObjectCountEventWithG1FullCollection.java new file mode 100644 index 0000000000000..cd70c05239ccb --- /dev/null +++ b/test/jdk/jdk/jfr/event/gc/objectcount/TestObjectCountEventWithG1FullCollection.java @@ -0,0 +1,22 @@ +package jdk.jfr.event.gc.objectcount; + +import jdk.test.lib.jfr.GCHelper; + +/** + * @test + * @requires vm.flagless + * @requires vm.hasJFR + * @requires (vm.gc == "G1" | vm.gc == null) + * & vm.opt.ExplicitGCInvokesConcurrent != true + * @library /test/lib /test/jdk + * @run main/othervm -XX:+UnlockExperimentalVMOptions + * -XX:-UseFastUnorderedTimeStamps -XX:+UseG1GC -XX:MarkSweepDeadRatio=0 + * -XX:-UseCompressedOops -XX:-UseCompressedClassPointers + * -XX:+IgnoreUnrecognizedVMOptions + * jdk.jfr.event.gc.objectcount.TestObjectCountEventWithG1FullCollection + */ +public class TestObjectCountEventWithG1FullCollection { + public static void main(String[] args) throws Exception { + ObjectCountEvent.test(GCHelper.gcG1Full); + } +} diff --git a/test/jdk/jdk/jfr/event/gc/objectcount/TestObjectCountEventWithParallelOld.java b/test/jdk/jdk/jfr/event/gc/objectcount/TestObjectCountEventWithParallelOld.java new file mode 100644 index 0000000000000..421c356a31579 --- /dev/null +++ b/test/jdk/jdk/jfr/event/gc/objectcount/TestObjectCountEventWithParallelOld.java @@ -0,0 +1,21 @@ +package jdk.jfr.event.gc.objectcount; + +import jdk.test.lib.jfr.GCHelper; + +/** + * @test + * @requires vm.flagless + * @requires vm.hasJFR + * @requires vm.gc == "Parallel" | vm.gc == null + * @library /test/lib /test/jdk + * @run main/othervm -XX:+UnlockExperimentalVMOptions + * -XX:-UseFastUnorderedTimeStamps -XX:+UseParallelGC + * -XX:MarkSweepDeadRatio=0 -XX:-UseCompressedOops + * -XX:-UseCompressedClassPointers -XX:+IgnoreUnrecognizedVMOptions + * jdk.jfr.event.gc.objectcount.TestObjectCountEventWithParallelOld + */ +public class TestObjectCountEventWithParallelOld { + public static void main(String[] args) throws Exception { + ObjectCountEvent.test(GCHelper.gcParallelOld); + } +} diff --git a/test/jdk/jdk/jfr/event/gc/objectcount/TestObjectCountEventWithShenandoah.java b/test/jdk/jdk/jfr/event/gc/objectcount/TestObjectCountEventWithShenandoah.java new file mode 100644 index 0000000000000..3191ebb43d3f2 --- /dev/null +++ b/test/jdk/jdk/jfr/event/gc/objectcount/TestObjectCountEventWithShenandoah.java @@ -0,0 +1,17 @@ +package jdk.jfr.event.gc.objectcount; +import jdk.test.lib.jfr.GCHelper; + +/** + * @test + * @requires vm.flagless + * @requires vm.hasJFR + * @requires (vm.gc == "Shenandoah" | vm.gc == null) + * & vm.opt.ExplicitGCInvokesConcurrent != false + * @library /test/lib /test/jdk + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:+ExplicitGCInvokesConcurrent -XX:MarkSweepDeadRatio=0 -XX:-UseCompressedOops -XX:-UseCompressedClassPointers -XX:+IgnoreUnrecognizedVMOptions jdk.jfr.event.gc.objectcount.TestObjectCountEventWithShenandoah + */ +public class TestObjectCountEventWithShenandoah { + public static void main(String[] args) throws Exception { + ObjectCountEvent.test(GCHelper.gcShenandoah); + } +} diff --git a/test/lib/jdk/test/lib/jfr/GCHelper.java b/test/lib/jdk/test/lib/jfr/GCHelper.java index 07c6c1e1ce550..4b30e79ba0d43 100644 --- a/test/lib/jdk/test/lib/jfr/GCHelper.java +++ b/test/lib/jdk/test/lib/jfr/GCHelper.java @@ -74,6 +74,7 @@ public class GCHelper { public static final String gcPSMarkSweep = "PSMarkSweep"; public static final String gcParallelOld = "ParallelOld"; public static final String pauseLevelEvent = "GCPhasePauseLevel"; + public static final String gcShenandoah = "Shenandoah"; private static final List g1HeapRegionTypes; private static final List shenandoahHeapRegionStates;