天天看点

Heap Dump分析 java.lang.OutOfMemoryError: Java Heap space

背景:tomcat服务器平均一天一次昏迷状态,等待半天无响应,像是死了而端口还能通,但是一直不响应,后来就挂了,在日志中发现了java.lang.OutOfMemoryError: Java heap space,想到是否内存有问题

首先是了解了一下堆内存

-Xms5000M
-Xmx5000M
-XX:NewRatio=3
-XX:PermSize=200M
-XX:MaxPermSize=512M
           

网上查了很多资料,建议-Xmx设置为系统物理内存的1/4,-Xms为1/64,堆内存根据需要在这两个参数之间变化,此处设置为一样的,避免了来回切换,服务器物理内存32G,这个设置也算合理,就算是增加堆内存,也是有限,解决不了根本问题,所以想到使用监控工具看下内存情况

开启远程监控,当应用服务加入到系统任务之后就需要开启远程监控才能访问

双击tomcat/bin/tomcatw.exe,点击java标签,在options文本框末尾添加

-Djava.rmi.server.hostname=172.16.100.171
-Dcom.sun.management.jmxremote.port=8090
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.authenticate=false
           
Heap Dump分析 java.lang.OutOfMemoryError: Java Heap space

这个地方还可以修改-Xmx等参数,具体可以自己搜索,下面三个文本框内容要清空

找到jdk根目录下bin/jvisualvm.exe,双击打开

Heap Dump分析 java.lang.OutOfMemoryError: Java Heap space

重点关注监视标签页的堆标签页,几分钟的时间堆内存一下从2G升到4G,比较幸运的是,这个刚好是我监控开始几分钟后出现的刚好捕捉到,然后前线立马就有电话打进来了,情况跟之前类似,没有等到内存溢出就重启了服务,所以快照反应的情况是基本符合,这次分析以这个时间点为样本

在堆标签的右上角有个堆dump按钮,点击之后,jvisualvm会自动生成heapdump快照

分析heapdump快照文件有很多种工具,jvisualvm自身也可以分析,载入heapdump文件,文件较大时需要等一会,打开后,找到类标签页,列表中默认按照类的实例大小排列

Heap Dump分析 java.lang.OutOfMemoryError: Java Heap space

 比如此次占用最多的类型就是java.lang.String,双击这个类型可以看到对应实例

Heap Dump分析 java.lang.OutOfMemoryError: Java Heap space

但是这个实例太普遍了,且不是全局变量,暂时没啥好办法往下分析(因为第一次用,网上也没找到教程怎么分析,看到这么多陌生的参数之后有点懵,其实从类型这里可以看到是查询数据库的结果RowHandlerCallback放在了ArrayList里面,然后又放在Object[]数组中,但这些都是后来才明白的),所以此处介绍IBM HeapAnalyzer

下载最新版本,解压后就是一个jar文件,启动命令,426是版本号,不是目前最新版本

C:\Users\Administrator>java -Xmx12g -jar C:\Users\Administrator\Desktop\ha426.jar

heapdump文件越大,-Xmx设置越大,此次我的快照文件为5.7G,默认启动参数为12g,刚开始10g也成功过,后来不行了又扩大到12G,解析后的页面大概长这个样子

Heap Dump分析 java.lang.OutOfMemoryError: Java Heap space

右键根节点,有个locate a leak suspect菜单,点击后可以自动找到疑似内存溢出对象,这里就是选中节点的第一个子节点

Heap Dump分析 java.lang.OutOfMemoryError: Java Heap space

就是这个类型,这里可以看出,是一个ArrayList<Form>的集合占用较大,源头是从数据库查询解析拿到的,一开始只注意到第一个大节点,后来发现第二个第三个大节点都是这个类型,加起来也就是占用89%内存

下面是根据业务分析的结果

看了一下这个类型对应的表字段,有一个nclob类型,且查询语句好多都是select * from form,这样查询的结果就是不管nclob字段是否需要都会查询出来,想象一下有N多对象的情况

根源是找到了,但是这个表的业务量占整个系统的三分之一,返回ArrayList<Form>的接口也有好多个,要改起来也是难,目前还没啥好方法

得出结论就是:

1.终于知道为啥不能写select * from table了,以前只是见过这样的建议

2.网上教程真的好少,千篇一律的介绍工具怎么使用,没有分析方法,我也是走到这里进行不下去了,问人也都是不知道,其实这里已经跟代码跟业务有关了,可以凭着对系统的熟悉程度硬解析参考连接:

参考链接:

https://www.cnblogs.com/quyanhui/p/5924474.html

https://blog.csdn.net/forest_hou/article/details/5669427

https://blog.csdn.net/renfufei/article/details/77585294

http://www-01.ibm.com/support/docview.wss?uid=swg27006624&aid=1

http://java.sys-con.com/node/995699

http://java.sys-con.com/node/1229281

https://docs.oracle.com/javase/8/docs/technotes/samples/hprof.html