Search code examples
javamemory-managementjava-native-interface

Access C++ memory from Java App


I have C++ App which calls some java functionality. I'm using JNI. Everything works fine, but still there is one small problem. In C++ App I have a few huge arrays. Java App needs this data also and returns one huge array of results. Currently I just copy the data (you can check some peace of code below). And it would be much better if I access these arrays from Java App directly per pointer. Is it possible? I assume no (because of JVM), but in that case I have to explain it in detail to my boss :) So, yes or no, and how or why?

jdoubleArray in2 = env->NewDoubleArray(1000);
jdouble *elems2 = (jdouble*)calloc(1000, sizeof(jdouble));
for(int i = 0; i < 1000; i++)
{
    elems2[i] = (jdouble)inputArray[i];
}
env->SetDoubleArrayRegion(in2, 0, 1000, elems2);
if(midCD != NULL)
{
    jdouble res1 = env->CallStaticDoubleMethod(cls, midCD, in2);
    double res1c = res1;
    printf("C++.Res1: %f\n", res1c);
}

Solution

  • The best that you can do is use the Get<PrimitiveType>ArrayElements routines. Hopefully the VM supports pinning:

    jdoubleArray in2 = env->NewDoubleArray(1000);
    jboolean isCopy;
    jdouble *elems2 = env->GetDoubleArrayElements(in2, &isCopy);
    for(int i = 0; i < 1000; i++)
    {
        elems2[i] = (jdouble)inputArray[i];
    }
    env->ReleaseDoubleArrayElements(in2, elems2, 0);
    elems2 = NULL;
    

    If isCopy is JNI_FALSE after the call to GetDoubleArrayElements(), then no copy was made.

    EDIT: After re-reading your question, you might want to consider implementing Archie's idea. This depends on how your Java methods use the data. If the Java methods do not use the entire array, or not all at once, then Archie's solution of creating a Java class wrapper of the C++ array, with native accessors, might be a good solution. If, however, the Java method requires all data, then you might just need the quickest way to get the data into the VM, which GetDoubleArrayElements() provides.

    If you make changes to some elements of the C++ array and want to make the same changes to the Java copy, that's where SetDoubleArrayRegion() comes into play.

    EDIT2: I believe that Archie is referring to something like:

    public class NativeDoubleArrayProxy {
    
        // This is the native `inputArray' pointer.
        private long p;
    
        private int length;
    
        private NativeDoubleArrayProxy(long p, int length) {
            this.p = p;
            this.length = length;
        }
    
        public int length() {
            return length;
        }
    
        public native double getDouble(int index);
    
        public native void getDoubles(int startingIndex, double[] out, int outOffset, int length);
    }
    

    The exact details depend on the type of your inputArray (is it a raw C-style array, or a std::vector<double>, something else?). But the idea is to construct a NativeDoubleArrayProxy object on the JNI side, passing it the pointer cast to a jlong. The JNI implementations of getDouble() and getDoubles() implement the copying-from-C++-to-Java code.

    Of course, you need to be very careful to make sure that the pointer remains valid.

    See also: What is the 'correct' way to store a native pointer inside a Java object?