Skip to content

Commit 502a524

Browse files
Markus GrönlundJaroslav Bachorik
andcommitted
8257602: Introduce JFR Event Throttling and new jdk.ObjectAllocationSample event (enabled by default)
Co-authored-by: Jaroslav Bachorik <jbachorik@openjdk.org> Reviewed-by: egahlin, jbachorik
1 parent 026b09c commit 502a524

39 files changed

+2096
-125
lines changed

make/src/classes/build/tools/jfr/GenerateJfrFiles.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ static class TypeElement {
172172
boolean startTime;
173173
String period = "";
174174
boolean cutoff;
175+
boolean throttle;
175176
boolean experimental;
176177
long id;
177178
boolean isEvent;
@@ -194,6 +195,7 @@ public void persist(DataOutputStream pos) throws IOException {
194195
pos.writeBoolean(startTime);
195196
pos.writeUTF(period);
196197
pos.writeBoolean(cutoff);
198+
pos.writeBoolean(throttle);
197199
pos.writeBoolean(experimental);
198200
pos.writeLong(id);
199201
pos.writeBoolean(isEvent);
@@ -490,6 +492,7 @@ public void startElement(String uri, String localName, String qName, Attributes
490492
currentType.startTime = getBoolean(attributes, "startTime", true);
491493
currentType.period = getString(attributes, "period");
492494
currentType.cutoff = getBoolean(attributes, "cutoff", false);
495+
currentType.throttle = getBoolean(attributes, "throttle", false);
493496
currentType.commitState = getString(attributes, "commitState");
494497
currentType.isEvent = "Event".equals(qName);
495498
currentType.isRelation = "Relation".equals(qName);
@@ -759,6 +762,7 @@ private static void printJfrEventClassesHpp(Metadata metadata, File outputFile)
759762
out.write(" void set_starttime(const Ticks&) const {}");
760763
out.write(" void set_endtime(const Ticks&) const {}");
761764
out.write(" bool should_commit() const { return false; }");
765+
out.write(" bool is_started() const { return false; }");
762766
out.write(" static bool is_enabled() { return false; }");
763767
out.write(" void commit() {}");
764768
out.write("};");
@@ -820,6 +824,7 @@ private static void printEvent(Printer out, TypeElement event, boolean empty) {
820824
out.write(" static const bool hasStackTrace = " + event.stackTrace + ";");
821825
out.write(" static const bool isInstant = " + !event.startTime + ";");
822826
out.write(" static const bool hasCutoff = " + event.cutoff + ";");
827+
out.write(" static const bool hasThrottle = " + event.throttle + ";");
823828
out.write(" static const bool isRequestable = " + !event.period.isEmpty() + ";");
824829
out.write(" static const JfrEventId eventId = Jfr" + event.name + "Event;");
825830
out.write("");

src/hotspot/share/gc/shared/allocTracer.cpp

Lines changed: 80 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2013, 2020, 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
@@ -24,14 +24,86 @@
2424

2525
#include "precompiled.hpp"
2626
#include "gc/shared/allocTracer.hpp"
27+
#include "gc/shared/threadLocalAllocBuffer.inline.hpp"
2728
#include "jfr/jfrEvents.hpp"
28-
#include "runtime/handles.hpp"
2929
#include "utilities/globalDefinitions.hpp"
3030
#include "utilities/macros.hpp"
3131
#if INCLUDE_JFR
3232
#include "jfr/support/jfrAllocationTracer.hpp"
3333
#endif
3434

35+
static THREAD_LOCAL int64_t _last_allocated_bytes = 0;
36+
37+
inline void send_allocation_sample(const Klass* klass, int64_t allocated_bytes) {
38+
assert(allocated_bytes > 0, "invariant");
39+
EventObjectAllocationSample event;
40+
if (event.should_commit()) {
41+
const size_t weight = allocated_bytes - _last_allocated_bytes;
42+
assert(weight > 0, "invariant");
43+
event.set_objectClass(klass);
44+
event.set_weight(weight);
45+
event.commit();
46+
_last_allocated_bytes = allocated_bytes;
47+
}
48+
}
49+
50+
inline bool send_allocation_sample_with_result(const Klass* klass, int64_t allocated_bytes) {
51+
assert(allocated_bytes > 0, "invariant");
52+
EventObjectAllocationSample event;
53+
if (event.should_commit()) {
54+
const size_t weight = allocated_bytes - _last_allocated_bytes;
55+
assert(weight > 0, "invariant");
56+
event.set_objectClass(klass);
57+
event.set_weight(weight);
58+
event.commit();
59+
_last_allocated_bytes = allocated_bytes;
60+
return true;
61+
}
62+
return false;
63+
}
64+
65+
inline intptr_t estimate_tlab_size_bytes(Thread* thread) {
66+
assert(thread != NULL, "invariant");
67+
const size_t desired_tlab_size_bytes = thread->tlab().desired_size() * HeapWordSize;
68+
const size_t alignment_reserve_bytes = thread->tlab().alignment_reserve_in_bytes();
69+
assert(desired_tlab_size_bytes > alignment_reserve_bytes, "invariant");
70+
return static_cast<intptr_t>(desired_tlab_size_bytes - alignment_reserve_bytes);
71+
}
72+
73+
inline int64_t load_allocated_bytes(Thread* thread) {
74+
const int64_t allocated_bytes = thread->allocated_bytes();
75+
if (allocated_bytes < _last_allocated_bytes) {
76+
// A hw thread can detach and reattach to the VM, and when it does,
77+
// it gets a new JavaThread representation. The thread local variable
78+
// tracking _last_allocated_bytes is mapped to the existing hw thread,
79+
// so it needs to be reset.
80+
_last_allocated_bytes = 0;
81+
}
82+
return allocated_bytes == _last_allocated_bytes ? 0 : allocated_bytes;
83+
}
84+
85+
// To avoid large objects from being undersampled compared to the regular TLAB samples,
86+
// the data amount is normalized as if it was a TLAB, giving a number of TLAB sampling attempts to the large object.
87+
static void normalize_as_tlab_and_send_allocation_samples(Klass* klass, intptr_t obj_alloc_size_bytes, Thread* thread) {
88+
const int64_t allocated_bytes = load_allocated_bytes(thread);
89+
assert(allocated_bytes > 0, "invariant"); // obj_alloc_size_bytes is already attributed to allocated_bytes at this point.
90+
if (!UseTLAB) {
91+
send_allocation_sample(klass, allocated_bytes);
92+
return;
93+
}
94+
const intptr_t tlab_size_bytes = estimate_tlab_size_bytes(thread);
95+
if (allocated_bytes - _last_allocated_bytes < tlab_size_bytes) {
96+
return;
97+
}
98+
assert(obj_alloc_size_bytes > 0, "invariant");
99+
do {
100+
if (send_allocation_sample_with_result(klass, allocated_bytes)) {
101+
return;
102+
}
103+
obj_alloc_size_bytes -= tlab_size_bytes;
104+
} while (obj_alloc_size_bytes > 0);
105+
}
106+
35107
void AllocTracer::send_allocation_outside_tlab(Klass* klass, HeapWord* obj, size_t alloc_size, Thread* thread) {
36108
JFR_ONLY(JfrAllocationTracer tracer(obj, alloc_size, thread);)
37109
EventObjectAllocationOutsideTLAB event;
@@ -40,6 +112,7 @@ void AllocTracer::send_allocation_outside_tlab(Klass* klass, HeapWord* obj, size
40112
event.set_allocationSize(alloc_size);
41113
event.commit();
42114
}
115+
normalize_as_tlab_and_send_allocation_samples(klass, static_cast<intptr_t>(alloc_size), thread);
43116
}
44117

45118
void AllocTracer::send_allocation_in_new_tlab(Klass* klass, HeapWord* obj, size_t tlab_size, size_t alloc_size, Thread* thread) {
@@ -51,6 +124,11 @@ void AllocTracer::send_allocation_in_new_tlab(Klass* klass, HeapWord* obj, size_
51124
event.set_tlabSize(tlab_size);
52125
event.commit();
53126
}
127+
const int64_t allocated_bytes = load_allocated_bytes(thread);
128+
if (allocated_bytes == 0) {
129+
return;
130+
}
131+
send_allocation_sample(klass, allocated_bytes);
54132
}
55133

56134
void AllocTracer::send_allocation_requiring_gc_event(size_t size, uint gcId) {

src/hotspot/share/jfr/jni/jfrJniMethod.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
#include "jfr/recorder/repository/jfrRepository.hpp"
3636
#include "jfr/recorder/repository/jfrChunkRotation.hpp"
3737
#include "jfr/recorder/repository/jfrChunkWriter.hpp"
38+
#include "jfr/recorder/service/jfrEventThrottler.hpp"
3839
#include "jfr/recorder/service/jfrOptionSet.hpp"
3940
#include "jfr/recorder/stacktrace/jfrStackTraceRepository.hpp"
4041
#include "jfr/recorder/stringpool/jfrStringPool.hpp"
@@ -175,6 +176,11 @@ NO_TRANSITION(jboolean, jfr_set_cutoff(JNIEnv* env, jobject jvm, jlong event_typ
175176
return JfrEventSetting::set_cutoff(event_type_id, cutoff_ticks) ? JNI_TRUE : JNI_FALSE;
176177
NO_TRANSITION_END
177178

179+
NO_TRANSITION(jboolean, jfr_set_throttle(JNIEnv* env, jobject jvm, jlong event_type_id, jlong event_sample_size, jlong period_ms))
180+
JfrEventThrottler::configure(static_cast<JfrEventId>(event_type_id), event_sample_size, period_ms);
181+
return JNI_TRUE;
182+
NO_TRANSITION_END
183+
178184
NO_TRANSITION(jboolean, jfr_should_rotate_disk(JNIEnv* env, jobject jvm))
179185
return JfrChunkRotation::should_rotate() ? JNI_TRUE : JNI_FALSE;
180186
NO_TRANSITION_END

src/hotspot/share/jfr/jni/jfrJniMethod.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,8 @@ jlong JNICALL jfr_get_unloaded_event_classes_count(JNIEnv* env, jobject jvm);
132132

133133
jboolean JNICALL jfr_set_cutoff(JNIEnv* env, jobject jvm, jlong event_type_id, jlong cutoff_ticks);
134134

135+
jboolean JNICALL jfr_set_throttle(JNIEnv* env, jobject jvm, jlong event_type_id, jlong event_sample_size, jlong period_ms);
136+
135137
void JNICALL jfr_emit_old_object_samples(JNIEnv* env, jobject jvm, jlong cutoff_ticks, jboolean, jboolean);
136138

137139
jboolean JNICALL jfr_should_rotate_disk(JNIEnv* env, jobject jvm);

src/hotspot/share/jfr/jni/jfrJniMethodRegistration.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ JfrJniMethodRegistration::JfrJniMethodRegistration(JNIEnv* env) {
8181
(char*)"setForceInstrumentation", (char*)"(Z)V", (void*)jfr_set_force_instrumentation,
8282
(char*)"getUnloadedEventClassCount", (char*)"()J", (void*)jfr_get_unloaded_event_classes_count,
8383
(char*)"setCutoff", (char*)"(JJ)Z", (void*)jfr_set_cutoff,
84+
(char*)"setThrottle", (char*)"(JJJ)Z", (void*)jfr_set_throttle,
8485
(char*)"emitOldObjectSamples", (char*)"(JZZ)V", (void*)jfr_emit_old_object_samples,
8586
(char*)"shouldRotateDisk", (char*)"()Z", (void*)jfr_should_rotate_disk,
8687
(char*)"exclude", (char*)"(Ljava/lang/Thread;)V", (void*)jfr_exclude_thread,

src/hotspot/share/jfr/leakprofiler/sampling/objectSampler.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ void ObjectSampler::sample(HeapWord* obj, size_t allocated, JavaThread* thread)
175175
record_stacktrace(thread);
176176
// try enter critical section
177177
JfrTryLock tryLock(&_lock);
178-
if (!tryLock.has_lock()) {
178+
if (!tryLock.acquired()) {
179179
log_trace(jfr, oldobject, sampling)("Skipping old object sample due to lock contention");
180180
return;
181181
}

src/hotspot/share/jfr/metadata/metadata.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -614,6 +614,12 @@
614614
<Field type="ulong" contentType="bytes" name="allocationSize" label="Allocation Size" />
615615
</Event>
616616

617+
<Event name="ObjectAllocationSample" category="Java Application" label="Object Allocation Sample" thread="true" stackTrace="true" startTime="false" throttle="true">
618+
<Field type="Class" name="objectClass" label="Object Class" description="Class of allocated object" />
619+
<Field type="long" contentType="bytes" name="weight" label="Sample Weight"
620+
description="The relative weight of the sample. Aggregating the weights for a large number of samples, for a particular class, thread or stack trace, gives a statistically accurate representation of the allocation pressure" />
621+
</Event>
622+
617623
<Event name="OldObjectSample" category="Java Virtual Machine, Profiling" label="Old Object Sample" description="A potential memory leak" stackTrace="true" thread="true"
618624
startTime="false" cutoff="true">
619625
<Field type="Ticks" name="allocationTime" label="Allocation Time" />

src/hotspot/share/jfr/metadata/metadata.xsd

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@
7070
<xs:attribute name="stackTrace" type="xs:boolean" use="optional" />
7171
<xs:attribute name="period" type="periodType" use="optional" />
7272
<xs:attribute name="cutoff" type="xs:boolean" use="optional" />
73+
<xs:attribute name="throttle" type="xs:boolean" use="optional" />
7374
<xs:attribute name="commitState" type="xs:string" use="optional" />
7475
</xs:complexType>
7576
</xs:element>

src/hotspot/share/jfr/recorder/jfrRecorder.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#include "jfr/recorder/jfrRecorder.hpp"
3434
#include "jfr/recorder/checkpoint/jfrCheckpointManager.hpp"
3535
#include "jfr/recorder/repository/jfrRepository.hpp"
36+
#include "jfr/recorder/service/jfrEventThrottler.hpp"
3637
#include "jfr/recorder/service/jfrOptionSet.hpp"
3738
#include "jfr/recorder/service/jfrPostBox.hpp"
3839
#include "jfr/recorder/service/jfrRecorderService.hpp"
@@ -289,6 +290,9 @@ bool JfrRecorder::create_components() {
289290
if (!create_thread_sampling()) {
290291
return false;
291292
}
293+
if (!create_event_throttler()) {
294+
return false;
295+
}
292296
return true;
293297
}
294298

@@ -362,6 +366,10 @@ bool JfrRecorder::create_thread_sampling() {
362366
return _thread_sampling != NULL;
363367
}
364368

369+
bool JfrRecorder::create_event_throttler() {
370+
return JfrEventThrottler::create();
371+
}
372+
365373
void JfrRecorder::destroy_components() {
366374
JfrJvmtiAgent::destroy();
367375
if (_post_box != NULL) {
@@ -396,6 +404,7 @@ void JfrRecorder::destroy_components() {
396404
JfrThreadSampling::destroy();
397405
_thread_sampling = NULL;
398406
}
407+
JfrEventThrottler::destroy();
399408
}
400409

401410
bool JfrRecorder::create_recorder_thread() {

src/hotspot/share/jfr/recorder/jfrRecorder.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ class JfrRecorder : public JfrCHeapObj {
5353
static bool create_storage();
5454
static bool create_stringpool();
5555
static bool create_thread_sampling();
56+
static bool create_event_throttler();
5657
static bool create_components();
5758
static void destroy_components();
5859
static void on_recorder_thread_exit();

0 commit comments

Comments
 (0)