从本地方法返回由JNI创建的本地引用 [英] Returning local reference created by JNI from a native method

查看:249
本文介绍了从本地方法返回由JNI创建的本地引用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

JNI引用说明:


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


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



我有点迷失在这里,根据上面的介绍,我必须显式调用 NewGlobalRef ,并将调用返回的对象传递给 NewObject
我试过这个,看起来当GC启动时,它不会收集我的引用(就像仍然保留在它们上面一样)
考虑下面的项目:
Main.java

 包lv.example; 

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

class Main {

pub lic static static main(String [] args){
ArrayList< Object> store = new ArrayList< Object>();
while(true){
Object d = null;
尝试{
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();
$ b $ @覆盖$ b $保护void finalize()抛出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)当我呼叫 getWeakGlobalRef getLocalRef 并清除 ArrayList 时,GC似乎收集了我创建的所有 Dummy
2)当我调用getGlobalRef时,无论我清除 ArrayList 还是不是,收集的对象都不会收集.l



我很善良这是否意味着我可以从本地方法返回本地引用返回到Java代码?

解决方案

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

re:


所以这意味着New * Ref是为仅针对本机代码? JVM是否将本地引用跟踪到将其分配给Java变量实例的位置?

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


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

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

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



原生函数产生一个线程继续使用该对象在原始线程中将控制权返回给java。



本地函数在一些全局变量中存储引用的副本,以便在随后的java调用中使用。



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


JNI reference says that

"Local references are valid for the duration of a native method call. They are freed automatically after the native method returns.

Source: http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/functions.html#global_local

I'm kind of lost here. According to above, I must explicitly call NewGlobalRef and pass object returned from a call to NewObject. I tried this and it seems that when a GC kicks in, it does not collect my references (like something still holds onto them). Consider the following project: 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 contains implementation of native methods:

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

Behaviour that main loop exhibits seems strange to me: 1) When I call getWeakGlobalRef or getLocalRef and clear ArrayList, GC seems to collect all the Dummy objects I've created. 2) When I call getGlobalRef, no objects get collected regardless if I clear ArrayList or not.l

I'm kind of confused here, does that mean that I can return local references from native methods back to Java code?

解决方案

The JVM needs to know whether the native code is keeping its own references to the objects. That is if the object were deleted, is it possible for the C/C++ code to still try access it (probably causing a segfault). The objects with global references are not garbage collected because the native code has indicated that it might still need them. The objects with local references can be garbage collected because the function where the references were created has already returned to java. The objects with weak global references can be garbage collected because the native code has indicated it will use IsSameObject to check if the object with the weak reference has been garbage collected so that it will behave correctly using the object only if it has not been garbage collected.

re:

So this means, that New*Ref is for pinning for native code only? Does JVM track a "local reference" to the point where I assign it to a instance of Java variable?

Local references are tracked until the return to java. Assigning it to a java variable doesn't remove the reference although it would independently prevent garbage collection of that object. The returned value would have a reference on the stack preventing garbage collection until it could be assigned to a java variable so there is no need for a returned object to have a global reference unless the native code might access that same object after the return. Without the local reference the JVM might garbage collect the object while the JNI function were executing since the garbage collector runs in another thread. Normally one doesn't need to explicitly call NewRef/DeleteRef as

All Java objects passed to the native method (including those that are returned as the results of JNI function calls) are automatically added to the registry(ie given a local reference).

(quoted from https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/design.html#implementing_local_references)

Cases you might need to do it.

The native function spawns a thread that continues using the object after control is returned to java in the original thread.

The native function stores a copy of the reference in some global variable for use in subsequent calls from java.

You want to explicitly delete references while the function is running to conserve memory during a function that might take a while.

这篇关于从本地方法返回由JNI创建的本地引用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