我们发现一个奇怪的 AddressSanitizer (clang/C++)“释放后堆使用”违规,可能与终结器极端情况有关。
假设一个 Java 对象 OBJ 有一个指向 native 资源 X 的句柄。之前创建 OBJ 的线程现在正在调用 OBJ.method(),该方法调用一个(静态) native 方法 staticMethod( X),其中使用X。
现在,我们几乎同时看到一个线程正在删除 native 资源 X。我们强烈假设这是由调用 OBJ.finalize() 的终结器触发的,它确实“删除了 X”。
对于终结器来说,这是有效的做法吗?
(OpenJDK 8)
最佳答案
一种安全的方法似乎是使用非静态 native JNI 方法。
在 C/C++ 中,静态 JNI 方法签名如下所示:
extern "C" JNIEXPORT jobject JNICALL
Java_com_example_MyClass_myMethod(JNIEnv* env, jclass type, jlong handle);
注意第二个参数jclass type
传递Java类的JNI表示。
但是,非静态 JNI 方法接受当前 Java 实例对象 (this
),如下所示:
extern "C" JNIEXPORT jobject JNICALL
Java_com_example_MyClass_myMethod(JNIEnv* env, jobject thisObj, jlong handle);
背景:虚拟机似乎非常积极地优化垃圾收集。仍在运行(非静态)方法但仅调用 native 静态方法的线程不会阻止释放对象。然而,如果 JNI 方法是非静态的,这会告诉 VM Java 对象仍在被引用。然后,只有当调用返回时,该对象的 native 引用才会被清除。因此,在此之前不允许运行任何终结器。
关于java - 是否可以在( native )调用过程中的对象上调用终结器?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47532395/