Hi Jaroslav,
On 31/05/2023 9:12 pm, Jaroslav Bachorík wrote:
Dear Team,
I've been investigating the unusual JVM crashes occurring in JVMTI calls
on a J9 JVM. During my investigation, I scrutinized the `jmethodID`
definition closely, available here: [jmethodID
definition](https://docs.oracle.com/en/java/javase/17/docs/specs/jvmti.html#jmethodID <https://docs.oracle.com/en/java/javase/17/docs/specs/jvmti.html#jmethodID>).
To paraphrase, the definition suggests that `jmethodID` identifies a
Java method, initializer, or constructor. These identifiers, once
returned by JVM TI functions and events, can be safely stored. However,
when the class is unloaded, they become invalid, rendering them
unsuitable for use.
My interpretation is that the JVMTI user should verify the validity of a
`jmethodID` value before using it to prevent potential crashes. Would
you agree with this interpretation?
Not quite - as you note you can't verify the jmethodID validity. What
the user needs to do, in line with what Dan was saying, is ensure that
they keep track of the classes to which the methods belong and keep them
alive if necessary. Now that may be easier said than done, but that is
the gist of it. This comes from the JNI spec:
"A field or method ID does not prevent the VM from unloading the class
from which the ID has been derived. After the class is unloaded, the
method or field ID becomes invalid and may not be passed to any function
taking such an ID. The native code, therefore, must make sure to:
keep a live reference to the underlying class, or
recompute the method or field ID
if it intends to use a method or field ID for an extended period of time."
This sounds like a sensible requirement, but its practical application
remains unclear. As far as I know, methods can be unloaded concurrently
to the native code executing JVMTI functions. This introduces a
potential race condition where the JVM unloads the methods during the
check->use flow, making it only a partial solution. To complicate
matters further, no method exists to confirm whether a `jmethodID` is valid.
Theoretically, we could monitor the `CompiledMethodUnload` event to
track the validity state, creating a constantly expanding set of
unloaded `jmethodID` values or a bloom filter, if one does not care
about few potential false positives. This strategy, however, doesn't
address the potential race condition, and it could even exacerbate it
due to possible event delays. This delay might mistakenly validate a
`jmethodID` value that has already been unloaded, but for which the
event hasn't been delivered yet.
Honestly, I don't see a way to use `jmethodID` safely unless the code
using it suspends the entire JVM and doesn't resume until it's finished
with that `jmethodID`. Any other approach might lead to JVM crashes, as
we've observed with J9.
Lastly, it's noteworthy that Hotspot takes meticulous measures to ensure
that using jmethodIDs for unloaded methods doesn't crash the JVM and
even provides useful information. This observation has led me to
question whether the documentation aligns with the Hotspot
implementation, especially given that following closely the
documentation appears to increase the risk associated with the use of
`jmethodID` values.
There have been folk who wanted to make this area more user-friendly but
that shouldn't be mistaken for moving towards a world where jmethodIDs
are always safe to use.
Cheers,
David
I welcome your thoughts and perspectives on this matter.
Best regards,
Jaroslav