Search code examples
java-native-interfacejnienv

Return a big object array from JNI?


According to the doc

jint EnsureLocalCapacity(JNIEnv *env, jint capacity);

Ensures that at least a given number of local references can be created in the current thread. Returns 0 on success; otherwise returns a negative number and throws an OutOfMemoryError.

Before it enters a native method, the VM automatically ensures that at least 16 local references can be created.

For backward compatibility, the VM allocates local references beyond the ensured capacity. (As a debugging support, the VM may give the user warnings that too many local references are being created. In the JDK, the programmer can supply the -verbose:jni command line option to turn on these messages.) The VM calls FatalError if no more local references can be created beyond the ensured capacity.

It seems there is some limitation for the number of Local Reference can be created.

And I have following code to return a big object array, which leads to a lot of Local Reference creation. Does that mean this would reach the limitation easily? -- the doc says 16 by default.

extern "C" JNIEXPORT
jobjectArray JNICALL Java_MessageQueueInterop_receive(JNIEnv * env, jobject this_obj, jclass cls)
{
    int count = 99999;
    jobjectArray ret = env->NewObjectArray( count, cls, NULL);
    if( ret ){
        for( int i = 0; i < count; i++ ) {
            jobject obj = env->NewObject( cls, constructor);
            if( obj ){
            env->SetIntField( obj, fieldID1, 2);

            jstring str = env->NewStringUTF("XXX");
            if( str )
                env->SetObjectField( obj, fieldID2, str);

            env->SetObjectArrayElement( ret, i, obj);
            }
        }
    }
    return ret;
}

How to extend the limitation?


Solution

  • Well 100,000 > 16 so you will definitely have trouble.

    You could try a combination of PushLocalFrame() and PopLocalFrame(), as follows:

    extern "C" JNIEXPORT
    jobjectArray JNICALL Java_MessageQueueInterop_receive(JNIEnv * env, jobject this_obj, jclass cls)
    {
        int count = 99999;
        jobjectArray ret = env->NewObjectArray( count, cls, NULL);
        if( ret ){
            for( int i = 0; i < count; i++ ) {
                PushLocalFrame(env, 1); // TODO error checking required here
                jobject obj = env->NewObject( cls, constructor);
                if( obj ){
                    env->SetIntField( obj, fieldID1, 2);
                    jstring str = env->NewStringUTF("XXX");
                    if( str )
                        env->SetObjectField( obj, fieldID2, str);
                }
                obj = PopLocalFrame(env, obj);
                env->SetObjectArrayElement( ret, i, obj);
            }
        }
        return ret;
    }
    

    E&OE. Might work, might not. [On second thoughts, probably not.]

    It might be better to have the caller provide a LinkedList so you don't have to allocate this huge array, and maybe a calling loop so you only add one element from the native queue at a time inside the JNI, or else a callback into Java to allocate the object.