使用Memory Analyzer分析内存溢出问题(分析hprof文件)

  • Post author:
  • Post category:其他


背景:生产环境有一个程序突然挂掉了,进去控制台没有任何反应,看到服务器已经生成了hprof文件,hprof后缀文件是堆内存溢出导致程序挂掉时保存的快照,但是是什么原因导致程序内存溢出的呢?记录一下分析hprof文件的流程笔记

注:

hprof是什么文件?

是java进程的内存镜像文件,里面包含了内存堆详细的使用信息,即某个时间点的java进程的内存快照



一、下载Memory Analyzer软件

Eclipse Memory Analyzer(MAT)是一款内存分析工具

下载Memory Analyzer的云盘地址(提取码:g142 ):


https://pan.baidu.com/s/115CaSY2Ojjq4sR5oXTSu6w



二、Memory Analyzer简单使用

在这里插入图片描述

上图最中央的那个饼状图展示了最大的几个对象所占内存的比例



底部有几个核心的菜单按钮

  • Histogram

    :可以列出内存中每个对象的名字、数量以及大小。


  • Dominator Tree

    :会将所有内存中的对象按大小进行排序,并且可以分析对象之间的引用结构


  • Top Consumers

    :通过图形列出最大的object以及包路径


  • Leak Suspects

    :分析内存泄漏的可能原因


  • Top Components

    : 列出大于总堆1%内存的对象



三、分析hprof文件

我们在jvm参数配置的时候加上这段-XX:+HeapDumpOnOutOfMemoryError

,项目发生OutOfMemoryError内存溢出时保存堆快照

在这里插入图片描述

1)首先把服务器生成的这个hprod后缀的文件下载到本地电脑,通过sz命令

2)打开Memory Analyzer,点击Open Heap Dump选中hprof文件

在这里插入图片描述

3)装入hprod文件后如图

从饼状图很明显就看出问题了,有对象占用了极大内存

在这里插入图片描述

4)点击Dominator Tree,看看那些对象占用内存排序以及关联对象之间的引用

从图中可以看到是一个线程占用了93%的内存,这线程里面关联了TreeMap以及poi的XSSFRow对象(暂时还分析不出原因)

在这里插入图片描述

注:

  1. Shallow Heap:就是对象本身占用内存的大小,不包含对其他对象的引用,也就是对象头加成员变量(不是成员变量的值)的总和。
  2. Retained Heap:是该对象自己的shallow size,加上从该对象能直接或间接访问到对象的shallow size之和。换句话说,retained size是该对象被GC之后所能回收到内存的总和。
  3. 重点是看Retained Heap,Retained Heap表示GC能回收到的内存

5)点击Leak Suspects,查看内存泄漏原因

在这里插入图片描述

在这里插入图片描述

分析:可以看出是某一个线程占用了大量内存,这个线程了有很多TreeMap对象,看到还有poi对象,可能是导出excel表格相关的操作导致的(用户的一个请求就是一个线程,有可能是用户在进行导出数据)

6)再点上图的See stacktrace

在这里插入图片描述

从这个异常栈可以定位到出错的代码位置,可以看到出错的位置是AssetBaseQueryController.export的方法,这个方法是我们使用poi导出excel的函数,里面使用了poi,poi的XSSFRow对象里面使用了TreeMap,结合上面的分析,错误原因就可以知道,是用户的一个请求在导出excel数据,而这个导出耗尽了内存,所以是在一个线程里面使用了大量的TreeMap,但是内存耗尽,所以程序挂掉了,也没办法触发GC回收了

写有大量数据的xlsx文件时,POI为我们提供了SXSSFWorkBook类来处理,这个类的处理机制是当内存中的数据条数达到一个极限数量的时候就flush这部分数据,再依次处理余下的数据,这个在大多数场景能够满足需求。

读有大量数据的文件时,使用WorkBook处理就不行了,因为POI对文件是先将文件中的cell读入内存,生成一个树的结构(针对Excel中的每个sheet,使用TreeMap存储sheet中的行)。如果数据量比较大,则同样会产生java.lang.OutOfMemoryError: Java heap space错误

在这里插入图片描述



四、解决方案

java对Excel的操作一般都是用POI,但是数据量大的话可能会导致频繁的FGC或OOM.

可以看出POI的对象以及相关的XML对象占用了绝大部分的内存消耗,频繁FGC说明这些对象一直存活,没有被回收。

原因是由于导出的数据比较大量,大概有10w行 * 50列,由于后台直接用XSSFWorkbook导出,在导出结束前内存有大量的Row,Cell,Style等,以及基于XLSX底层存储的XML对象没有被释放。

使用EasyExcel替换原来的poi导出方法

EasyExcel是阿里巴巴开源的一个excel处理框架,以使用简单、节省内存著称

EasyExcel能大大减少占用内存的主要原因是在解析Excel时没有将文件数据一次性全部加载到内存中,而是从磁盘上一行行读取数据,逐个解析。



版权声明:本文为qq_21187515原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。