首先让我说我对 JNA 和 Java 如何直接分配本机内存的理解充其量是发自内心的,所以我试图描述我对正在发生的事情的理解。除了响应之外的任何更正都会很棒......
我正在运行一个使用 JNA 混合 Java 和 C 本机代码的应用程序,并且正在运行 Java 垃圾收集器无法释放对直接本机内存分配的引用,从而导致 C 堆内存不足的可重现问题。
我很肯定我的 C 应用程序不是分配问题的根源,因为我将 a 传递java.nio.ByteBuffer
到我的 C 代码中,修改缓冲区,然后在我的 Java 函数中访问结果。我在每个函数调用期间都有一个malloc
和一个对应free
的,但是在 Java 中反复运行代码后,malloc 最终会失败。
这是一组显示该问题的有点琐碎的代码——实际上,我试图在函数调用期间在 C 堆上分配大约 16-32MB。
我的 Java 代码执行以下操作:
public class MyClass{
public void myfunction(){
ByteBuffer foo = ByteBuffer.allocateDirect(1000000);
MyDirectAccessLib.someOp(foo, 1000000);
System.out.println(foo.get(0));
}
}
public MyDirectAccessLib{
static {
Native.register("libsomelibrary");
}
public static native void someOp(ByteBuffer buf, int size);
}
那么我的 C 代码可能是这样的:
#include <stdio.h>
#include <stdlib.h>
void someOp(unsigned char* buf, int size){
unsigned char *foo;
foo = malloc(1000000);
if(!foo){
fprintf(stderr, "Failed to malloc 1000000 bytes of memory\n");
return;
}
free(foo);
buf[0] = 100;
}
问题是在反复调用这个函数后,Java 堆有点稳定(增长缓慢),但 C 函数最终无法分配更多内存。在高层次上,我认为这是因为 Java 正在为 C 堆分配内存,但没有清理指向该内存的 ByteBuffer,因为 Java ByteBuffer 对象相对较小。
到目前为止,我发现在我的函数中手动运行 GC 将提供所需的清理,但这似乎既是一个糟糕的主意,也是一个糟糕的解决方案。
我怎样才能更好地管理这个问题,以便适当地释放 ByteBuffer 空间并控制我的 C 堆空间?
我对问题的理解是否不正确(我是否运行不正确)?
编辑:调整缓冲区大小以更能反映我的实际应用程序,我正在分配大约 3000x2000 的图像...