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
285 views
in Technique[技术] by (71.8m points)

java - Shall jarrayObject (array of strings) be deleted/released after usage in a JNI call ?

I'm experimenting in C++ with JNI and have a doubt about what to do with Java object that I've created in C++ for being used as JNI call argument.

Take this very simple java class, with a string array argument:

public class MyTest {
    public static void main(String[] args) {
        System.out.println("Hello, World in java");
        int i; 
        for (i=0; i<args.length; i++)
            System.out.println(args[i]);        
    }
}

I call it from C++ with the following JNI code:

jmethodID mid3 = env->GetStaticMethodID(cls2, "main", "([Ljava/lang/String;)V"); // signature for String array arg.
if(mid3 == nullptr) 
    cerr << "ERROR: method not found !" << endl;
else {
     jobjectArray arr = env->NewObjectArray(5,   // constructs java array of 5
             env->FindClass("java/lang/String"),
                env->NewStringUTF("str"));   // with this default value
     env->SetObjectArrayElement(             // change one of the array elements
             arr, 1, env->NewStringUTF("MYOWNSTRING"));
     env->CallStaticVoidMethod(cls2, mid3, arr); // call method
}

This works very well. But I'm unsure what I have to to with the jarrayObject (and the java strings it contains) afterwards, to keep the things clean.

My understanding would be that the JVM is responsible for the java objects. But how does it know which objectsz I no longer need on the C++ side ? I googled around and didn't find any clear explanation. I read in the JNI specifications for DeleteLocalRef() that:

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

So shall I call DeleteLocalRef() for the jarrayObject (or even for every java string it contains) or some other cleaning function ? Or can I assume that JVM takes care of this on its own ?

See Question&Answers more detail:os

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

1 Answer

0 votes
by (71.8m points)

The jni design specs (see section Global and local references) explains that:

  • all references passed to a native method as well as all references returned by JNI functions are local references;
  • these local references are automatically freed after the native method returns.

However, this principle applies only to the case of a C++ function called by Java: the local references are released when the function returns to Java (see section Implementing local references).

If you create objects or get references from JNI functions in C++ code that is not called from Java, the local references ARE NOT FREED AUTOMATICALLY, causing memory to leak.

So you should better use DeleteLocalRef() to free java objects created/obtained from C++ but no longer used.

Demonsration:

The following simple Java code allocates big objects without keeping their references, and call some memory inspection:

class BigMemoryConsumer {
    private char myTable[];   

    public BigMemoryConsumer () {   // Allocate and use 1Mb 
        myTable = new char[1048576];
        for (int i=0; i<1048576; i++) 
            myTable[i] = (char) (i % 256);  
    }
    
    public static long showMem() {  // Show memory statistics 
        int funit = 1024*1024;
        String unit =  " Mb"; 
        Runtime runtime = Runtime.getRuntime();
        System.gc();    // opportunity to run garbage collector (not guaranteed !)  
        long used = runtime.totalMemory() - runtime.freeMemory();
        System.out.println("Used Memory:  " + used / funit + unit 
            + " ("+ (used*100/runtime.maxMemory())+"%)");
        System.out.println("Free Memory:  " + runtime.freeMemory() / funit + unit);
        System.out.println("Total Memory: " + runtime.totalMemory() / funit + unit);
        System.out.println("Max Memory:   " + runtime.maxMemory() / funit + unit);
        System.out.println("");
        return used;   
    }
    
    public static void main (String[] args) {     // test in java
        long lastmem = showMem(); 
        for (int i=0; i<256; i++) {
            BigMemoryConsumer m = new BigMemoryConsumer(); 
            long mem = showMem(); 
            if (mem<=lastmem) {
                System.out.println ("Garbage collector freed some memory"); 
                return; 
            }
            else lastmem = mem;
        }
    }
}

When you run this class directly from Java (aka main()) You'll notice that Java will very quicly (second or third iteration on my 64bit system) run the garbage collector: the object m is reinitialised on every iteration, meaning that previously created objects are no longer referenced.

Now I've reproduced the equivalent main() in C++ code, just after having loaded the JVM and initialized the JNI environment:

jclass cls = env->FindClass("BigMemoryConsumer");
jmethodID ctor = env->GetMethodID(cls, "<init>", "()V");    // find consructor 
jmethodID show = env->GetStaticMethodID(cls, "showMem", "()J");  // find memShow() 

jlong lastmem = 0;
vector<jobject> v;
for(int i = 0; i < 256; i++) {
    jobject myo = env->NewObject(cls, ctor);
    v.push_back(myo);
    jlong mem = env->CallStaticLongMethod(cls, show);
    if(mem <= lastmem) {
        cout << "ATTENTION:  garbage collector called as consumed java memory didn't increase after "<<i<<" iterations
";
        break;
    }
    else lastmem = mem;
    //env->DeleteLocalRef(myo); ///!!!! <======= SEE TEXT 
}

If running the code without the DeleteLocalRef(), you'll notice a continuous increase in consumed memory: no garbage collection takes place, as the JVM is not aware that the reference to the java object requested from C++ is no longe used.

If you comment out the highlighted line, the DeleteLocalRef() will tell the JVM that the object is no longer needed in the C++ code, and the garbage collector will behave exactly as in the pure java example, rfreeing up the object after a couple of iterations.


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

...