JNA / ByteBuffer не освобождается и приводит к нехватке памяти кучи C
Позвольте мне начать с того, что мое понимание того, как JNA и Java напрямую распределяют внутреннюю память, в лучшем случае интуитивно, поэтому я пытаюсь описать свое понимание происходящего. Любые исправления в дополнение к ответам было бы здорово ...
Я запускаю приложение, которое смешивает нативный код Java и C с использованием JNA и сталкиваюсь с воспроизводимой проблемой, когда сборщик мусора Java не может освободить ссылки для прямого выделения собственной памяти, что приводит к нехватке памяти в куче C.
Я уверен, что мое приложение C не является источником проблемы распределения, так как я передаюjava.nio.ByteBuffer
в мой C-код, модифицируя буфер, а затем получая доступ к результату в моей Java-функции. У меня есть одинmalloc
и один соответствующийfree
во время каждого вызова функции, но после многократного запуска кода в Java malloc в конечном итоге завершится ошибкой.
Вот несколько упрощенный набор кода, который демонстрирует проблему:реально я пытаюсь выделить около 16-32MB на кучу C во время вызова функции.
Мой код 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 ...