可以在处于(本机)调用中间的对象上调用终结器吗?

时间:2017-11-28 13:08:22

标签: java java-native-interface finalizer

我们看到一个奇怪的AddressSanitizer(clang / C ++)“可用后堆使用”违规可能与终结器角落案件有关。

假设Java对象OBJ具有本机资源X的句柄。之前创建OBJ的线程现在正在对OBJ.method()进行调用,该调用调用(静态)本机方法staticMethod( X),其中使用了X.

现在,或多或少同时,我们看到一个删除本机资源X的线程。我们强烈认为这是由终结器调用OBJ.finalize()触发的,它执行“删除X”。

这对终结者来说是否有效?

(OpenJDK 8)

2 个答案:

答案 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);
    }
}