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

萝らか妹 提交于 2020-02-01 09:16:02

问题


We're seeing a weird AddressSanitizer (clang/C++) "heap-use-after-free" violation that might relate to a finalizer corner case.

Let's say, a Java object OBJ has a handle to to a native resource X. A thread that created OBJ before, is now making a call on OBJ.method(), which calls into a (static) native method staticMethod(X), in which X is used.

Now, at more or less the same time, we're seeing a thread deleting the native resource X. We strongly assume that this triggered by the finalizer calling OBJ.finalize(), which does "delete X".

Is this a valid thing to do for a finalizer?

(OpenJDK 8)


回答1:


A safe way to do this seems to be using non-static native JNI methods.

In C/C++, a static JNI method signature looks something like this:

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

Note the second parameter jclass type passing the JNI representation of a Java class.

A non-static JNI method, however, accepts the current Java instance object (this) instead and looks like this:

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

Background: The VM seems to optimize garbage collection quite aggressively. A thread still running a (non-static) method but calling only into a native static method does NOT prevent the object from being freed. If the JNI method is non-static however, this tells the VM that the Java object is still being referenced. Then, only once the the call returns this native reference to the object is cleared. Thus, no finalizer is allowed to run before that.




回答2:


Default implementation of finalize() method does nothing:

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

Your description sounds like shared native resource was removed in one thread while it is required in another thread. You need to check out all native methods (in java) which removes something from native memory space.
Java don't know about objects allocated in native code. You need manually control this via native calls. For example:

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


来源:https://stackoverflow.com/questions/47532395/can-a-finalizer-be-called-on-an-object-that-is-in-the-middle-of-a-native-call

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!