Manifestation:
[4.642s][warning][jfr,system] Unable to commit. Requested size 24 too large
[4.642s][warning][jfr,system] Unable to commit. Requested size 27 too large
[4.642s][warning][jfr,system] Unable to commit. Requested size 27 too large
[4.643s][warning][jfr,system] Unable to commit. Requested size 27 too large
[4.643s][warning][jfr,system] Unable to commit. Requested size 24 too large
[4.643s][warning][jfr,system] Unable to commit. Requested size 27 too large
.,,
...
<non-deterministic crash point>
Reason:
Threads::create_vm() {
...
// Notify JVMTI agents that VM initialization is complete - nop if no agents.
JvmtiExport::post_vm_initialized();
JFR_ONLY(Jfr::on_vm_start();)
...
}
The valid offsets for the JfrJavaEventWriter are currently determined and setup during Jfr::on_vm_start().
When JFR initializes, there will be a lot of events of type jdk.ActiveSettingEvent generated. In fact, there will be enough of these events written to induce a flush of the thread local buffer. At the point of flush, because the correct offsets for the JfrJavaEventWriter are not yet properly setup, the JfrJavaEventWriter instance is not updated with new positions. This leads to the warning messages about "Unable to commit".
Unfortunately it gets worse: the offsets are statically initialized to "invalid_offset" (-1). But there are no assertions to validate that offsets have been properly initialized (i.e non "invalid_offset").
Because the oopDesc::long_field_put(int offset, jlong value) takes an int as the offset, a value of -1 is perfectly acceptable. So the value is written to oop - (1 * HeapWord), corrupting the memory address just prior to the oop.
Fix:
JfrJavaEventWriter::initialize() should move to JfrRecorder::create() to cover early initialization as well. Asserts should be added to the flush mechanism to verify correct offsets.
Longer term, maybe setting a field in an oop using a relative offset should not accept signed values, but only unsigned.