Skip to content
3 changes: 3 additions & 0 deletions src/hotspot/share/gc/shared/gcCause.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,9 @@ const char* GCCause::to_string(GCCause::Cause cause) {
case _shenandoah_allocation_failure_evac:
return "Allocation Failure During Evacuation";

case _shenandoah_jfr_object_count:
return "JFR Object Count Event Enabled";

case _shenandoah_humongous_allocation_failure:
return "Humongous Allocation Failure";

Expand Down
1 change: 1 addition & 0 deletions src/hotspot/share/gc/shared/gcCause.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ class GCCause : public AllStatic {
_shenandoah_humongous_allocation_failure,
_shenandoah_concurrent_gc,
_shenandoah_upgrade_to_full_gc,
_shenandoah_jfr_object_count,

_z_timer,
_z_warmup,
Expand Down
4 changes: 2 additions & 2 deletions src/hotspot/share/gc/shared/gcTrace.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,8 @@ class GCTracer {
void report_gc_reference_stats(const ReferenceProcessorStats& rp) const;

// Sends event data to the ObjectCount and/or ObjectCountAfterGC event
template <typename T>
void report_object_count(T* heap) NOT_SERVICES_RETURN;
template <typename T, class 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
14 changes: 9 additions & 5 deletions src/hotspot/share/gc/shared/gcTrace.inline.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,10 @@ class ObjectCountEventSenderClosure : public KlassInfoClosure {
virtual void do_cinfo(KlassInfoEntry* entry) {
if (should_send_event(entry)) {
ObjectCountEventSender::send<Event>(entry, _timestamp);
_cit->delete_entry(entry, &_total_size_in_words);
}
// Delete the entry even if we don't send it. This ensure live objects that
// weren't sent in a previous event emission are not monotonically increasing.
_cit->delete_entry(entry);
}

private:
Expand All @@ -38,17 +40,19 @@ class ObjectCountEventSenderClosure : public KlassInfoClosure {
}
};

template <typename T>
void GCTracer::report_object_count(T* heap) {
if (!ObjectCountEventSender::should_send_event<EventObjectCountAfterGC>()) {
template <typename T, class Event>
void GCTracer::report_object_count() {
if (!ObjectCountEventSender::should_send_event<Event>()) {
return;
}

T* heap = T::heap();
Comment thread
pf0n marked this conversation as resolved.
KlassInfoTable* cit = heap->get_cit();

if (!cit->allocation_failed()) {
ObjectCountEventSenderClosure<EventObjectCountAfterGC> event_sender(cit->size_of_instances_in_words(), Ticks::now(), cit);
ObjectCountEventSenderClosure<Event> event_sender(cit->size_of_instances_in_words(), Ticks::now(), cit);
cit->iterate(&event_sender);
cit->reset_size_of_instances_in_words();
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,7 @@ void ShenandoahConcurrentGC::vmop_entry_final_mark() {

// 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);
heap->tracer()->report_object_count<ShenandoahHeap, EventObjectCountAfterGC>();
}

void ShenandoahConcurrentGC::vmop_entry_init_update_refs() {
Expand Down
3 changes: 2 additions & 1 deletion src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ class ShenandoahConcurrentGC : public ShenandoahGC {
public:
ShenandoahConcurrentGC(ShenandoahGeneration* generation, bool do_old_gc_bootstrap);

bool collect(GCCause::Cause cause) override;
// ShenandoahJFRObjectCountGC will override ShenandoahConcurrentGC's collect method if enabled.
virtual bool collect(GCCause::Cause cause) override;
Comment thread
pf0n marked this conversation as resolved.
ShenandoahDegenPoint degen_point() const;

void entry_concurrent_update_refs_prepare(ShenandoahHeap* heap);
Expand Down
3 changes: 2 additions & 1 deletion src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,8 @@ void ShenandoahMarkConcurrentRootsTask<GENERATION>::work(uint worker_id) {
nullptr : _old_queue_set->queue(worker_id);

// Use object counting closure if ObjectCountAfterGC event is enabled
bool object_count_enabled = ObjectCountEventSender::should_send_event<EventObjectCountAfterGC>();
const bool object_count_enabled = ObjectCountEventSender::should_send_event<EventObjectCountAfterGC>() ||
ObjectCountEventSender::should_send_event<EventObjectCount>();
if (object_count_enabled) {
KlassInfoTable* const main_cit = ShenandoahHeap::heap()->get_cit();
KlassInfoTable local_cit(false);
Expand Down
43 changes: 42 additions & 1 deletion src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
*
*/

#include "gc/shared/gcTrace.inline.hpp"
#include "gc/shared/objectCountEventSender.inline.hpp"
#include "gc/shenandoah/heuristics/shenandoahHeuristics.hpp"
#include "gc/shenandoah/mode/shenandoahMode.hpp"
#include "gc/shenandoah/shenandoahCollectorPolicy.hpp"
Expand All @@ -34,6 +36,7 @@
#include "gc/shenandoah/shenandoahGeneration.hpp"
#include "gc/shenandoah/shenandoahHeap.inline.hpp"
#include "gc/shenandoah/shenandoahMonitoringSupport.hpp"
#include "gc/shenandoah/shenandoahJfrObjectCountGC.hpp"
#include "gc/shenandoah/shenandoahUtils.hpp"
#include "logging/log.hpp"
#include "memory/metaspaceStats.hpp"
Expand All @@ -48,6 +51,8 @@ ShenandoahControlThread::ShenandoahControlThread() :
}

void ShenandoahControlThread::run_service() {
jlong jfr_event_timestamp = os::javaTimeMillis();

ShenandoahHeap* const heap = ShenandoahHeap::heap();
const GCMode default_mode = concurrent_normal;
const GCCause::Cause default_cause = GCCause::_shenandoah_concurrent_gc;
Expand Down Expand Up @@ -174,6 +179,9 @@ void ShenandoahControlThread::run_service() {

// If this cycle completed without being cancelled, notify waiters about it
if (!heap->cancelled_gc()) {
if (ObjectCountEventSender::should_send_event<EventObjectCount>()) {
jfr_event_timestamp = os::javaTimeMillis();
}
notify_alloc_failure_waiters();
}

Expand Down Expand Up @@ -229,6 +237,24 @@ void ShenandoahControlThread::run_service() {

// Print Metaspace change following GC (if logging is enabled).
MetaspaceUtils::print_metaspace_change(meta_sizes);
} else {
if (ObjectCountEventSender::should_send_event<EventObjectCount>() &&
(os::javaTimeMillis() - jfr_event_timestamp > (jlong) ShenandoahJFRObjectCountInterval)) {
GCIdMark gc_id_mark;

// Do not increment the GC ID:
// size_t current_gc_id = get_gc_id();
// GCIdMark gc_id_mark(get_gc_id());

const GCCause::Cause count_cause = GCCause::_shenandoah_jfr_object_count;
service_object_count_cycle(count_cause);

// Reset GC Cycle
heap->phase_timings()->flush_par_workers_to_cycle();
heap->phase_timings()->flush_cycle_to_global();

jfr_event_timestamp = os::javaTimeMillis();
}
}

// Check if we have seen a new target for soft max heap size or if a gc was requested.
Expand All @@ -253,7 +279,6 @@ void ShenandoahControlThread::run_service() {
}
os::naked_short_sleep(sleep);
}

heap->set_cit(nullptr);
}

Expand Down Expand Up @@ -362,6 +387,22 @@ void ShenandoahControlThread::service_stw_degenerated_cycle(GCCause::Cause cause
gc.collect(cause);
}

void ShenandoahControlThread::service_object_count_cycle(GCCause::Cause cause) {
ShenandoahHeap* heap = ShenandoahHeap::heap();

// Create a GC Session so marking will work
ShenandoahGCSession session(cause, heap->global_generation());

TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters());

ShenandoahJfrObjectCountGC gc(heap->global_generation());

if (gc.collect(cause)) {
heap->tracer()->report_object_count<ShenandoahHeap, EventObjectCount>();
}
}


void ShenandoahControlThread::request_gc(GCCause::Cause cause) {
if (ShenandoahCollectorPolicy::should_handle_requested_gc(cause)) {
handle_requested_gc(cause);
Expand Down
2 changes: 2 additions & 0 deletions src/hotspot/share/gc/shenandoah/shenandoahControlThread.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ class ShenandoahControlThread: public ShenandoahController {
void service_concurrent_normal_cycle(GCCause::Cause cause);
void service_stw_full_cycle(GCCause::Cause cause);
void service_stw_degenerated_cycle(GCCause::Cause cause, ShenandoahGC::ShenandoahDegenPoint point);
// Service cycle for counting live objects
void service_object_count_cycle(GCCause::Cause cause);
Comment thread
pf0n marked this conversation as resolved.

void notify_gc_waiters();

Expand Down
57 changes: 57 additions & 0 deletions src/hotspot/share/gc/shenandoah/shenandoahJfrObjectCountGC.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#include "gc/shenandoah/shenandoahHeap.inline.hpp"
#include "gc/shenandoah/shenandoahJfrObjectCountGC.hpp"
#include "prims/jvmtiTagMap.hpp"
#include "gc/shenandoah/shenandoahVerifier.hpp"
#include "gc/shenandoah/shenandoahPhaseTimings.hpp"
#include "gc/shenandoah/shenandoahUtils.hpp"

void ShenandoahJfrObjectCountGC::op_final_mark() {
ShenandoahHeap* const heap = ShenandoahHeap::heap();
assert(ShenandoahSafepoint::is_at_shenandoah_safepoint(), "Should be at safepoint");
assert(!heap->has_forwarded_objects(), "No forwarded objects on this path");

if (ShenandoahVerify) {
heap->verifier()->verify_roots_no_forwarded();
}

if (!heap->cancelled_gc()) {
_mark.finish_mark();

// Notify JVMTI that the tagmap table will need cleaning.
JvmtiTagMap::set_needs_cleaning();

if (ShenandoahVerify) {
ShenandoahTimingsTracker v(ShenandoahPhaseTimings::final_mark_verify);
heap->verifier()->verify_after_concmark();
}
}

{
ShenandoahTimingsTracker timing(ShenandoahPhaseTimings::final_mark_propagate_gc_state);
heap->propagate_gc_state_to_all_threads();
}
}

bool ShenandoahJfrObjectCountGC::collect(GCCause::Cause cause) {
ShenandoahHeap* const heap = ShenandoahHeap::heap();

entry_reset();

vmop_entry_init_mark();

entry_scan_remembered_set();

entry_mark_roots();
if (check_cancellation_and_abort(ShenandoahDegenPoint::_degenerated_roots)) {
return false;
}

entry_mark();
if (check_cancellation_and_abort(ShenandoahDegenPoint::_degenerated_mark)) {
return false;
}

vmop_entry_final_mark();
entry_reset_after_collect();
return true;
}
25 changes: 25 additions & 0 deletions src/hotspot/share/gc/shenandoah/shenandoahJfrObjectCountGC.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#ifndef SHARE_GC_SHENANDOAH_SHENANDOAHOBJECTCOUNTGC_HPP
#define SHARE_GC_SHENANDOAH_SHENANDOAHOBJECTCOUNTGC_HPP

#include "gc/shared/gcCause.hpp"
#include "gc/shenandoah/shenandoahConcurrentGC.hpp"
#include "gc/shenandoah/shenandoahConcurrentMark.hpp"
#include "gc/shenandoah/shenandoahGC.hpp"
#include "gc/shenandoah/shenandoahHeap.hpp"

// A pseudo Shenandoah GC where only the marking phase is initiated
// without any memory reclamation. This GC is used specifically for
// the Java Flight Recorder's ObjectCount event.
class ShenandoahJfrObjectCountGC : public ShenandoahConcurrentGC {
public:
ShenandoahJfrObjectCountGC(ShenandoahGeneration* generation, bool do_old_gc_bootstrap = false)
: ShenandoahConcurrentGC(generation, do_old_gc_bootstrap) {}

// Only make use of the mark phase in Shenandoah and does not reclaim any garbage
bool collect(GCCause::Cause cause) override;

protected:
void op_final_mark() override;
};

#endif // SHARE_GC_SHENANDOAH_SHENANDOAHOBJECTCOUNTGC_HPP
3 changes: 2 additions & 1 deletion src/hotspot/share/gc/shenandoah/shenandoahMark.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ void ShenandoahMark::mark_loop_prework(uint w, TaskTerminator *t, ShenandoahRefe
mark_loop_work<Closure, GENERATION, CANCELLABLE, STRING_DEDUP>(&cl, ld, w, t, req);
} else {
// Use object counting closure if ObjectCountAfterGC event is enabled
bool object_count_enabled = ObjectCountEventSender::should_send_event<EventObjectCountAfterGC>();
const bool object_count_enabled = ObjectCountEventSender::should_send_event<EventObjectCountAfterGC>() ||
ObjectCountEventSender::should_send_event<EventObjectCount>();
if (object_count_enabled) {
KlassInfoTable* const main_cit = ShenandoahHeap::heap()->get_cit();
KlassInfoTable local_cit(false);
Expand Down
3 changes: 2 additions & 1 deletion src/hotspot/share/gc/shenandoah/shenandoahSTWMark.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,8 @@ void ShenandoahSTWMark::mark_roots(uint worker_id) {
switch (_generation->type()) {
case NON_GEN: {
// Use object counting closure if ObjectCountAfterGC event is enabled
bool object_count_enabled = ObjectCountEventSender::should_send_event<EventObjectCountAfterGC>();
const bool object_count_enabled = ObjectCountEventSender::should_send_event<EventObjectCountAfterGC>() ||
ObjectCountEventSender::should_send_event<EventObjectCount>();
if (object_count_enabled) {
KlassInfoTable* const main_cit = ShenandoahHeap::heap()->get_cit();
KlassInfoTable local_cit(false);
Expand Down
5 changes: 5 additions & 0 deletions src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -530,6 +530,11 @@
"to prevent starvation of the old collector. Setting this to " \
"0 will allow back to back young collections to run during old " \
"collections.") \
\
product(uintx, ShenandoahJFRObjectCountInterval, 0, \
"Duration in milliseconds to run object counting GC cycles. " \
"0 means disabled.") \
range(0, max_uintx) \
// end of GC_SHENANDOAH_FLAGS

#endif // SHARE_GC_SHENANDOAH_SHENANDOAH_GLOBALS_HPP
8 changes: 5 additions & 3 deletions src/hotspot/share/memory/heapInspection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -280,11 +280,13 @@ bool KlassInfoTable::merge_entry(const KlassInfoEntry* cie) {
return false;
}

void KlassInfoTable::delete_entry(KlassInfoEntry* entry, size_t* total_table_size) {
void KlassInfoTable::reset_size_of_instances_in_words() {
_size_of_instances_in_words = 0;
}

void KlassInfoTable::delete_entry(KlassInfoEntry* entry) {
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 {
Expand Down
6 changes: 5 additions & 1 deletion src/hotspot/share/memory/heapInspection.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,11 @@ class KlassInfoTable: public StackObj {
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);
void delete_entry(KlassInfoEntry* entry);
// Reset the size of instances in words to 0. Only call this method
// after sending the KlassInfoTable for the ObjectCount or
// ObjectCountAfterGC event and deleting it's entries.
void reset_size_of_instances_in_words();
friend class KlassInfoHisto;
friend class KlassHierarchy;
};
Expand Down
3 changes: 1 addition & 2 deletions test/jdk/jdk/jfr/event/gc/objectcount/ObjectCountEvent.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,11 @@ public static void test(String gcName) throws Exception {
recording.start();

Path tempFile = Path.of("temp.jfr");
Asserts.assertFalse(Files.exists(tempFile), "File already exists.");
recording.dump(tempFile); // Forces chunk rotation
System.gc();
recording.stop();

Files.deleteIfExists(tempFile);

System.out.println("gcName=" + gcName);
List<RecordedEvent> events = Events.fromRecording(recording);
for (RecordedEvent event : events) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
* @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
* @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:ShenandoahJFRObjectCountInterval=300 -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 {
Expand Down
Loading