Нет утечки.
ByteBuffer.allocateDirect()
выделяет память из собственной кучи / свободного хранилища (думаю, malloc()
), которое, в свою очередь, помещается в экземпляр ByteBuffer
.
Когда экземпляр ByteBuffer
получает сборщик мусора, собственная память освобождается ( в противном случае вы бы потеряли собственную память ).
Вы вызываете System.gc()
в надежде, что собственная памятьисправлено немедленно .Однако вызов System.gc()
- это всего лишь запрос , который объясняет, почему ваш второй оператор журнала не сообщает вам, что память была освобождена: это потому, что она еще не была!
В вашей ситуацииочевидно, в куче Java достаточно свободной памяти, и сборщик мусора решает ничего не делать: вследствие этого недоступные экземпляры ByteBuffer
еще не собраны, их финализатор не запущен и собственная память не освобождена.
Кроме того, имейте в виду эту ошибку в JVM (хотя не знаю точно, как она применима к Dalvik), когда большое распределение прямых буферов приводит к неисправимым OutOfMemoryError
.
Вы прокомментировали управление вещами из JNI.Это действительно возможно, вы можете реализовать следующее:
опубликовать native ByteBuffer allocateNative(long size)
точку входа, которая:
- вызывает
void* buffer = malloc(size)
для выделения собственной памяти - упаковывает вновь выделенный массив в экземпляр
ByteBuffer
с вызовом (*env)->NewDirectByteBuffer(env, buffer, size);
- преобразует локальную ссылку
ByteBuffer
в глобальную с (*env)->NewGlobalRef(env, directBuffer);
опубликует native void disposeNative(ByteBuffer buffer)
точку входа, которая:
- вызывает
free()
по адресу прямого буфера, возвращаемого *(env)->GetDirectBufferAddress(env, directBuffer);
- удаляет глобальную ссылку с помощью
(*env)->DeleteGlobalRef(env, directBuffer);
Как только вы вызываете disposeNative
в буфере, вы не должны использоватьссылка больше, так что это может быть очень подвержено ошибкам.Подумайте, нужен ли вам такой явный контроль над шаблоном распределения.
Забудьте, что я сказал о глобальных ссылках.На самом деле глобальные ссылки - это способ хранения ссылки в собственном коде (как в глобальной переменной), так что дальнейший вызов методов JNI может использовать эту ссылку.Таким образом, вы должны иметь, например:
- из Java, вызвать собственный метод
foo()
, который создает глобальную ссылку из локальной ссылки (полученной путем создания объекта с собственной стороны) и сохраняет его в собственной глобальной переменной (как jobject
) - один раз назад, снова из Java, вызовите собственный метод
bar()
, который получает jobject
, сохраненный foo()
и далее обрабатывает его - наконец, все еще из Java, последний вызов нативного
baz()
удаляет глобальную ссылку
Извините запутаница.