Skip to content

Implement the JFR ObjectAllocationSample event - VM part - #24609

Open
LinHu2016 wants to merge 1 commit into
eclipse-openj9:masterfrom
LinHu2016:jfrAllocationSampling2
Open

LinHu2016 wants to merge 1 commit into
eclipse-openj9:masterfrom
LinHu2016:jfrAllocationSampling2

Conversation

@LinHu2016

@LinHu2016 LinHu2016 commented Aug 21, 2026 •

Copy link
Copy Markdown
Contributor

Base on JFR event specification (from SAP JFR Events
17
):

  • Define the event structure J9JFRObjectAllocationSample in
    j9nonbuilder.h and J9JFR_EVENT_TYPE_OBJECT_ALLOCATION_SAMPLE id in
    j9consts.h

  • Register a JFR-internal callback jfrObjectAllocationSample() on
    J9HOOK_MM_OBJECT_ALLOCATION_SAMPLING_INTERNAL in
    startJFRRecording(), implement the callback to write a
    J9JFRObjectAllocationSample event with
    a stack trace into the per-thread buffer, and unregister it in
    stopJFRRecording().

  • JFR specifies ObjectAllocationSample throttling in events-per-second
    (default: 150/s, profiling: 300/s). The GC layer works in bytes. A
    conversion is needed: from the JVM's current heap allocation rate
    (bytes/sec), derive a byte-granularity interval such that approximately
    N events/second are emitted. JVM startup uses a reasonable default
    byte interval 512KB. new objectAllocationSampleThrottleRate (throttle
    value (events/s)) in vm->jfrState.

  • Add a periodic recalibration step in jfrSamplingThreadProc()'s
    1-second tick block, Read total bytes allocated since last GC, Compute
    new byte interval: newInterval = allocatedBytes / (throttleRate *
    elapsedSinceLastGC).

#depends on: eclipse-omr/omr#8407
#depends on: #24774
#relate to: #24778
#fix: #24213

Signed-off-by: lhu linhu@ca.ibm.com

@LinHu2016
LinHu2016 force-pushed the jfrAllocationSampling2 branch 2 times, most recently from 19a978f to 878ab16 Compare August 24, 2026 16:03
@tajila
tajila requested review from dmitripivkine and tajila August 25, 2026 15:11
@LinHu2016
LinHu2016 force-pushed the jfrAllocationSampling2 branch 13 times, most recently from fcbcd85 to 3496f10 Compare August 27, 2026 14:13
@LinHu2016
LinHu2016 force-pushed the jfrAllocationSampling2 branch from 3496f10 to 9261d6f Compare August 28, 2026 15:38
@LinHu2016
LinHu2016 force-pushed the jfrAllocationSampling2 branch 8 times, most recently from 66940ae to c9fa930 Compare September 10, 2026 21:13
@LinHu2016
LinHu2016 force-pushed the jfrAllocationSampling2 branch 2 times, most recently from 7c315a1 to 63d9beb Compare September 15, 2026 16:29
@LinHu2016 LinHu2016 changed the title Implement the JFR ObjectAllocationSample event - step1 Implement the JFR ObjectAllocationSample event - VM part Sep 15, 2026
@LinHu2016
LinHu2016 force-pushed the jfrAllocationSampling2 branch 3 times, most recently from defe6aa to 2647565 Compare September 18, 2026 02:08
@LinHu2016

Copy link
Copy Markdown
Contributor Author

Hi @babsingh Could you please review the changes, Thanks

@LinHu2016

Copy link
Copy Markdown
Contributor Author

Optimize recalibration and integrate JFR standard settings/configuration in separate PRs.

@babsingh babsingh left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Also adding @gacholio to review this PR. They’re currently on vacation and will return next week.

Comment thread runtime/vm/jfr.cpp Outdated
Comment on lines +1169 to +1171
J9ArrayClass *arrayClass = (J9ArrayClass *)data->clazz;
U_8 *classLeafName = J9UTF8_DATA(J9ROMCLASS_CLASSNAME(arrayClass->leafComponentType->romClass));
UDATA lenClassLeafName = J9UTF8_LENGTH(J9ROMCLASS_CLASSNAME(arrayClass->leafComponentType->romClass));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

use tabs for indentation to match the coding standard

Comment thread runtime/vm/jfr.cpp Outdated
Comment on lines +1161 to +1166
MM_ObjectAllocationSamplingInternalEvent *data =
(MM_ObjectAllocationSamplingInternalEvent *)eventData;
J9VMThread *currentThread = data->currentThread;

