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性能优化——之控件的优化 前面讲了图像的优化,接下...
前言 上一篇已经讲了如何实现textView中粗字体效果,里面主要...
最近项目重构,涉及到了数据库和文件下载,发现GreenDao这个...
WebView加载页面的两种方式 一、加载网络页面 加载网络页面,...
给APP全局设置字体主要分为两个方面来介绍 一、给原生界面设...
前言 最近UI大牛出了一版新的效果图,按照IOS的效果做的,页...