内存泄漏
定义
某些对象或者数据没有利用价值了,但是由于某些原因占用着内存,无法被回收,就造成了内存泄漏。
例子:比如说有一个数组对象,占用内存很大,在使用完毕以后,还有强应用引用着该数组对象,那么这块内存就无法回收。
内存泄漏种类
Java使用的内存种类包含三种,这三种类型的内存都可能发生内存泄漏。
- 堆内存泄漏:
- 如果在JVM中没有足够的内存空间分配给java对象,将会抛出OOM错误。
- 原因:一般情况下是程序出现了问题,生成的对象占用过多堆内存,并且没有及时释放,从而造成内存泄漏
- 本地内存泄漏:
- 如果JVM无法获取更多的本地内存,它将抛出OOM错误。
- 为什么无法获取更多的本地内存?内存被占用过多了,不够了
- 原因:Java调用本地方法,这些本地方法有内存泄漏。
解决方法
压力测试环境,对一Java应用服务进行12小时稳定性压测,压测结束后服务器的CPU使用率还很高,使用top使用观察Java进程使用了720%,机器配置为8C。
使用jstat命令查看java进程,eden区内存占用了接近100%,老年代占用了99.79%,从FGC列看到JVM在不断做Full GC操作。
通过上述分析,可以确定问题的原因是JVM有内存泄漏。
JVM内存泄漏问题的解决相对来说比较简单。
- 使用下面的命令dump出JVM内存映像
jmap -dump:format=b,file=mydump.bin pid
生成的dump文件会比较大,比JVM配置的堆大小相当 - 将dump文件下载到本地,因为分析dump文件比较耗费服务器资源,不要影响到线上服务器
- 使用内存泄漏工具分析这个dump文件,比如说MAT。
找到占用内存最多的对象,结合程序分析这个对象在程序中的使用,一般很容易就定位出内存泄漏的原因。
PS:上述其实是由于CPU打满了,然后发现原因是频繁FullGC。而频繁Full GC的原因是因为发生了内存泄漏。
实战
首先有一个程序,存在内存泄漏:
/**
* 内存泄漏示例
* @author huangy on 2020-02-23
*/
public class OOMDemo {
public static void main(String[] args) throws Exception {
// 程序运行的时候,生成一个大的数组对象,然后没有释放
int[] arr = new int[1024 * 1024];
while (true) {
Thread.sleep(1000);
}
}
}
使用jps查看java进程pId:
使用jmap -dump:format=b,file=mydump.bin 6384
导出内存快照文件
使用MAT定位使用内存最多的内存,再定位到代码位置
首先找到Dominator Tree,如下图:
打开Dominator Tree,按照内存占用从大到小排序:
找到占用内存最多的对象