On Fri, 20 Jun 2025 20:41:21 GMT, Coleen Phillimore <[email protected]> wrote:
>> This change uses a ConcurrentHashTable to associate Method* with jmethodID,
>> instead of an indirection. JNI is deprecated in favor of using Panama to
>> call methods, so I don't think we're concerned about JNI performance going
>> forward. JVMTI uses a lot of jmethodIDs but there aren't any performance
>> tests for JVMTI, but running vmTestbase/nsk/jvmti with in product build with
>> and without this change had no difference in time.
>>
>> The purpose of this change is to remove the memory leak when you unload
>> classes: we were leaving the jmethodID memory just in case JVMTI code still
>> had references to that jmethodID and instead of crashing, should get
>> nullptr. With this change, if JVMTI looks up a jmethodID, we've removed it
>> from the table and will return nullptr. Redefinition and the
>> InstanceKlass::_jmethod_method_ids is somewhat complicated. When a method
>> becomes "obsolete" in redefinition, which means that the code in the method
>> is changed, afterward creating a jmethodID from an "obsolete" method will
>> create a new entry in the InstanceKlass table. This mechanism increases the
>> method_idnum to do this. In the future maybe we could throw
>> NoSuchMethodError if you try to create a jmethodID out of an obsolete method
>> and remove all this code. But that's not in this change.
>>
>> Tested with tier1-4, 5-7.
>
> Coleen Phillimore has updated the pull request incrementally with one
> additional commit since the last revision:
>
> Fix the test
Another way to look at the safety of the new mechanism is to
consider the use of jmethodIDs from the `AsyncGetCallTrace` API:
src/hotspot/share/prims/forte.cpp:
108 2025.06.25 12:09:14 $ cat /tmp/fred
// Forte Analyzer AsyncGetCallTrace() entry point. Currently supported
// on Linux X86, Solaris SPARC and Solaris X86.
//
// Async-safe version of GetCallTrace being called from a signal handler
// when a LWP gets interrupted by SIGPROF but the stack traces are filled
// with different content (see below).
//
// This function must only be called when JVM/TI
// CLASS_LOAD events have been enabled since agent startup. The enabled
// event will cause the jmethodIDs to be allocated at class load time.
// The jmethodIDs cannot be allocated in a signal handler because locks
// cannot be grabbed in a signal handler safely.
//
// void (*AsyncGetCallTrace)(ASGCT_CallTrace *trace, jint depth, void* ucontext)
//
// Called by the profiler to obtain the current method call stack trace for
// a given thread. The thread is identified by the env_id field in the
// ASGCT_CallTrace structure. The profiler agent should allocate a
ASGCT_CallTrace
// structure with enough memory for the requested stack depth. The VM fills in
// the frames buffer and the num_frames field.
//
// Arguments:
//
// trace - trace data structure to be filled by the VM.
// depth - depth of the call stack trace.
// ucontext - ucontext_t of the LWP
//
// ASGCT_CallTrace:
// typedef struct {
// JNIEnv *env_id;
// jint num_frames;
// ASGCT_CallFrame *frames;
// } ASGCT_CallTrace;
//
// Fields:
// env_id - ID of thread which executed this trace.
// num_frames - number of frames in the trace.
// (< 0 indicates the frame is not walkable).
// frames - the ASGCT_CallFrames that make up this trace. Callee followed
by callers.
//
// ASGCT_CallFrame:
// typedef struct {
// jint lineno;
// jmethodID method_id;
// } ASGCT_CallFrame;
//
// Fields:
// 1) For Java frame (interpreted and compiled),
// lineno - bci of the method being executed or -1 if bci is not
available
// method_id - jmethodID of the method being executed
// 2) For native method
// lineno - (-3)
// method_id - jmethodID of the method being executed
The `AsyncGetCallTrace` API returns jmethodIDs without explicitly
associated jclass references. The `AsyncGetCallTrace` API relies on
JVM/TI CLASS_LOAD events to have been enabled since agent startup for
jmethodID creation. Implicit in that requirement is the agent keeping a
jclass reference for each CLASS_LOAD event so that the class cannot be
unloaded while the data returned by `AsyncGetCallTrace` is processed.
-------------
PR Comment: https://git.openjdk.org/jdk/pull/25267#issuecomment-3005716494