4

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);
}
Cornelius Dol
64.2k26 gold badges142 silver badges190 bronze badges
asked Dec 17, 2012 at 21:43
2
  • 2
    I would create a Java 'array' class that is implemented by its peer class in C++, do all operations will native routines. This way the data itself will be entirely in C++ world without any copying required. Commented Dec 17, 2012 at 22:14
  • Sorry, I don't really understand, what exactly you mean. How the peer class in C++ should look like? And how the Java array class can access it? Commented Dec 18, 2012 at 8:05

2 Answers 2

3

Direct ByteBuffers were introduced in Java 1.4 for this purpose. Along with the new classes on the Java side were the corresponding JNI functions. You can wrap your native arrays in a ByteBuffer (and the corresponding IntBuffer, etc., views) and read and write without making copies.

answered Dec 18, 2012 at 0:41
Sign up to request clarification or add additional context in comments.

3 Comments

Thanks, I'll try. But if I have an array in C++, than I have to create this Buffer on the JVM side and copy data into it, right? In this case it would be pretty the same like in my code.
+1 This is a good idea. If inputArray is already a jdouble C array, then on the JNI side, call NewDirectByteBuffer() passing inputArray and length * sizeof (jdouble) as the capacity. On the Java size, create a DoubleBuffer view of the ByteBuffer via the ByteBuffer#asDoubleBuffer() method.
And if you take a look at java.nio.Buffer source code, you'll find that it's actually implemented the way we discussed above via embedding native pointer into a Java class (proprietary sun.misc.Unsafe package is used to manipulate native pointers).
1

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?

answered Dec 17, 2012 at 23:43

3 Comments

Thanks. But still I didn't get an answer to my question, can I access "C++" data from Java App directly or should I copy it to the JVM via jArrays? What do you think about Bufferes, what jpavel wrote? And could you please explain me a bit more in detail, what is Archie's idea?
@Andrew: You can access C++ data from Java directly. See my updated answer. You will need to evaluate whether that is best for your project, though.
That's what I meant. Thank you Daniel for elaboration. In Java classpath terminology these are called peer-classes. They are always coupled via native pointer embedded into Java object. The real work of accessing and manipulating your data is done at native side, and the Java class is indeed just a proxy. The whole AWT is developed this way.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.