【Java】解决程序堆内存正常,但实际内存过高的问题
一、前言
最近遇到一个很头疼的问题,用Java写的工具运行几天后,内存涨到8G以上,我用jvisualvm监控发现堆内存持续稳定未出现泄漏,然后就各种搜问题,最后经过1周多时间排查处理,最后终于修复了,因此记录一下。
二、问题与解决方案
1. jni 内存泄漏问题
由于我的Java需要调用C++语言写的模块,因此采用了jni调用,将java中的byte[]传给C++进行处理,最后返回已处理的byte[]数据,这里遇到了第一个内存泄漏问题。
// jni c++ jbyte* bytedata = env->GetByteArrayElements(data, 0); // 对bytedata进行处理后返回 //c_decode_data(bytedata) jbyteArray result = env->NewByteArray(len); env->SetByteArrayRegion(result, 0, len, bytedata); env->ReleaseByteArrayElements(data, bytedata, 0); return result
上面的代码运行会产生内存泄漏,具体原因如下:
1). 使用env->GetByteArrayElements后必须调用env->ReleaseByteArrayElements,其他GetXXX(...)也是类似。
2). 使用env->NewByteArray生成的对象需要调用env->
DeleteLocalRef删除引用,其他NewXXX(...)也是类似。
2. Java堆内存正常,但是实际使用内存过高
使用pmap -x 打印内存信息时,发现有很多64MB的[anon]内存块,如下图(图片来自网络截取)。
引起上面的原因很多地方都说是glibc自2.11以后的版本导致的,glibc使用arena的memory pool来解决多线程内存分配竞争的问题。解决方法是在启动Java程序前设置MALLOC_ARENA_MAX=1或者其他比较小的值来解决,推荐值为4。也可以通过使用tcmalloc库来规避这个问题。
3. glibc的另一个问题
C语言中的malloc和free在不同平台有不同的实现,在linux上由glibc实现,glibc内部维护了一个内存池,在调用free的时候内存只会归还给glibc,再由glibc决定是否归还给系统。如果想立即归还给系统可以主动调用malloc_trim(0)。
三、总结
以上就是本次解决Java内存泄漏问题学到的几个知识点,我也只是大致了解了一下,对其原理并未深入理解。大家如果遇到同样的问题时,可以尝试我介绍的方法,如果能够深入理解原理就更好了。
欢迎微信搜索"游戏测试开发"关注一起沟通交流。