4

I am writing C++ library that will be used by different Android applications to process some kind of data organized like two-dimensional storage where each dimension has no predefined restrictions for size (like array of arrays of float, and size of arrays can be quite large).

Current solution uses SWIG to copy data from memory allocated by Java code to C++ structures. It turns out that each array of float value (in Java) became vector of float (in C++).

The problem is that duplication of a large amount of data increases the risk of running out of memory available for application. I understand that, in any case, memory consumption issue should be resolved by input volume limitation, but the library does not know how much memory is available and should have whole data (access to any data element is needed repeatedly) to perform correct processing.

So now I am considering possibility to use one data storage for Java and C++, so C++ code require direct access to data stored by Java code to memory allocated on Java side (making memory allocated by C++ code as single storage is not considered).

I want to know how to organize such memory sharing in a safe manner (preferably using SWIG).

I feel that some difficulties can be with such implementation, e.g. with Java garbage collector (C++ code can address to storage which already deallocated) and slowing memory access through the wrapper (as mentioned earlier, the library requires repeated access to each data item)… but perhaps someone advise me a reliable solution. The explanation of why my idea is wrong can be accepted, if supported with sufficiently and compelling arguments.

VolAnd
  • 6,367
  • 3
  • 25
  • 43
  • SWIG's [Android documentation](http://www.swig.org/Doc2.0/Android.html) says "support for Android is the same as for Java, where **the Java Native Interface (JNI) is used to call from Android Java into C or C++ compiled code**". SWIG's [Java documentation](http://www.swig.org/Doc1.3/Java.html) goes into more details. – Remy Lebeau Mar 23 '17 at 17:54
  • JNI has direct access to Java allocated memory, can allocate new memory within the Java environment, and increment/decrement references to Java objects to control their garbage collection. So what is the actual problem you are experiencing? Do you understand how to [work with JNI](http://docs.oracle.com/javase/7/docs/technotes/guides/jni/) in general? Have you read Android's [JNI Tips](https://developer.android.com/training/articles/perf-jni.html)? – Remy Lebeau Mar 23 '17 at 17:54
  • take a look at those: http://stackoverflow.com/questions/17709210/jni-passing-large-amounts-of-data-between-java-and-native-code, https://docs.oracle.com/javase/7/docs/api/java/nio/FloatBuffer.html and for the wrapping part with swig: http://stackoverflow.com/questions/32294864/how-to-define-and-pass-bytebuffer-using-swig – V-master Mar 24 '17 at 07:30
  • @V-master Do I understood correctly that ByteBuffer can be converted to any C++ type? What about `vector< vector >`? – VolAnd Mar 24 '17 at 10:32
  • 1
    @VolAnd If you want to avoid making another copy of the data, you can't use C++ vectors. If you want to use the data from Java directly, you'll need to use POD arrays such as `float *floatArray = env->GetDirectBufferAddress(javaFloatBufferObj);` or `long *longArray = env->GetLongArrayElements(javaFloatArrayObj);`. Whether you use Java arrays or direct buffers in your Java code depends on what processing you need to do in Java. See http://stackoverflow.com/questions/18913001/when-to-use-array-buffer-or-direct-buffer – Andrew Henle Mar 24 '17 at 10:59
  • @AndrewHenle Tanks for clarification. – VolAnd Mar 24 '17 at 13:31
  • Can you give a concrete example so I can give a concrete answer? – Flexo Mar 24 '17 at 18:09

1 Answers1

1

You can take access to raw array of data using Critical Native implementation. This tecknology allow to access directly to jvm memory without owerhead of transfering data between Java and native code.

But this have next restrictions:

  • must be static and not synchronized;
  • argument types must be primitive or primitive arrays;
  • implementation must not call JNI functions, i.e. it cannot allocate Java objects or throw exceptions;
  • should not run for a long time, since it will block GC while running.

The declaration of a critical native looks like a regular JNI method, except that:

  • it starts with JavaCritical_ instead of Java_;
  • it does not have extra JNIEnv* and jclass arguments;
  • Java arrays are passed in two arguments: the first is an array length, and the second is a pointer to raw array data. That is, no need to call GetArrayElements and friends, you can instantly use a direct array pointer.

Look at original answer and source article for details.

Community
  • 1
  • 1
Sergei Bubenshchikov
  • 5,275
  • 3
  • 33
  • 60
  • 1
    thank you for information. This time it is not helpful (because of restrictions), but it can really help in the future. – VolAnd Apr 30 '17 at 07:23