Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
313 views
in Technique[技术] by (71.8m points)

java - Returning local reference created by JNI from a native method

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?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...