【转】又一次线上 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服 ...
随机推荐
- time模块和datetime模块详解
一.time模块 time模块中时间表现的格式主要有三种: a.timestamp时间戳,时间戳表示的是从1970年1月1日00:00:00开始按秒计算的偏移量 b.struct_time时间元组,共 ...
- luogu2120 [ZJOI2007]仓库建设
大米饼写的太棒辣qwqqwq #include <iostream> #include <cstdio> using namespace std; typedef long l ...
- laravel5.2总结--composer使用和自动加载介绍
首先看下phpcomposer官方的定义,composer是 PHP 用来管理依赖(dependency)关系的工具.你可以在自己的项目中声明所依赖的外部工具库(libraries),Composer ...
- Java之OutOfMemoryError简单分析
Java之OutOfMemoryError简单分析 最近编码遇到了Java内存溢出的问题,所以就想顺便总结一下几种导致Java内存溢出的栗子,以及碰到Java内存溢出要如何去解决. Java堆溢出 J ...
- hibernate 出错 集合
Lazy="false"反而出错 错误信息: “System.Configuration.ConfigurationErrorsException”类型的异常在 Spring.Co ...
- Python+Selenium练习篇之6-利用class name定位元素
有时候,我们在用firepath(不会的请点这里)查看元素的XPath信息,发现没有可以用来定位的id信息,这个时候我们就需要考虑用其他的可用的来定位元素.本文介绍如何通过元素节点中class nam ...
- Python数据分析基础——读写CSV文件2
2.2筛选特定的行: 行中的值满足某个条件 行中的值属于某个集合 行中的值匹配于某个模式(即:正则表达式) 2.2.1:行中的值满足于某个条件: 基础python版: #!/usr/bin/env p ...
- 深入学习之mysql(五)连接查询
深入学习Mysql(五)连接查询 1.准备数据库: CREATE DATABASE IF NOT EXISTS `db_book2` DEFAULT CHARACTER SET UTF8; USE ` ...
- python正则 转
python中的正则表达式(re模块) 一.简介 正则表达式本身是一种小型的.高度专业化的编程语言,而在python中,通过内嵌集成re模块,程序媛们可以直接调用来实现正则匹配.正则表达式模式被编 ...
- Incorrect column count: expected 1, actual 6
JdbcTemplate使用时出现了一些问题: 解决办法: