【转】又一次线上 OOM 排查经过
最近线上一个服务又出现了频繁Full GC的情况,导致提供的业务经常超时。问题出现非常不稳定,经过两周的时候,终于又捕捉到了一次Full GC,于是联系运维做Heap Dump之后,经过一系列分析,终于解决问题。这次的问题稍微复杂一点,但是也比较有代表性,用到了VisualVM和MAT两个工具,继续记录如下。
现象
这次使用公司的CAT监控平台看到的内存表现如下:

可以看到,具体表现是:
- 在很长一段时间内(数个小时),New GC比较频繁,Full GC较少(一小时个位数)。
- 过了某一时间点后,Full GC增加,New GC则减少。
- 将服务切换下线后,Memory Free逐渐回升,Full GC减少。
然后观察某一时刻的JMAP histo的内容如下:
num #instances #bytes class name
----------------------------------------------
1: 5958517 5840833584 [C
2: 37983 706246056 [B
3: 5960539 190737248 java.lang.String
4: 1522282 126603936 [Ljava.lang.Object;
5: 3692000 88608000 java.lang.Double
可以看到"[C"即"char[]"占用内存达到了5.8G,它正是String的内部结构,换句话说,因为存在了大量的大String导致这个问题。
联系运维进行了Heap Dump之后,就开始了分析的过程。因为服务器内存有8G,所以相应的DUMP也有8G,对分析也造成了一点小困难。
下面是一些工具的使用过程,操作系统是OS X 10.8。
使用VisualVM分析
首先使用VisualVM对Heap Dump进行分析。分析需要比较大的内存,而VisualVM默认的内存是256M,所以需要先设置/Applications/VisualVM.app/Contents/Resources/visualvm/etc/visualvm.conf中的最大内存量,这里我们设置成了4G-J-Xmx4096m。
好了,现在打开dump文件,整个分析过程共占用内存2G,仍然在可接受范围。之后分析内存,可以看到跟之前histo一样的类关系。

不同的是,这时候可以点进去,查看具体的对象。这里看到,竟然有几个大的String占用了756M的内存,看来我们找到问题了。

发现一个奇怪的现象:“计算保留大小”之后,这些String的保留大小都是0。
保留大小是什么呢?
它是分析工具从GC roots开始查找,找到的所有不会回收的对象,然后按照引用关系,计算出这个“对象以及它引用的对象”的内存大小。这里很有趣的是,这些对象的保留大小都是0。
开始我甚至以为是工具出了问题,后来想想,其实答案很简单:这些大String是临时对象,没有什么对象持有它——通过分析这些String的依赖关系也说明了这一点。这些对象是可以被回收的,换句话说,并不是有明显的内存泄露。
这个“没有对象持有的String”很有意思,让我想到了,会不会是log的过程中,产生了这个String?因为log的时候,会调用对象的toString()方法,而一个大对象的toString()可能是很大的。查看了一下String的内容,是[Class[field1=xxx,field2=yyy]]这种结构,几乎可以肯定是toString()的结果,那么我们现在只要查看一下具体的内容,到底是哪里太大就可以了。
可惜的是,VisualVM里我尝试了各种方法,都没有办法Dump出这个对象的数据,甚至还尝试了VisualVM提供的OQL。
OQL是一种类SQL的表达式,同时整合了一些Javascript的函数功能,但是它的缺点就是太慢了…最后尝试用OQL Dump对象未果。

使用MAT分析
转而尝试一下MAT。Eclipse MAT(Memory Analyzer Tool)是一个强大的内存分析工具,它可以方便的分析内存泄露的问题。实际使用之后,确实也感觉到比VisualVM更加强大一些。
我们使用MAT打开Dump文件,这也是一个漫长的过程。与VisualVM不同的是,MAT分析的时候,会在本地产生几个临时文件保存分析结果,所以相应的内存占用会小一些(1G左右),第二次打开也会快很多。
打开完成后,我们看到可以看到几个占用内存较大的对象,这就是最可疑的内存泄漏源。意外的是Size显示的只有1.3GB,而且我们的大String都没在里面。

开始我们一度认为是Dump文件有错,后来上网搜索才知道,因为MAT的主要目标是排查内存占用量,所以默认大小是不计算不可达对象的——而我们的String都是不可达对象。对应Eclipse的文档里有介绍http://wiki.eclipse.org/MemoryAnalyzer/FAQ#How_to_analyse_unreachable_objects。
于是我们按照说明,在"Preferences=>Memory Analyzer"中勾选"Keep Unreachable Objects",删除索引文件Dump同路径下的所有".index",即可看到所有的对象。

点击Histogram,即可看到类的占用。在类上选择"List Objects",即可看到所有对象。在对象上选择"Copy=>Value to File",即可保存到文件。

原理
之后我们通过分析对象,发现是因为某个用于缓存的对象使用了一个List结构,但是添加元素的没有去重,导致List越来越大造成的。这个对象本身占用内存只有110M,但是toString()出来的字符串达到700M的大小,所以引起了频繁的GC——最开始对象小的时候是New GC,后来对象大了,直接进入了年老代,就变成了Full GC。
总结
回到之前的问题,通过这次分析,我们可以这么总结:
将服务切换下线后,Memory Free逐渐回升,Full GC减少,Heap Dump的可达对象较少
这种情况不是内存泄露,有可能是对象创建开销太大造成的。
在1的前提下,New GC很频繁
这种情况可能是频繁创建小对象,或者创建一些较大的对象(尚不足以直接进入年老代)
在1的前提下,Full GC很频繁
这种情况是频繁创建大对象,或者创建了一些超大对象导致的。
第3种情况下,大对象toString()产生的大String很可能是罪魁祸首
【转】又一次线上 OOM 排查经过的更多相关文章
- 一次线上OOM故障排查经过
转贴:http://my.oschina.net/flashsword/blog/205266 本文是一次线上OOM故障排查的经过,内容比较基础但是真实,主要是记录一下,没有OOM排查经验的同学也可以 ...
- Java线上问题排查思路及Linux常用问题分析命令学习
前言 之前线上有过一两次OOM的问题,但是每次定位问题都有点手足无措的感觉,刚好利用星期天,以测试环境为模版来学习一下Linux常用的几个排查问题的命令. 也可以帮助自己在以后的工作中快速的排查线上问 ...
- java:线上问题排查常用手段(转)
出处:java:线上问题排查常用手段 一.jmap找出占用内存较大的实例 先给个示例代码: import java.util.ArrayList; import java.util.List; imp ...
- 火山引擎MARS-APM Plus x 飞书 |降低线上OOM,提高App性能稳定性
通过使用火山引擎MARS-APM Plus的memory graph功能,飞书研发团队有效分析定位问题线上case多达30例,线上OOM率降低到了0.8‰,降幅达到60%.大幅提升了用户体验,为飞书的 ...
- BTrace:线上问题排查工具
BTrace简介 GitHub地址:BTrace 下载地址:v1.3.11.3 官方使用教程:Btrace使用教程 使用场景 BTrace 是一个事后工具,所谓事后工具就是在服务已经上线了,但是发现存 ...
- 记一次线上bug排查-quartz线程调度相关
记一次线上bug排查,与各位共同探讨. 概述:使用quartz做的定时任务,正式生产环境有个任务延迟了1小时之久才触发.在这一小时里各种排查找不出问题,直到延迟时间结束了,该任务才珊珊触发.原因主要就 ...
- 线上问题排查神器 Arthas
线上问题排查神器 Arthas 之前介绍过 BTrace,线上问题排查神器 BTrace 的使用,也说它是线上问题排查神器.都是神器,但今天这个也很厉害,是不是更厉害不好说,但是使用起来非常简单.如果 ...
- JVM 线上故障排查基本操作--CPU飙高
JVM 线上故障排查基本操作 CPU 飚高 线上 CPU 飚高问题大家应该都遇到过,那么如何定位问题呢? 思路:首先找到 CPU 飚高的那个 Java 进程,因为你的服务器会有多个 JVM 进程.然后 ...
- Java架构师线上问题排查,这些命令程序员一定用得到!
Java架构师线上问题排查,这些命令程序员一定用得到! 线上问题排查,以下场景,你遇到过吗? 一.了解机器连接数情况 问题:1.2.3.4的sshd的监听端口是22,如何统计1.2.3.4的sshd服 ...
随机推荐
- 浅谈I/O模型
在学习线程,NIO等知识时都需要知道一些基础知识. 一.什么是同步或异步 同步:个人通俗理解多个人排队打饭一个窗口,只有前面一个人打完了,后面的人才能打.如果前面人因为什么原因一直站在那里不走,后面的 ...
- Algorithms(fourth edition)——无向图
1.设计图基本操作API 2.用什么数据结构来表示图并实现API 要求:(1)要预留足够空间 (2)实例方法实现要快 三个选择: 邻接矩阵:布尔矩阵,不满足条件一,而且无法表示平行边 边的数组:不满足 ...
- 一次失败的刷题经历:[LeetCode]292之尼姆游戏(Nim Game)
最近闲来无事刷LeetCode,发现这道题的Accept Rate还是挺高的,尝试着做了一下,结果悲剧了,把过程写下来,希望能长点记性.该题的描述翻译成中文如下: 你正在和你的朋友玩尼姆游戏(Nim ...
- C#操作XML配置文件
代码为C#操作xml配置文件的范例类,函数SetValue用于向配置文件写入一个值,GetValue用于根据Key获取相应值。这种方法的配置文件不需要手动创建,程序在运行后会自动处理创建。 注意:1. ...
- 理解机器为什么可以学习(五)---Noise and Error
之前我们讨论了VC Dimension,最终得到结论,如果我们的hypetheset的VC Dimension是有限的,并且有足够的资料,演算法能够找到一个hypethesis,它的Ein很低的话,那 ...
- w3wp CPU 100%问题解决
问题: web服务器w3wp CPU占用率非常高,导致整个服务器CPU 100%占用,问题无法正常重现 解决方法: --问题尚未解决,此处记录目前的解决状态 1)下载windbg 参考https:// ...
- 小谈Bing桌面、Bing助手的现状与发展
Teamwork中我的工作,既然写了就直接放上来吧. 一.Bing搜索的相关背景 第一,必应搜索前几年的发展重点在于欧美市场,并且取得了一定的成效:根据 Hitwise 的统计数据,Bing 在 20 ...
- 错误:支持“EFDbContext”上下文的模型已在数据库创建后发生更改。请考虑使用 Code First 迁移更新数据库(http://go.microsoft.com/fwlink/?LinkId
支持"EFDbContext"上下文的模型已在数据库创建后发生更改.请考虑使用 Code First 迁移更新数据库(http://go.microsoft.com/fwlink/ ...
- linux系统负载状态检查脚本
为了便于分析问题,编写了一个linux状态检查脚本,脚本可放置任意目录,脚本执行检测后会输出日志记录到当前目录下.直接执行脚本可用于一次检测,可通过日志进行分析.如果需要长时间监测,可执行-x参数,脚 ...
- mysql-Innodb事务隔离级别-repeatable read详解1
经验总结: Python使用MySQLdb数据库后,如使用多线程,每个线程创建一个db链接,然后再各自创建一个游标cursor,其中第一个线程读一个表中数据为空,第二个写入该表一条数据并提交,第一个线 ...