内存泄漏



定义

某些对象或者数据没有利用价值了,但是由于某些原因占用着内存,无法被回收,就造成了内存泄漏。

例子:比如说有一个数组对象,占用内存很大,在使用完毕以后,还有强应用引用着该数组对象,那么这块内存就无法回收。



内存泄漏种类

Java使用的内存种类包含三种,这三种类型的内存都可能发生内存泄漏。

  • 堆内存泄漏:
  • 如果在JVM中没有足够的内存空间分配给java对象,将会抛出OOM错误。
  • 原因:一般情况下是程序出现了问题,生成的对象占用过多堆内存,并且没有及时释放,从而造成内存泄漏
  • 本地内存泄漏:
  • 如果JVM无法获取更多的本地内存,它将抛出OOM错误。
  • 为什么无法获取更多的本地内存?内存被占用过多了,不够了
  • 原因:Java调用本地方法,这些本地方法有内存泄漏。


解决方法

压力测试环境,对一Java应用服务进行12小时稳定性压测,压测结束后服务器的CPU使用率还很高,使用top使用观察Java进程使用了720%,机器配置为8C。

lua 服务器内存泄漏工具 服务器内存泄露怎么办_JVM

使用jstat命令查看java进程,eden区内存占用了接近100%,老年代占用了99.79%,从FGC列看到JVM在不断做Full GC操作。

lua 服务器内存泄漏工具 服务器内存泄露怎么办_lua 服务器内存泄漏工具_02

通过上述分析,可以确定问题的原因是JVM有内存泄漏。

JVM内存泄漏问题的解决相对来说比较简单。

  1. 使用下面的命令dump出JVM内存映像
    jmap -dump:format=b,file=mydump.bin pid
    生成的dump文件会比较大,比JVM配置的堆大小相当
  2. 将dump文件下载到本地,因为分析dump文件比较耗费服务器资源,不要影响到线上服务器
  3. 使用内存泄漏工具分析这个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:

lua 服务器内存泄漏工具 服务器内存泄露怎么办_JVM_03

使用jmap -dump:format=b,file=mydump.bin 6384导出内存快照文件

lua 服务器内存泄漏工具 服务器内存泄露怎么办_Java_04

使用MAT定位使用内存最多的内存,再定位到代码位置

首先找到Dominator Tree,如下图:

lua 服务器内存泄漏工具 服务器内存泄露怎么办_lua 服务器内存泄漏工具_05

打开Dominator Tree,按照内存占用从大到小排序:

lua 服务器内存泄漏工具 服务器内存泄露怎么办_lua 服务器内存泄漏工具_06

找到占用内存最多的对象

lua 服务器内存泄漏工具 服务器内存泄露怎么办_lua 服务器内存泄漏工具_07