返回JNI从本机方法

时间:2015-11-07 10:28:12

标签: java garbage-collection java-native-interface jnienv

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

    }

}

Dummy.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包含 native 方法的实现:

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)当我调用 getWeakGlobalRef getLocalRef 并清除 ArrayList 时,GC似乎收集了所有 Dummy 对象I&# 39; ve创造了。 2)当我调用getGlobalRef时,无论是否清除 ArrayList ,都不会收集任何对象.l

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

2 个答案:

答案 0 :(得分:5)

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

重新:

  

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

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

  

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

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

您可能需要这样做的案例。

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

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

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

答案 1 :(得分:1)

是。 JNI本地引用是指对象;您可以返回对该对象的引用。回到Java端,&#34;本地参考&#34;没有任何意义。

垃圾收集器的工作原理是从一组root&#34; live&#34;引用和标记从它们可以到达的任何对象&#34; live&#34;。每个其他对象都可以删除。

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