liujianguo
公司的业务量比较大,在生产环境如果经常出现OOM(Out Of Memory,JVM内存溢出)的现象,那该如何快速响应,快速定位,快速恢复问题呢?
本文将通过一个线上环境JVM内存溢出的案例向大家介绍一下处理思路与分析方法。
案例:架构组接到某项目组反馈,Zabbix监控上显示JMX不可用,请求协助处理。
分析思路:
JMX:Java Management Extensions,即Java管理扩展,是一个为应用程序、设备、系统等植入管理功能的框架。 JMX可以跨越一系列异构操作系统平台、系统体系结构和网络传输协议,灵活的开发无缝集成的系统、网络和服务管理应用。
具体分析过程如下:
通常线上的故障会对业务造成重大影响,影响用户体验,故如果线上服务器出现故障,应规避对业务造成影响,但不能简单的重启服务器,因为需要尽可能保留现场,为后续的问题分析打下基础。
那我们如何快速规避对业务的影响,并能保留现场呢?
通常的做法是隔离故障服务器。
通常线上服务器是集群部署,一个好的分布式负载方案会自动剔除故障的机器,从而实现高可用架构,但如果未被剔除,则需要运维人员将故障服务器进行剔除,保留现场进行分析。
发生内存泄露,通常情况下是由于代码的原因造成的,一般无法立即对代码进行修复,很容易会发送连锁反应造成应用服务器一台一台接连宕机,故障面积会慢慢扩大,针对此种情况,应快速定位发生内存泄露的原因,将该服务进行降级,避免对其他服务造成影响。最简单的降级方法是根据F5(Nginx)转发策略,对该功能定向到一个单独的集群,与其他流量进行隔离,确保其他业务不受牵连,给故障排查、解决提供宝贵的缓冲时间。
首先可以通过查看日志,确定是哪种内存溢出,堆内存溢出可发生的地方:Java heap space(堆空间)、perm space(持久代)。
收集Dump文件有两种方式:
在每次发生内存溢出时,JVM会自动将堆转储,dump文件存放在-XX:HeapDumpPath指定的路径下。
在获取Dump文件后,可以使用工具MAT(MemoryAnalyzer)进行分析,该工具大家可以通过百度自行下载。
使用MAT打开Dump文件后,首页截图如下:
Shallow Size: 对象自身占用的内存大小,不包括它引用的对象。 针对非数组类型的对象,它的大小就是对象与它所有的成员变量大小的总和。 当然这里面还会包括一些java语言特性的数据存储单元。 针对数组类型的对象,它的大小是数组元素对象的大小总和。 Retained Size: Retained Size=当前对象大小+当前对象可直接或间接引用到的对象的大小总和。(间接引用的含义:A->B->C, C就是间接引用) 换句话说,Retained Size就是当前对象被GC后,从Heap上总共能释放掉的内存。
上图中,GC Roots直接引用了A、B和D三个对象。
A对象的Retained Size=A对象的Shallow Size
B对象的Retained Size=B对象的Shallow Size + C对象的Shallow Size
这里不包括D对象,因为D对象被GC Roots直接引用。
如果GC Roots不引用D对象呢?
此时, B对象的Retained Size=B对象的Shallow Size + C对象的Shallow Size + D对象的Shallow Size
工具按钮介绍:
:直方图视图,将堆中所有的内存消耗情况统计出来,其如图所示:
:内存使用树状结构,以线程为维度,树状形式展开,如图所示:
线程栈,其截图如下:
根据该图,可以明确,堆的总大小为1.9G,被4个线程全部占据,导致其他线程无法再申请资源,抛出堆内存溢出错误。
接下来,我通常的做法是直接去看
这个视图(以线程为基本维度,查找线程中占用内存的对象),为后续定位排查提供必要的依据。
从上面的截图中可以得出如下关键信息点:
由于SQL查询代码中,是用HashMap来接收数据库中的返回字段,无法一时间看出是那个查询,那我们能不能精确找到是哪一个查询,哪一行代码,甚至与哪一条SQL语句呢?
答案是可以的,我们可以从
视图一探究竟。
温馨提示:
视图使用技巧:展开技巧:沿着使用率最高的项一层一层进行展开,直至发现具体占用内存的对象。
接下来我们从 视图去寻找是哪个方法,哪条SQL语句触发的。 具体方法:首先完全展开一个线程,从展开图的底部向上寻找: 其线程的入口(控制层代码)
继续往上查找,要找到SQL语句,应该找到Mybatis处理结果集相关的类,如图所示:
然后展开boundSql即能找到SQL语句:
然后鼠标可以放在SQL属性中,右键,可以将SQL语句复制出来。
由于这里涉及到公司的代码机密,故在这里不贴出具体的SQL语句。
这里根据后面的分析,原来是在做导出功能的时候,没有使用分页对数据进行分页查询,分页写入Excel文件,而是一次将全部数据查询,导致导出功能如果并发数超过4个时,就会将所有内存耗尽。
解决方案:
作者介绍:丁威,上海优速物流科技架构师,《RocketMQ技术内幕》作者
https://www.cnblogs.com/softidea/p/5833196.htm