Search code examples
javac++arraysjava-native-interface

JNI cache java array


I have a java application and some native c++ code. The java application runs some threads. Each thread has an array of double which is passed as parameter to the native code. So far everything works well. Now in my native code, I want to write values in the array. Currently I'm doing this by:

  JNIEXPORT void JNICALL Java_JNIPerformanceTest_alterArray___3I_3D
    (JNIEnv *env, jobject, jintArray intArray, jdoubleArray doubleArray){

         jint count = env->GetArrayLength(doubleArray);
         jdouble* S_native = env->GetDoubleArrayElements(doubleArray, NULL);

         for (int i=0; i < count; i++) {
                S_native[i] = i;
         }

         jint count2 = env->GetArrayLength(intArray);
         jint* S_native2 = env->GetIntArrayElements(intArray, NULL);


         for (int i=0; i < count2; i++) {
                S_native2[i] = i;
         }

         env->ReleaseDoubleArrayElements(doubleArray, S_native, 0);
         env->ReleaseIntArrayElements(intArray, S_native2, 0);
    }

As the GetArrayLength and GetDoubleArrayElements take some time and I'm always passing the same java array instance to the native code, I'm wondering if there is a solution without getting the element-pointer and releasing function-calls. I already tried to store the pointers globally, of course with

 env->NewWeakGlobalRef(array);

. But if I don't call ReleaseDoubleArrayElements on this array, the elements didn't change if examined in java. What is the best way so solve such an issue? Using the ByteBuffer?

Here is also my java code:

    double ARRAY_SIZE = 10;

    for (int i = 0; i < 4; i++) {
        Thread thread = new Thread(new Runnable() {

            boolean done = false;

            @Override
            public void run() {
                while (true) {
                    double d[] = null;
                    int intArray[] = null;
                    if (!done) {
                        d = new double[ARRAY_SIZE];
                        for (int i = 0; i < ARRAY_SIZE; i++) {
                            d[i] = 222;
                        }

                        intArray = new int[ARRAY_SIZE];
                    }

                    long start = System.nanoTime();
                    alterArray(intArray, d);

                    long end = System.nanoTime();
                    System.out.println("test_alterArray: time for " + NUM_LOOPS + " loops: " + ((end - start) / 1000) + " us.");

                }
            }
        });
        thread.start();
    }

Solution

  • Yes, the correct way to do this would be to use DirectByteBuffer. It comes with its own drawbacks, though. For example, such Buffer is not backed by array, and access to it from the Java side may be less efficient.