例如:做
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()会删除全局引用
对困惑感到抱歉.