Today, when a ClassLoaderData is deallocated, its JNIMethodBlocks are kept around, resulting in a slow memory leak. We do this because JVMTI agents can reuse a jmethodID that belongs to a class that has been deallocated. The JVMTI implementation tolerates this by checking if the jmethodID (which is basically a handle to a Method*) contains a NULL pointer. If so, this indicates that the jmethodID was referring to a Method that has since been deallocated. JVMTI APIs will return JVMTI_ERROR_INVALID_METHODID instead of proceeding further, e.g.,
jvmti_GetMethodDeclaringClass(jvmtiEnv* env,
jmethodID method,
jclass* declaring_class_ptr) {
...
Method* checked_method = Method::checked_resolve_jmethod_id(method);
if (checked_method == NULL) {
return JVMTI_ERROR_INVALID_METHODID;
}
However, the "regular" JNI APIs (those declared in jni.h) as implemented in HotSpot do not tolerate such invalid jmethodIDs. I have checked all functions under jni.cpp in JDK 16. Every single one of them will crash immediately when an invalid jmethodID is given, e.g., the following will crash by dereferencing the invalid pointer <m>.
JNI_ENTRY(jobject, jni_ToReflectedMethod(JNIEnv *env, jclass cls, jmethodID method_id, jboolean isStatic))
...
methodHandle m (THREAD, Method::resolve_jmethod_id(method_id));
...
if (m->is_initializer()) {
Therefore, we can conclude that no working JNI code today can use an invalid jmethodID without crashing (except for JVMTI agent code). As a result, we can free the JNIMethodBlocks to avoid memory leaks.
jvmti_GetMethodDeclaringClass(jvmtiEnv* env,
jmethodID method,
jclass* declaring_class_ptr) {
...
Method* checked_method = Method::checked_resolve_jmethod_id(method);
if (checked_method == NULL) {
return JVMTI_ERROR_INVALID_METHODID;
}
However, the "regular" JNI APIs (those declared in jni.h) as implemented in HotSpot do not tolerate such invalid jmethodIDs. I have checked all functions under jni.cpp in JDK 16. Every single one of them will crash immediately when an invalid jmethodID is given, e.g., the following will crash by dereferencing the invalid pointer <m>.
JNI_ENTRY(jobject, jni_ToReflectedMethod(JNIEnv *env, jclass cls, jmethodID method_id, jboolean isStatic))
...
methodHandle m (THREAD, Method::resolve_jmethod_id(method_id));
...
if (m->is_initializer()) {
Therefore, we can conclude that no working JNI code today can use an invalid jmethodID without crashing (except for JVMTI agent code). As a result, we can free the JNIMethodBlocks to avoid memory leaks.
- relates to
-
JDK-8313332 Simplify lazy jmethodID cache in InstanceKlass
-
- Resolved
-
-
JDK-8268364 jmethod clearing should be done during unloading
-
- Resolved
-
-
JDK-8268416 Clarify invalid method and field ID in JNI specification
-
- Resolved
-
- links to
-
Review(master) openjdk/jdk/25267