ByteBuffer的不释放内存内存、ByteBuffer

2023-09-12 06:14:58 作者:倪:倪是风儿、小倪鳅

在Android上,一个直接ByteBuffer不是永远似乎并没有释放其内存,调用System.gc即使是在()。

On Android, a direct ByteBuffer does not ever seem to release its memory, not even when calling System.gc().

例:做

Log.v("?", Long.toString(Debug.getNativeHeapAllocatedSize()));
ByteBuffer buffer = allocateDirect(LARGE_NUMBER);
buffer=null;
System.gc();
Log.v("?", Long.toString(Debug.getNativeHeapAllocatedSize()));

给出两个数字在日志中,第二个是至少LARGE_NUMBER大于所述第一

gives two numbers in the log, the second one being at least LARGE_NUMBER larger than the first.

我如何摆脱这种泄漏?

补充:

继建议,由格雷戈里处理分配/免费对C ++的一面,我再定义

Following the suggestion by Gregory to handle alloc/free on the C++ side, I then defined

JNIEXPORT jobject JNICALL Java_com_foo_bar_allocNative(JNIEnv* env, jlong size)
    {
    void* buffer = malloc(size);
    jobject directBuffer = env->NewDirectByteBuffer(buffer, size);
    jobject globalRef = env->NewGlobalRef(directBuffer);
    return globalRef;
    }

JNIEXPORT void JNICALL Java_com_foo_bar_freeNative(JNIEnv* env, jobject globalRef)
    {
    void *buffer = env->GetDirectBufferAddress(globalRef);
    free(buffer);
    env->DeleteGlobalRef(globalRef);
    }

然后我得到我的ByteBuffer在Java端与

I then get my ByteBuffer on the JAVA side with

ByteBuffer myBuf = allocNative(LARGE_NUMBER);

和免费用

freeNative(myBuf);

不幸的是,虽然它并分配很正常,但一)仍保持根据分配的内存 Debug.getNativeHeapAllocatedSize()和b)会导致错误

W/dalvikvm(26733): JNI: DeleteGlobalRef(0x462b05a0) failed to find entry (valid=1)

我现在是彻底糊涂了,我想我至少明白了事情的C ++方...为什么是免费的()没有返回的记忆?而我在做什么毛病 DeleteGlobalRef()

推荐答案

有没有泄漏。

ByteBuffer.allocateDirect()从本机堆/自由存储分配内存(想想的malloc())的进而被包裹在一个的ByteBuffer 实例。

ByteBuffer.allocateDirect() allocates memory from the native heap / free store (think malloc()) which is in turn wrapped in to a ByteBuffer instance.

的ByteBuffer 实例被垃圾回收,本机内存被回收(,否则你会泄漏本机内存的)。

When the ByteBuffer instance gets garbage collected, the native memory is reclaimed (otherwise you would leak native memory).

您正在呼叫的System.gc()在希望的本机内存被回收的马上的。然而,调用的System.gc()只是一个请求这也解释了为什么你的第二个日志语句不告诉你的内存已经释放:它的因为它有没有!

You're calling System.gc() in hope the native memory is reclaimed immediately. However, calling System.gc() is only a request which explains why your second log statement doesn't tell you memory has been released: it's because it hasn't yet!

在你的情况,也显然是足够的可用内存中的Java堆和垃圾收集器决定什么也不做:作为一个后果,可达的ByteBuffer 实例不收集,他们的终结并不运行,本机内存不会被释放。

In your situation, there is apparently enough free memory in the Java heap and the garbage collector decides to do nothing: as a consequence, unreachable ByteBuffer instances are not collected yet, their finalizer is not run and native memory is not released.

另外,请记住,这错误在JVM(不知道如何将它应用于Dalvik的虽然),其中的直接缓冲区重分配将导致不可恢复的的OutOfMemoryError

Also, keep in mind this bug in the JVM (not sure how it applies to Dalvik though) where heavy allocation of direct buffers leads to unrecoverable OutOfMemoryError.

你的评论这样做控制事情的JNI。其实,这是可能的,你可以实现以下内容:

You commented about doing controlling things from JNI. This is actually possible, you could implement the following:

发布本地的ByteBuffer allocateNative(长尺寸)入口点: 电话无效*缓冲区=的malloc(大小)来分配本机内存 包装了新分配的数组成的ByteBuffer 实例与调用(* ENV) - > NewDirectByteBuffer(ENV,缓冲区,大小) ; 转换的ByteBuffer 本地引用一个全球性与(* ENV) - > NewGlobalRef(ENV,directBuffer); calls void* buffer = malloc(size) to allocate native memory wraps the newly allocated array into a ByteBuffer instance with a call to (*env)->NewDirectByteBuffer(env, buffer, size); converts the ByteBuffer local reference to a global one with (*env)->NewGlobalRef(env, directBuffer);

发布本地无效disposeNative(ByteBuffer的缓冲区)入口点:

电话免费()返回的直接缓冲区地址*(ENV) - > GetDirectBufferAddress(ENV,directBuffer); 删除与全球REF(* ENV) - > DeleteGlobalRef(ENV,directBuffer); calls free() on the direct buffer address returned by *(env)->GetDirectBufferAddress(env, directBuffer); deletes the global ref with (*env)->DeleteGlobalRef(env, directBuffer);

在你调用 disposeNative 上的缓冲,你不应该再使用参考,因此它可能是很容易出错。重新考虑是否真的需要这种明确的控制权的分配模式。

Once you call disposeNative on the buffer, you're not supposed to use the reference anymore, so it could be very error prone. Reconsider whether you really need such explicit control over the allocation pattern.

忘了我说过的全局引用。事实上全局引用是一种存储在本地code基准(如在一个全局变量),以便进一步调用JNI方法可以使用​​该引用。所以,你会如:

从Java,调用本地方法 FOO()这将创建一个全球参考出来的局部引用(通过从本机端对象获得)并存储在一个本地全局变量(作为 jobject ) 一次回来,再从Java,调用本地方法酒吧()它得到了 jobject 存储通过 FOO()并进一步处理它 最后,还是从Java到原生最后通话巴兹()删除全局引用 from Java, call native method foo() which creates a global reference out of a local reference (obtained by creating an object from native side) and stores it in a native global variable (as a jobject) once back, from Java again, call native method bar() which gets the jobject stored by foo() and further processes it finally, still from Java, a last call to native baz() deletes the global reference

抱歉的混乱。