又一次线上OOM排查经过

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

现象

这次使用公司的CAT监控平台看到的内存表现如下:

可以看到,具体表现是:

  1. 在很长一段时间内(数个小时),New GC比较频繁,Full GC较少(一小时个位数)。
  2. 过了某一时间点后,Full GC增加,New GC则减少。
  3. 将服务切换下线后,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。

总结

回到之前的问题,通过这次分析,我们可以这么总结:

  1. 将服务切换下线后,Memory Free逐渐回升,Full GC减少,Heap Dump的可达对象较少

    这种情况不是内存泄露,有可能是对象创建开销太大造成的。

  2. 在1的前提下,New GC很频繁

    这种情况可能是频繁创建小对象,或者创建一些较大的对象(尚不足以直接进入年老代)

  3. 在1的前提下,Full GC很频繁

    这种情况是频繁创建大对象,或者创建了一些超大对象导致的。

  4. 第3种情况下,大对象toString()产生的大String很可能是罪魁祸首

【转】又一次线上 OOM 排查经过的更多相关文章

  1. 一次线上OOM故障排查经过

    转贴:http://my.oschina.net/flashsword/blog/205266 本文是一次线上OOM故障排查的经过,内容比较基础但是真实,主要是记录一下,没有OOM排查经验的同学也可以 ...

  2. Java线上问题排查思路及Linux常用问题分析命令学习

    前言 之前线上有过一两次OOM的问题,但是每次定位问题都有点手足无措的感觉,刚好利用星期天,以测试环境为模版来学习一下Linux常用的几个排查问题的命令. 也可以帮助自己在以后的工作中快速的排查线上问 ...

  3. java:线上问题排查常用手段(转)

    出处:java:线上问题排查常用手段 一.jmap找出占用内存较大的实例 先给个示例代码: import java.util.ArrayList; import java.util.List; imp ...

  4. 火山引擎MARS-APM Plus x 飞书 |降低线上OOM,提高App性能稳定性

    通过使用火山引擎MARS-APM Plus的memory graph功能,飞书研发团队有效分析定位问题线上case多达30例,线上OOM率降低到了0.8‰,降幅达到60%.大幅提升了用户体验,为飞书的 ...

  5. BTrace:线上问题排查工具

    BTrace简介 GitHub地址:BTrace 下载地址:v1.3.11.3 官方使用教程:Btrace使用教程 使用场景 BTrace 是一个事后工具,所谓事后工具就是在服务已经上线了,但是发现存 ...

  6. 记一次线上bug排查-quartz线程调度相关

    记一次线上bug排查,与各位共同探讨. 概述:使用quartz做的定时任务,正式生产环境有个任务延迟了1小时之久才触发.在这一小时里各种排查找不出问题,直到延迟时间结束了,该任务才珊珊触发.原因主要就 ...

  7. 线上问题排查神器 Arthas

    线上问题排查神器 Arthas 之前介绍过 BTrace,线上问题排查神器 BTrace 的使用,也说它是线上问题排查神器.都是神器,但今天这个也很厉害,是不是更厉害不好说,但是使用起来非常简单.如果 ...

  8. JVM 线上故障排查基本操作--CPU飙高

    JVM 线上故障排查基本操作 CPU 飚高 线上 CPU 飚高问题大家应该都遇到过,那么如何定位问题呢? 思路:首先找到 CPU 飚高的那个 Java 进程,因为你的服务器会有多个 JVM 进程.然后 ...

  9. Java架构师线上问题排查,这些命令程序员一定用得到!

    Java架构师线上问题排查,这些命令程序员一定用得到! 线上问题排查,以下场景,你遇到过吗? 一.了解机器连接数情况 问题:1.2.3.4的sshd的监听端口是22,如何统计1.2.3.4的sshd服 ...

随机推荐

  1. HDU 5739 Fantasia 双连通分量 树形DP

    题意: 给出一个无向图,每个顶点有一个权值\(w\),一个连通分量的权值为各个顶点的权值的乘积,一个图的权值为所有连通分量权值之和. 设删除顶点\(i\)后的图\(G_i\)的权值为\(z_i\),求 ...

  2. Go语言之并发编程(二)

    通道(channel) 单纯地将函数并发执行是没有意义的.函数与函数间需要交换数据才能体现并发执行函数的意义.虽然可以使用共享内存进行数据交换,但是共享内存在不同的goroutine中容易发生竞态问题 ...

  3. iOS笔记052- Quartz2D-绘图

      简介 Quartz 2D是一个二维绘图引擎,同时支持iOS和Mac系统 Quartz 2D能完成的工作       绘制图形 : 线条\三角形\矩形\圆\弧等       绘制文字       绘 ...

  4. hibernate 出错 集合

    Lazy="false"反而出错 错误信息: “System.Configuration.ConfigurationErrorsException”类型的异常在 Spring.Co ...

  5. python-day4-装饰器的使用

    摘要:某公司的基础开发平台,有大概N多个函数,boss要求小A,为每个函数添加权限验证功能,而且要求不得修改函数内部结构,让小A尝试从代码外部入手,作为新手小A来讲,这无疑是一个巨大的工作量,难道TM ...

  6. Codeforces 1062E 题解

    给出一棵有根树,1为根结点,接下来q次询问,每次给出一个[l,r]区间,现在允许删掉[l,r]区间内任何一个点,使得所有点的最近公共祖先的深度尽可能大,问删掉的点是哪个点,深度最大是多少. 做法: 线 ...

  7. 菜鸟之路——机器学习之SVM分类器学习理解以及Python实现

    SVM分类器里面的东西好多呀,碾压前两个.怪不得称之为深度学习出现之前表现最好的算法. 今天学到的也应该只是冰山一角,懂了SVM的一些原理.还得继续深入学习理解呢. 一些关键词: 超平面(hyper ...

  8. 基础概念:Oracle数据库

    基础概念:Oracle数据库.实例.用户.表空间.表之间的关系 数据库:Oracle数据库是数据的物理存储.这就包括(数据文件ORA或者DBF.控制文件.联机日志.参数文件).其实Oracle数据库的 ...

  9. algorithm 头文件

    非修改性序列操作(12个) 循环 对序列中的每个元素执行某操作 for_each() 查找 在序列中找出某个值的第一次出现的位置 find() 在序列中找出符合某谓词的第一个元素 find_if() ...

  10. jquery版右下角弹窗效果

    <!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8&quo ...