U_8 *className = J9UTF8_DATA(J9ROMCLASS_CLASSNAME(data->clazz->romClass));
UDATA lenClassName = J9UTF8_LENGTH(J9ROMCLASS_CLASSNAME(data->clazz->romClass));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

these read-only locals be made const: data, className, the name lengths, and arrayClass/classLeafName.

Comment thread runtime/vm/jfr.cpp
Comment on lines +1954 to +1961
UDATA newInterval = ((0 != totalBytes) && (0 != lastGCEnd))
? (totalBytes * 1000000 / (elapsedMicros * throttleRate))
: J9JFR_OBJECT_ALLOCATION_SAMPLE_DEFAULT_INTERVAL;
if (newInterval < 1024) {
newInterval = 1024;
} else if (newInterval > (64 * 1024 * 1024)) {
newInterval = 64 * 1024 * 1024;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

macros must be defined for: 1000000, 1024 and 64, as per the coding standard, similar to

#define J9JFR_OBJECT_ALLOCATION_SAMPLE_DEFAULT_THROTTLE_RATE 150
#define J9JFR_OBJECT_ALLOCATION_SAMPLE_PROFILING_THROTTLE_RATE 300

Comment thread runtime/vm/jfr.cpp Outdated
#define J9JFR_GLOBAL_BUFFER_SIZE (10 * J9JFR_THREAD_BUFFER_SIZE)
#define J9JFR_SAMPLING_RATE 10
#define J9JFR_CLASSNAME_BUFFER_SIZE 128
#define J9TIME_NANOSECONDS_PER_SECOND (1000000000ULL)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

this doesn't appear to be used

Comment thread runtime/vm/jfr.cpp Outdated
Comment on lines +76 to +78
#define J9JFR_OBJECT_ALLOCATION_SAMPLE_DEFAULT_INTERVAL (512 * 1024) /* bytes; same as JVMTI default per JEP 331 */
#define J9JFR_OBJECT_ALLOCATION_SAMPLE_DEFAULT_THROTTLE_RATE 150 /* events per second */
#define J9JFR_OBJECT_ALLOCATION_SAMPLE_PROFILING_THROTTLE_RATE 300 /* events per second */

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

inconsistent spacing before the comment

Suggested change
#define J9JFR_OBJECT_ALLOCATION_SAMPLE_DEFAULT_INTERVAL (512 * 1024) /* bytes; same as JVMTI default per JEP 331 */
#define J9JFR_OBJECT_ALLOCATION_SAMPLE_DEFAULT_THROTTLE_RATE 150 /* events per second */
#define J9JFR_OBJECT_ALLOCATION_SAMPLE_PROFILING_THROTTLE_RATE 300 /* events per second */
#define J9JFR_OBJECT_ALLOCATION_SAMPLE_DEFAULT_INTERVAL (512 * 1024) /* bytes; same as JVMTI default per JEP 331 */
#define J9JFR_OBJECT_ALLOCATION_SAMPLE_DEFAULT_THROTTLE_RATE 150 /* events per second */
#define J9JFR_OBJECT_ALLOCATION_SAMPLE_PROFILING_THROTTLE_RATE 300 /* events per second */

Comment thread runtime/vm/jfr.cpp
Comment on lines +1187 to +1188
J9JFRObjectAllocationSample *jfrEvent = (J9JFRObjectAllocationSample *)reserveBufferWithStackTrace(
currentThread, currentThread, J9JFR_EVENT_TYPE_OBJECT_ALLOCATION_SAMPLE, sizeof(J9JFRObjectAllocationSample), 0);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

reserveBufferWithStackTrace() can eventually flush the JFR buffer, where allocateMemFromGlobalBuffer() releases VM access to acquire jfrBufferMutex. Since this callback is running from the allocation hook, can we confirm that it is valid for the subscriber to drop VM access here and that the allocator does not retain any GC sensitive refs across the hook dispatch? If not, this path needs to avoid dropping VM access or protect/update those references.

@gacholio for review

Comment thread runtime/vm/JFRChunkWriter.hpp Outdated
SystemGCID = 36,
YoungGarbageCollectionID = 38,
OldGarbageCollectionID = 39,
ObjectAllocationSampleID = 83, /* jdk.ObjectAllocationSample -- must match JFR metadata blob */

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

does this also work with JFRv2?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

for Java17 also JfrObjectAllocationSampleEvent = 83, but need to update for Java 25 JfrObjectAllocationSampleEvent = 91

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is this consistent (or correctly inconsistent, I suppose) with the RI? I suppose we only really care about LTS versions.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

for Java17 also JfrObjectAllocationSampleEvent = 83, but need to update for Java 25 JfrObjectAllocationSampleEvent = 91

In V1 it will be the same for all Java versions. Don't worry about V2 for now @thallium is working on a change that will use the generated IDs in V2.

Also, the comment /* jdk.ObjectAllocationSample -- must match... is not needed as it applies the same to all Ids in the enum.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Here's the PR for using generated IDs in JFR v2: #24839

Comment thread runtime/vm/jfr.cpp Outdated
Comment on lines +1172 to +1182
Trc_VM_jfrObjectAllocationSample_indexableObject(currentThread,
lenClassName,
className,
lenClassLeafName,
classLeafName,
data->weight,
data->objectSize);
} else {
Trc_VM_jfrObjectAllocationSample(currentThread,
lenClassName,
className,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

arguments should start on a new line

Comment thread runtime/vm/jfr.cpp Outdated
Comment on lines +1191 to +1192
jfrEvent->objectClass = data->clazz;
jfrEvent->weight = data->weight;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
jfrEvent->objectClass = data->clazz;
jfrEvent->weight = data->weight;
jfrEvent->objectClass = data->clazz;
jfrEvent->weight = data->weight;

Comment thread runtime/vm/jfr.cpp Outdated
}

/* enable JFRObjectAllocationSample */
vm->jfrState.objectAllocationSampleThrottleRate = J9JFR_OBJECT_ALLOCATION_SAMPLE_DEFAULT_THROTTLE_RATE;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
vm->jfrState.objectAllocationSampleThrottleRate = J9JFR_OBJECT_ALLOCATION_SAMPLE_DEFAULT_THROTTLE_RATE;
vm->jfrState.objectAllocationSampleThrottleRate = J9JFR_OBJECT_ALLOCATION_SAMPLE_DEFAULT_THROTTLE_RATE;

Comment thread runtime/vm/JFRChunkWriter.hpp Outdated
static constexpr int NETWORK_UTILIZATION_EVENT_SIZE = (4 * sizeof(U_64)) + sizeof(U_32);
static constexpr int DATA_LOSS_EVENT_SIZE = sizeof(U_8) + LEB128_32_SIZE + (3 * LEB128_64_SIZE);
static constexpr int THREAD_ALLOCATION_STATISTICS_EVENT_SIZE = sizeof(U_8) + LEB128_32_SIZE + (3 * LEB128_64_SIZE);
/* OBJECT_ALLOCATION_SAMPLE_EVENT_SIZE: eventSize(LEB128_64) + eventType(LEB128_32) + ticks(LEB128_64) + eventThread(LEB128_64) + stackTrace(LEB128_32) + objectClass(LEB128_32) + weight(LEB128_64) */

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

this comment is not needed

Comment thread runtime/vm/JFRConstantPoolTypes.hpp Outdated
I_64 ticks;
U_64 eventThreadIndex;
U_32 stackTraceIndex;
U_32 objectClassIndex; /**< class constant-pool index for the allocated object class */

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

same here

@LinHu2016
LinHu2016 force-pushed the jfrAllocationSampling2 branch 2 times, most recently from 6696a8e to 4f90688 Compare September 29, 2026 16:20
Base on JFR event specification (from [SAP JFR Events
17](https://sap.github.io/jfrevents/17.html#objectallocationsample)):

- Define the event structure J9JFRObjectAllocationSample in
j9nonbuilder.h and J9JFR_EVENT_TYPE_OBJECT_ALLOCATION_SAMPLE id in
j9consts.h

- Register a JFR-internal callback jfrObjectAllocationSample() on
J9HOOK_MM_OBJECT_ALLOCATION_SAMPLING_INTERNAL in
startJFRRecording(), implement the callback to write a
J9JFRObjectAllocationSample event with
a stack trace into the per-thread buffer, and unregister it in
stopJFRRecording().

- JFR specifies `ObjectAllocationSample` throttling in events-per-second
(default: 150/s, profiling: 300/s). The GC layer works in bytes. A
conversion is needed: from the JVM's current heap allocation rate
(bytes/sec), derive a byte-granularity interval such that approximately
`N` events/second are emitted. JVM startup uses a reasonable default
byte interval 512KB. new objectAllocationSampleThrottleRate (throttle
value (events/s)) in vm->jfrState.

- Add a periodic recalibration step in jfrSamplingThreadProc()'s
1-second tick block, Read total bytes allocated since last GC, Compute
new byte interval: newInterval = allocatedBytes / (throttleRate *
elapsedSinceLastGC).

Signed-off-by: lhu <linhu@ca.ibm.com>
@LinHu2016
LinHu2016 force-pushed the jfrAllocationSampling2 branch from 4f90688 to 76316ae Compare September 29, 2026 16:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

JFR: ObjectAllocationSample event

5 participants