是否可以在(本机)调用中间的对象上调用终结器?

Can a finalizer be called on an object that is in the middle of a (native) call?

我们发现一个奇怪的 AddressSanitizer (clang/C++) "heap-use-after-free" 违规行为可能与终结器极端情况有关。

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

现在,几乎同时,我们看到一个线程正在删除本机资源 X。我们强烈假设这是由调用 OBJ.finalize() 的终结器触发的,它确实 "delete X".

这是对终结器有效的做法吗?

(OpenJDK 8)

finalize() 方法的默认实现不执行任何操作:

public class Object {
    protected void finalize() throws Throwable { }
}

您的描述听起来像是在一个线程中删除了共享本机资源,而在另一个线程中需要它。您需要检查所有 native 方法(在 java 中),这些方法从 native 内存 space.
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);
    }
}

执行此操作的安全方法似乎是使用 non-static 本机 JNI 方法。

在 C/C++ 中,静态 JNI 方法签名如下所示:

extern "C" JNIEXPORT jobject JNICALL
Java_com_example_MyClass_myMethod(JNIEnv* env, jclass type, jlong handle);

注意第二个参数 jclass type 传递了 Java class 的 JNI 表示。

然而,

A non-static JNI 方法接受当前的 Java 实例对象 (this),如下所示:

extern "C" JNIEXPORT jobject JNICALL
Java_com_example_MyClass_myMethod(JNIEnv* env, jobject thisObj, jlong handle);

背景:VM 似乎非常积极地优化垃圾收集。一个线程仍然 运行 宁一个 (non-static) 方法但只调用一个本地静态方法不会阻止对象被释放。但是,如果 JNI 方法是 non-static,这会告诉 VM Java 对象仍在被引用。然后,只有在调用 returns 时,这个对对象的本机引用才会被清除。因此,在此之前不允许终结器运行。