Reputation: 31161
"How can I use large static C or C++ arrays in Java?"
I'm working with Android NDK and JNI to get some native C (or C++) code working in Java. This is fine, my JNI-generated interface and my C (or C++) implementation are working.
However, my app needs a very large array. In C/C++, and originally Objective-C, this is sure to be contiguous whereas this is explicitly not the case in Java. Even if Java arrays were like C or C++ arrays, I can't pass it by reference from Java to my C implementation. I can't copy the Java array to a C or C++ array in my implementation because this doubles the memory cost. And I can't create it from scratch each time my C or C++ implementation is called.
I'd therefore like to keep this array out of a Java header and in a C or C++ header, and have it ready in C or C++ world at all times from the moment the app loads. This is a critical component of the app.
What can I do?
Thanking you kindly in advance.
//////////////////////////////////
Using Get<PrimitiveType>ArrayElements
from JNI is a long way towards a solution, the only worry is memory allocation. It's likely that the array is copied after all.
But it is running in the Android emulator and the results are impressive: A calculation that takes 13 seconds in Java on the emulator takes 0.7 seconds on the emulator when carried out in C.
Because the Android emulator is fairly slow this is likely to be an authentic figure, the only way to be sure is to test it on the device.
Upvotes: 0
Views: 415
Reputation: 52343
Take a look at this JNI Tips FAQ entry. It may answer your question, or it least give you a sense for the available options.
ADDENDUM:(SK9) (Too big for comments box, sorry) The relevant part of the JNI tips is below. Following the guidance here the app will crash if the array is copied (it surely will be) because of insufficient memory.
///////////////////
Primitive Arrays
JNI provides functions for accessing the contents of array objects. While arrays of objects must be accessed one entry at a time, arrays of primitives can be read and written directly as if they were declared in C.
To make the interface as efficient as possible without constraining the VM implementation, the GetArrayElements family of calls allows the VM to either return a pointer to the actual elements, or allocate some memory and make a copy. Either way, the raw pointer returned is guaranteed to be valid until the corresponding Release call is issued (which implies that, if the data wasn't copied, the array object will be pinned down and can't be relocated as part of compacting the heap). You must Release every array you Get. Also, if the Get call fails, you must ensure that your code doesn't try to Release a NULL pointer later.
You can determine whether or not the data was copied by passing in a non-NULL pointer for the isCopy argument. This is rarely useful.
The Release call takes a mode argument that can have one of three values. The actions performed by the VM depend upon whether it returned a pointer to the actual data or a copy of it:
0 Actual: the array object is un-pinned. Copy: data is copied back. The buffer with the copy is freed. JNI_COMMIT Actual: does nothing. Copy: data is copied back. The buffer with the copy is not freed. JNI_ABORT Actual: the array object is un-pinned. Earlier writes are not aborted. Copy: the buffer with the copy is freed; any changes to it are lost. One reason for checking the isCopy flag is to know if you need to call Release with JNI_COMMIT after making changes to an array — if you're alternating between making changes and executing code that uses the contents of the array, you may be able to skip the no-op commit. Another possible reason for checking the flag is for efficient handling of JNI_ABORT. For example, you might want to get an array, modify it in place, pass pieces to other functions, and then discard the changes. If you know that JNI is making a new copy for you, there's no need to create another "editable" copy. If JNI is passing you the original, then you do need to make your own copy.
Some have asserted that you can skip the Release call if *isCopy is false. This is not the case. If no copy buffer was allocated, then the original memory must be pinned down and can't be moved by the garbage collector.
Also note that the JNI_COMMIT flag does NOT release the array, and you will need to call Release again with a different flag eventually."
Upvotes: 1