1

JNI 参考说

“本地引用在本地方法调用期间有效。本地方法返回后它们会自动释放。

资料来源:http ://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/functions.html#global_local

我有点迷失在这里。根据上述,我必须显式调用NewGlobalRef并将调用返回的对象传递给NewObject。我试过了,似乎当 GC 启动时,它不会收集我的引用(就像某些东西仍然保留在它们上一样)。考虑以下项目: Main.java

package lv.example;

import java.io.IOException;
import java.util.ArrayList;

class Main {

    public static void main(String[] args) {
        ArrayList<Object> store = new ArrayList<Object>();
            while(true) {
                Object d = null;
                try {
                    int c = System.in.read();
                    d = Dummy.getWeakGlobalRef();
                    if(c == 'c')
                        store.clear();
                    store.add(d);
                    System.out.println(d);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

    }

}

虚拟.java

package lv.example;

class Dummy {

    static {

        System.loadLibrary("dummy");
    }
       native static Dummy getLocalRef();
       native static Dummy getGlobalRef();
       native static Dummy getWeakGlobalRef();

       @Override
       protected void finalize() throws Throwable {
            System.out.println("Finalized");
       }

}

libdummy.so 包含本机方法的实现:

JNIEXPORT jobject JNICALL Java_lv_example_Dummy_getLocalRef (JNIEnv *env, jclass cls) {
    jmethodID id = env->GetMethodID(cls, "<init>", "()V");
    return env->NewObject(cls, id);
}


JNIEXPORT jobject JNICALL Java_lv_example_Dummy_getGlobalRef (JNIEnv *env, jclass cls) {

    jmethodID id = env->GetMethodID(cls, "<init>", "()V");
    return env->NewGlobalRef(env->NewObject(cls, id));
}


JNIEXPORT jobject JNICALL Java_lv_example_Dummy_getWeakGlobalRef (JNIEnv *env, jclass cls) {
    jmethodID id = env->GetMethodID(cls, "<init>", "()V");
    return env->NewWeakGlobalRef(env->NewObject(cls, id));
}

主循环表现出的行为对我来说似乎很奇怪:1)当我调用getWeakGlobalRefgetLocalRef并清除ArrayList时,GC 似乎收集了我创建的所有Dummy对象。2)当我调用getGlobalRef时,无论我是否清除ArrayList ,都不会收集任何对象。l

我在这里有点困惑,这是否意味着我可以将本地方法的本地引用返回给 Java 代码?

4

2 回答 2

6

JVM 需要知道本机代码是否保留了自己对对象的引用。也就是说,如果对象被删除,C/C++ 代码是否仍然可以尝试访问它(可能导致段错误)。具有全局引用的对象不会被垃圾回收,因为本机代码表明它可能仍需要它们。具有本地引用的对象可以被垃圾回收,因为创建引用的函数已经返回给 java。具有弱全局引用的对象可以被垃圾收集,因为本机代码已指示它将使用 IsSameObject 来检查具有弱引用的对象是否已被垃圾收集,以便只有在对象尚未被垃圾收集时才会正确使用该对象.

回覆:

所以这意味着 New*Ref 仅用于固定本机代码?JVM 是否跟踪“本地引用”到我将其分配给 Java 变量实例的点?

跟踪本地引用,直到返回 java。将其分配给 java 变量不会删除引用,尽管它会独立地阻止对该对象的垃圾收集。返回的值将在堆栈上具有引用,以防止垃圾收集,直到它可以分配给 java 变量,因此返回的对象不需要具有全局引用,除非本机代码在返回后可能访问同一个对象。如果没有本地引用,JVM 可能会在 JNI 函数执行时对对象进行垃圾收集,因为垃圾收集器在另一个线程中运行。通常不需要将 New Ref/Delete Ref 显式调用为

所有传递给本地方法的Java 对象(包括那些作为JNI 函数调用的结果返回的对象)都会自动添加到注册表中(即,给定一个本地引用)。

(引自 https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/design.html#implementing_local_references

您可能需要这样做的情况。

本机函数生成一个线程,该线程在控制权返回给原始线程中的 java 后继续使用该对象。

本机函数将引用的副本存储在某个全局变量中,以供后续从 java 调用中使用。

您希望在函数运行时显式删除引用,以在可能需要一段时间的函数期间节省内存。

于 2015-11-07T14:32:07.173 回答
1

是的。一个 JNI 本地引用是指一个对象;您可以返回对该对象的引用。回到 Java 方面,“本地引用”没有任何意义。

垃圾收集器的工作方式是从一组根“活动”引用开始,并将任何可从它们访问的对象标记为“活动”。其他所有对象都可能被删除。

JNI 的本地和全局引用保存在根引用集中。当创建它们的本机方法返回时,会自动删除本地引用。您可以,有时应该在此之前删除本地引用。全局引用随时由本机代码显式创建和删除。

于 2015-11-08T17:55:28.370 回答