android – ByteBuffer不释放内存

前端之家收集整理的这篇文章主要介绍了android – ByteBuffer不释放内存前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
Android上,一个直接的ByteBuffer似乎并没有释放它的内存,甚至在调用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.

如何摆脱这种泄漏?

添加

按照Gregory在C方面处理alloc / free的建议,然后定义

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);
    }

然后在JAVA一边得到我的ByteBuffer

ByteBuffer myBuf = allocNative(LARGE_NUMBER);

并释放它

freeNative(myBuf);

不幸的是,虽然它分配的很好,它a)仍然保留根据Debug.getNativeHeapAllocatedSize()分配的内存和b)导致错误

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

我现在彻底迷惑了,我以为我至少明白了C方面的事情…为什么是free()没有返回内存?我在DeleteGlobalRef()中做错了什么?

解决方法

没有泄漏.

ByteBuffer.allocateDirect()从本机堆/免费存储(认为malloc())分配内存,然后将其包装到ByteBuffer实例中.

当ByteBuffer实例收集垃圾内容时,回收本机内存(否则将泄漏本机内存).

您正在调用System.gc(),希望立即回收原生内存.但是,调用System.gc()只是一个请求,它解释了为什么你的第二个日志语句不会告诉你内存已经被释放:这是因为还没有!

在您的情况下,Java堆中的空闲内存显然足够,垃圾收集器决定不执行任何操作:因此,不可访问的ByteBuffer实例尚未收集,其终结器不会运行,并且本机内存不会被释放.

此外,请注意JVM中的这个bug(不知道它如何适用于Dalvik),其中大量分配的直接缓冲区导致不可恢复的OutOfMemoryError.

你对JNI做的事情做了评论.这实际上是可能的,你可以实现以下几点:

>发布本机ByteBuffer allocateNative(long size)入口点:

调用void * buffer = malloc(size)来分配本地内存
>将新分配的数组转换为ByteBuffer实例,并调用(* env) – > NewDirectByteBuffer(env,buffer,size);
>使用(* env) – > NewGlobalRef(env,directBuffer)将ByteBuffer本地引用转换为全局引用.

>发布一个native void disposeNative(ByteBuffer buffer)入口点:

在*(env) – > GetDirectBufferAddress(env,directBuffer)返回的直接缓冲区地址上调用free();
>使用(* env) – > DeleteGlobalRef(env,directBuffer)删除全局参考;

一旦你在缓冲区上调用了disposeNative,那么你不应该使用引用,所以它可能非常容易出错.重新考虑您是否真的需要对分配模式进行明确的控制.

忘记我关于全局参考的内容.实际上,全局引用是一种在本机代码中存储引用的方法(如全局变量),以便进一步调用JNI方法可以使用该引用.所以你会有:

>从Java中调用本地方法foo(),该方法从本地引用(通过从本机创建对象获取)创建全局引用,并将其存储在本机全局变量(作为作业)中)
再次从Java中调用本地方法bar(),它获取foo()存储的jobject并进一步处理它
>最后,仍然从Java,最后一次调用native baz()会删除全局引用

对困惑感到抱歉.

猜你在找的Android相关文章