我们看到一个奇怪的AddressSanitizer(clang / C ++)“可用后堆使用”违规可能与终结器角落案件有关。
假设Java对象OBJ具有本机资源X的句柄。之前创建OBJ的线程现在正在对OBJ.method()进行调用,该调用调用(静态)本机方法staticMethod( X),其中使用了X.
现在,或多或少同时,我们看到一个删除本机资源X的线程。我们强烈认为这是由终结器调用OBJ.finalize()触发的,它执行“删除X”。
这对终结者来说是否有效?
(OpenJDK 8)
答案 0 :(得分:1)
一种安全的方法似乎是使用非静态本机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);
背景:VM似乎非常积极地优化了垃圾回收。仍在运行(非静态)方法但仅调用本机静态方法的线程不会阻止对象被释放。但是,如果JNI方法是非静态的,这将告诉VM仍在引用Java对象。然后,仅在调用返回该对象的本机引用后,才清除该调用。因此,在此之前不允许终结器运行。
答案 1 :(得分:0)
finalize()
方法的默认实现不执行任何操作:
public class Object {
protected void finalize() throws Throwable { }
}
您的描述听起来像是在一个线程中删除了共享本机资源,而在另一个线程中需要它。您需要查看从本机内存空间中删除内容的所有本机方法(在java中)。
Java不知道在本机代码中分配的对象。您需要通过本机调用手动控制它。例如:
public class A {
private int id;
static {
// load native library
}
public A(int id) {
// create required native resources for this instance
allocateAContext(id)
}
// this method will create required native resources out of java heap
protected native void allocateAContext(int id);
// this method will remove allocated native resources
protected native void deleteAContext(int id);
@Override
protected void finalize() throws Throwable {
super.finalize();
// release native resources when garbage collector will remove A object
deleteAContext(id);
}
}