如何避免在Java和本地C++代码之间复制数据

4
我正在编写一个C++库,将用于不同的Android应用程序来处理某种类型的数据,该数据组织方式类似于二维存储,其中每个维度没有预定义的大小限制(例如浮点数组的数组,数组的大小可能相当大)。
目前的解决方案使用SWIG将数据从Java代码分配的内存复制到C++结构中。结果发现每个浮点值数组(在Java中)变成了浮点向量(在C++中)。
问题在于大量数据的复制会增加应用程序可用内存耗尽的风险。我知道,无论如何,内存消耗问题都应该通过输入量限制来解决,但是库不知道有多少可用内存并且需要整个数据(需要重复访问任何数据元素)才能执行正确的处理。
因此,现在我正在考虑使用一个数据存储区域供Java和C++使用,因此C++代码需要直接访问由Java代码存储在Java端分配的内存中的数据(不考虑将由C++代码分配的内存作为单个存储区域)。
我想知道如何以安全的方式组织这样的内存共享(最好使用SWIG)。
我感觉这种实现可能会遇到一些困难,例如Java垃圾回收器(C++代码可以访问已经被释放的存储区域)和通过包装器减慢内存访问(如前所述,库需要重复访问每个数据项)...但也许有人可以为我提供一个可靠的解决方案。如果支持足够和令人信服的论据,我可以接受对我的想法错误的解释。

SWIG的Android文档指出:“对于Android的支持与Java相同,其中使用Java本地接口(JNI)从Android Java调用C或C ++编译代码”。 SWIG的Java文档提供了更多详细信息。 - Remy Lebeau
JNI可以直接访问Java分配的内存,在Java环境中分配新的内存,并增加/减少对Java对象的引用以控制其垃圾回收。那么您遇到的实际问题是什么?您是否了解如何一般使用JNI?您是否阅读过Android的JNI技巧? - Remy Lebeau
请查看以下内容:https://dev59.com/fWMm5IYBdhLWcg3wT9qU,https://docs.oracle.com/javase/7/docs/api/java/nio/FloatBuffer.html 关于使用swig进行包装的部分,请参考:https://dev59.com/t47ea4cB1Zd3GeqPDZp3 - V-master
@V-master 我理解的对吗,ByteBuffer可以转换为任何C++类型?那vector<vector<float>>呢? - VolAnd
1
@VolAnd 如果你想避免再次复制数据,就不能使用C++向量。如果你想直接使用Java中的数据,你需要使用POD数组,例如float *floatArray = env->GetDirectBufferAddress(javaFloatBufferObj);或者long *longArray = env->GetLongArrayElements(javaFloatArrayObj);。在Java代码中使用Java数组或直接缓冲区取决于你需要在Java中进行什么处理。请参见https://dev59.com/mmMk5IYBdhLWcg3w2RaT。 - Andrew Henle
显示剩余2条评论
1个回答

1
您可以使用关键本地实现来访问原始数据数组。这种技术允许直接访问jvm内存,无需在Java和本地代码之间传输数据。但是,这有以下的限制
  • 必须是静态的并且未同步;
  • 参数类型必须是原始类型或原始数组;
  • 实现不能调用JNI函数,即它不能分配Java对象或抛出异常;
  • 不应该长时间运行,因为它会在运行时阻塞GC。
关键本地的声明看起来像一个常规的JNI方法,除了:
  • JavaCritical_开头而不是Java_
  • 没有额外的JNIEnv*jclass参数;
  • Java数组通过两个参数传递:第一个是数组长度,第二个是指向原始数组数据的指针。也就是说,不需要调用GetArrayElements等函数,可以直接使用一个直接的数组指针。

请查看原始答案来源文章以获取详细信息。


1
谢谢您提供的信息。这次它并没有帮上忙(因为受限制),但在未来确实可以帮上大忙。 - VolAnd

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接