Memory Analyzer 是什么  Memory Analyzer(MAT) 是由 eclipse 提供的一款可视化 java 堆分析器,可以快速帮你查找内存泄漏并减少内存消耗,它提供了可视化的报告并可以查看堆文件中各个对象的信息。官网下载链接  什么时候用 MAT  当线上的某个 service/ 微服务老莫 ..

使用 Memory Analyzer(MAT) 定位内存溢出 / 泄漏

本贴最后更新于 200 天前,其中的信息可能已经物是人非

Memory Analyzer 是什么

 Memory Analyzer(MAT) 是由 eclipse 提供的一款可视化 java 堆分析器,可以快速帮你查找内存泄漏并减少内存消耗,它提供了可视化的报告并可以查看堆文件中各个对象的信息。官网下载链接

什么时候用 MAT

 当线上的某个 service/ 微服务老莫名其妙的崩掉或者出现莫名问题,当你需要对不熟悉的代码进行调优,当内存溢出 / 泄漏且代码量巨大的时候。因为 MAT 的可视化,你很容易就能把堆文件里面对应的大对象找出来,随之可以快速的定位大对象对应的代码处进行查看。

MAT 实践

1) 内存溢出

  /**
   * 测试内存溢出 
   */
  public class TestHeapSpace {

	public static void main(String[] args) throws Exception{
	  getLines("/Users/yangjunwei/data/test/HNP_StatsFootNot.csv").forEach(x-> System.out.println(x));
	}

    /**
	 * 返回文件的内容 * @param file
	 * @return
	 * @throws Exception
    */  
	private static List getLines(String file) throws Exception{
	  BufferedReader reader = null;
	  List list = new ArrayList<>();
	  try{
		reader = new BufferedReader(new FileReader(new File(file)));
		String tmp =null;
		while ((tmp = reader.readLine()) != null){
		  list.add(tmp);
		}
	  }finally {
		if( reader != null ){
		  reader.close();
		}
	  }
	  return list;
	}
  }

 上面是一个读取文件内容的例子,乍一看问题不大,但文件内容过多,执行内存不是太大的情况下会发生什么?

  ll -h /Users/yangjunwei/data/test/HNP_StatsFootNot.csv
  22M  1 25 13:06 /Users/yangjunwei/data/test/HNP_StatsFootNot.csv
  
  // 模拟内存溢出,限定该程序最大堆内存为10M,因为文件内容为22M,10M必然加载不了22M的文本信息,所以就发生了内存溢出异常并导出了堆文件(启动参数加了-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath)。
  java -jar -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath='/Users/yangjunwei/data/test/head_outMemory.hprof' -Xmx10M yy_test_heap_outMemory.jar
  java.lang.OutOfMemoryError: Java heap space
  Dumping heap to /Users/yangjunwei/data/test/head_outMemory.hprof ...
  Heap dump file created [10782477 bytes in 0.064 secs]
  Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
	at java.util.Arrays.copyOfRange(Arrays.java:3664)
	at java.lang.String.<init>(String.java:207)
	at java.io.BufferedReader.readLine(BufferedReader.java:356)
	at java.io.BufferedReader.readLine(BufferedReader.java:389)
	at com.winasdaq.yy_test.TestHeapSpace.getLines(TestHeapSpace.java:32)
	at com.winasdaq.yy_test.TestHeapSpace.main(TestHeapSpace.java:15)
  ll -h /Users/yangjunwei/data/test/head_outMemory.hprof
  10M  2  1 15:44 /Users/yangjunwei/data/test/head_outMemory.hpro

 下来咱们就用 MAT 来分析一下这个 10M 的堆文件
 1.1 打开要分析的堆文件

 1.2 选择 LEAk REPORT

 1.3 LEAk REPORT 的饼图 (由这个饼图发现有一个问题,有一个大对象占了 8.7M 的内存)

 1.4 揪出这个大对象

 1.5 定位问题
 发现那个大对象是一个 ArrayList, 并占了百分之 97 的内存。这个对象的里面的记录正是文本的一行行记录。
2) 内存泄漏
 如果上述代码申请了足够的内存且在一个 webservice 里面,把下面代码代码注释掉就会发生内存泄漏。

 if( reader != null ){
	reader.close();
 }

 当一个对象失去所有的引用的时候,在 GC 的时候,就可以将其回收,反之则不回收,因为 reader 里面加载了文件的内容还没有关闭,所以它不会被 GC 回收,一个泄漏不可怕,多次泄漏之后堆内存里面会存放大量的不能被 GC 回收的对象,最终会导致于把内存撑爆,发生内存溢出的情况。用 MAT 实践同样能定位到问题,这里就不做赘述。
堆文件导出
 1)被动导出,上面测试内存溢出的范例中已有,启动参数加上 (-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPat) 即可,这种情况只是在内存溢出的时候才会被动导出堆文件
 2)利用 jdk 的工具 jmap 把堆文件主动导出 (openjdk 的话要另外安装 openjdk-devel 包)

  $ ./jmap
  Usage:
    jmap [option] <pid>
        (to connect to running process)
    jmap [option] <executable <core>
        (to connect to a core file)
    jmap [option] [server_id@]<remote server IP or hostname>
        (to connect to remote debug server)

  where <option> is one of:
    <none>               to print same info as Solaris pmap
    -heap                to print java heap summary
    -histo[:live]        to print histogram of java object heap; if the "live"
                         suboption is specified, only count live objects
    -clstats             to print class loader statistics
    -finalizerinfo       to print information on objects awaiting finalization
    -dump:<dump-options> to dump java heap in hprof binary format
                         dump-options:
                           live         dump only live objects; if not specified,
                                        all objects in the heap are dumped.
                           format=b     binary format
                           file=<file>  dump heap to <file>
                         Example: jmap -dump:live,format=b,file=heap.bin <pid>
    -F                   force. Use with -dump:<dump-options> <pid> or -histo
                         to force a heap dump or histogram when <pid> does not
                         respond. The "live" suboption is not supported
                         in this mode.
    -h | -help           to print this help message
    -J<flag>             to pass <flag> directly to the runtime system

 先 ps -ef|grep java,把需要导出的 java 的 pid 找出来,然后通过 jmap 命令导出

  // 导出pid为1151的堆文件,格式2进制,导出文件为/Users/yangjunwei/data/test/1151.hprof
  ./jmap -dump:format=b,file='/Users/yangjunwei/data/test/1151.hprof' 1151
  Dumping heap to /Users/yangjunwei/data/test/1151.hprof ...
  Heap dump file created

  • B3log

    B3log 是一个开源组织,名字来源于“Bulletin Board Blog”缩写,目标是将独立博客与论坛结合,形成一种新的网络社区体验,详细请看 B3log 构思。目前 B3log 已经开源了多款产品:PipeSoloSymWide 等,欢迎大家加入,贡献开源。

    1889 引用 • 3628 回帖 • 590 关注
  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    2268 引用 • 7693 回帖 • 925 关注
回帖   
请输入回帖内容...