首先,我要说明我的JNA和Java直接本地内存分配的理解至少是基于直觉的,因此我正在尝试描述我对正在发生的事情的理解。除了回答之外,任何更正都是很好的……
我正在运行一个使用JNA混合Java和C本地代码的应用程序,并遇到了一个可重现的问题,即Java垃圾收集器无法释放对直接本地内存分配的引用,导致C堆内存不足。
我确定我的C应用程序不是分配问题的源头,因为我将一个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堆分配内存,但由于Java ByteBuffer对象相对较小,没有清理指向该内存的ByteBuffer。
到目前为止,我发现在我的函数中手动运行GC将提供所需的清理,但这似乎既不是好主意,也不是好解决方案。
如何更好地管理此问题,以便适当释放ByteBuffer空间并控制我的C堆空间?
我的理解是否不正确(我是否运行有误)?
编辑:将缓冲区大小调整为更符合我的实际应用程序,我正在为大约3000x2000的图像分配内存...
-XX:MaxDirectMemorySize=128m
,但是当我创建和丢弃太多的Memory
实例而没有调用System.gc()
时,仍然会出现OutOfMemoryError
。有了它,错误就消失了。 - Mark Jeronimus