最近笔者有点忙,这次OOM事故发生过去两周前,记得笔者那天正带着家人在外地玩,正中午跟友人吃饭的时候,钉钉连续告警爆表,接着就是钉钉电话(显示广东抬头)一看就知道BBQ了,又一次故障发生了,今天把那次故障复盘一下,做个总结,也给小伙伴分享一下 我是怎么从接到告警开始,怎么一步一步分析故障,然后定位到问题,最后完美解决,成功上线解决问题的。

上述告警内容,由于笔者所在服务是用CMS垃圾回收器,当其GC次数太频繁,达到公司监控平台设置的阈值时,就会通过钉钉通知告知开发者,发送到对应的控制台上。这个异常先从字面意义上来说倒也比较明显,如果老年代里的对象太多,无法提供空间容纳年轻代传递过来的对象的时候,就会触发FULL GC。

这里我们先简单分析一下,对象什么情况下会进入老年代,以及老年代又是在什么情况下会触发FULL GC?只有先知道了原理性东西,你才能带着思路去分析,真实线上场景属于对应哪种情况

首先科普一下对象什么情况下会进入老年代?

1)躲过15次GC之后进入老年代

public class Kafka{
//只要Kafka这个类存在,r这个静态变量就会一直存在
private static ReplicManager r=new ReplicManager();
}
像上面这块代码,成员变量是GCROOT引用,所以一直不会回收不掉;这个对象每次从Eden躲过一次到Survivor区域中,它的年龄就增长1岁,当年龄增加到15岁时候,就会转移到老年代里。
 
2)动态对象年龄判断
意思是如果Survivor空间中相同年龄的所有对象大小总和大于Survivor空间的一半,年龄大于等于该年龄的对象会直接进入老年代
 
3)大对象直接进入老年代
 
4)空间担保策略
在发生MinorGc之前,JVM会检查老年代的最大连续可用空间是否大于新生代所有对象总空间,如果不成立,那么JVM会查看一个参数值查看是否允许担保,如果之前配置了允许,那么会检查老年代最大可用空间是否大于历次晋升到老年代对象的平均大小,如果大于将尝试进行一次Minor GC;如果小于或者参数设置不允许冒险,那么就会进行一次FULL GC。
 
那老年代又是在什么情况下会触发FULL GC?
1.也是上面第四种情况,就不写了
2.yongGC之后如果满足上述分析的[#首先科普一下对象什么情况下会进入老年代?]其中一种情况,那么进入老年代,但这个时候如果老年代空间不足,就会触发FULLGC
3.如果老年代内存使用率超过92%其实会触发fullgc的
 
好了先科普一下相关知识点,利于后续的分析做铺垫。下面开始逐步分析具体具体原因,到底是什么大对象充满了老年代内存区域。
 
首先一碰到特别是线上这种重大事故,第一思路是保留线程,然后快速止血。这也是笔者所在公司对于开发的其中一条军规(估计之前出现过太多的这种事故,形成规范了)。
那我也按照这种思想,通过日志分析,马上知道这个是有同时在批量导数据,导致入口流量很大,先联系同时快速止血,暂停导入操作。果然没多久 就不报警了,告警恢复通知一个接一个过来。
 
接下来我们开始分析到底是什么对象快速把老年代给填满了,相应入口在哪里。
先看业务监控大图:

现象是从下午4点开始内存有一波快速增长。
通过阿里的Arthas分析工具,通过命令dashboard查看当下系统的实时信息。
(下面这张图已经是止血之后文档的图了,但老年代还是填充了不少对象的)

线上由于比较麻烦dump线程。而且现场已经过去了,所以我还是自己写了一段压测代码(类似Jmeter效果),来压测相应的总入口,看看具体是哪个对象占了大内存

很明显是有一个nashorn相关对象占据了比较大的占比。那这个对象其实对应笔者的程序是

ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("nashorn");
Compilable compEngine = (Compilable) engine;
try {
CompiledScript compile = compEngine.compile(script);
}catch(Exception e){

}

简单来说,Nashorn的编译入口可以从 Context.compileScript() 开始看:[ JavaScript源码 ] -> ( 语法分析器 Parser ) -> [ 抽象语法树(AST) ir ] -> ( 编译优化 Compiler ) -> [ 优化后的AST + Java Class文件(包含Java字节码) ] -> JVM加载和执行生成的字节码 -> [ 运行结果 ]

此过程是十分耗时的,每次执行eval 去运行js ,都需要编译成字节码、然后加载执行。同时会将编译过的字节码缓存起来,以便后续使用,因此加载的类会长时间存活,占用很大的内存空间。

所以笔者尝试将CompiledScript这一对象第一次编译完后,本地缓存起来用

private static Map<Long, CompiledScript> scriptMap = new ConcurrentHashMap<>();
缓存起来,下一次如果已经存在,就直接拿来用。
重新压测后效果还是明显的

总结
线上场景 特别对于一些新的框架或技术 如果你的流量很大,笔者那时参与了这个项目,工期特别短,功能又特别多,想着先上线,下一步再做压测,想不到等不到下一步问题就暴露出来了

 
 

公司内部一次关于OOM故障复盘分享的更多相关文章

  1. 公司内部一次关于kafka消息队列消费积压故障复盘分享

    背景现象 1.20晚上8点业务线开始切换LBS相关流量,在之后的1个小时时间内,积压量呈上升趋势,一路到达50W左右,第二天的图没贴出具体是50W数字,以下是第一天晚上的贴图部分. 现象一: 现象二: ...

  2. HBase 查询导致RegionServer OOM故障复盘

    背景:我司作为某运营商公司的技术咨询公司,发现有第三方开发公司在使用HBase 1.1.2 (HDP 2.4.2.258版本)一段时间使用正常后,从某一天开始报OOM,从而导致RegionServer ...

  3. Rafy 领域实体框架 - 公司内部培训视频

    本月给公司内部一个项目做架构重构,其中使用到了 Rafy 框架.所以我培训了 Rafy 领域实体框架的使用方法,过程中录制了视频,方便其他同事查看.现在把视频放到园里来分享下,有兴趣的朋友可以看看,有 ...

  4. 故障复盘究竟怎么做?美图SRE结合10年经验做了三大总结(附模板)

    美图崇尚的故障文化是 "拥抱故障,卓越运维",倡导的基准是 No-Blame, 即「不指责,重改进」.今年 9 月 TakinTalks 社区曾经分享过美图的三段式故障治理方法(美 ...

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

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

  6. 公司内部Samba 服务器架设

    1.需求 在公司内部打造一个文件管理系统,其作用域仅仅在公司内部,支持在线对文件的修改和保存操作等,同时也要注意权限问题. 2.策划 目前设立四个群组:运维.开发 .测试和普通,当然所对应的对文件的访 ...

  7. 关于运维之故障复盘篇-Case Study

    关于故障的事后复盘,英文名 Case Study是非常有必要做的,当然是根据故障的级别,不可能做到每个故障都Case Study,除非人员和时间充足: 文档能力也是能力的一种,一般工程师的文档能力比较 ...

  8. 生产环境想要对某个Pod排错、数据恢复、故障复盘有什么办法?

    生产环境想要对某个Pod排错.数据恢复.故障复盘有什么办法? k8s考点灵魂拷问9连击之5 考点之简单描述一下k8s副本集ReplicaSet有什么作用? 考点之为什么ReplicaSet将取代Rep ...

  9. 搭建公司内部的NuGet Server

    随着公司业务慢慢的拓展,项目便会越来越来多,很多项目会依赖其他项目DLL,比如一些底层的技术框架DLL引用,还有各业务系统的也有可能会有引用的可能. 项目多,交叉引用多,如果要是有一个DLL更新,那就 ...

随机推荐

  1. 如何美化 Matplotlib 3D坐标系

    前言 ~mpl_toolkits.mplot3d 生成的3D坐标系背景色是灰色的,刻度线也向内延伸了,如果搭配上其他白色背景的 2D 图,看起来很奇怪,比如下面这张图: 网上有一些办法可以将3D坐标区 ...

  2. libcurl库openssl编译

    openssl编译 win32: perl Configure  no-shared VC-WIN32 --prefix=E:/lib/openssl-1.1.1 linux: ./config -f ...

  3. Web标准和骨架

    Web 标准的好处 1.让Web的发展前景更广阔 2.内容能被更广泛的设备访问 3.更容易被搜寻引擎搜索 4.降低网站流量费用 5.使网站更易于维护 6.提高页面浏览速度 Web 标准构成 Web标准 ...

  4. 【转】Mysql相关子查询&&MySQL获取分组后的TOP N记录

    https://www.cnblogs.com/Yiran583/p/6743870.html select * from test1 a where 2 > (select count(*) ...

  5. 位运算符、|和||、&和&&的区别

    一.位运算符操作的都是整数类型 1.<<:左移,在一定范围内向左移动n位,相当于乘以2的n次幂 左移不管是正数还是负数,都是在后面添0: 2.>>:右移,在一定范围内向右移动n ...

  6. CSS快速入门(四)

    目录 CSS快速入门(四) 浮动 float属性 clear属性 浮动解决的问题及其影响 解决父标签塌陷的方法 浮动案例 定位 什么是脱离文档流 定位的两种方法 position定位 static定位 ...

  7. Java--面向对象设计

    [转载自本科老师上课课件] 问题一: 在一个软件的功能模块中,需要一种图像处理的功能.该图像处理的策略(如何处理)与图像的内容是相关的.如:卫星的运行图片,使用策略A处理方式,如果是卫星内云图片,则需 ...

  8. HashMap自动扩容机制源码详解

    一.简介 HashMap的源码我们之前解读过,数组加链表,链表过长时裂变为红黑树.自动扩容机制没细说,今天详细看一下 往期回顾: Java1.7的HashMap源码分析-面试必备技能 Java1.8的 ...

  9. HTML笔记整理--上节

    一.认识WEB 「网页」主要是由文字.图像和超链接等元素构成,当然除了这些元素,网页中还可以包括音频.视频以及Flash等. 「浏览器」是网页显示.运行的平台. 「浏览器内核」(排版引擎.解释引擎.渲 ...

  10. Solution -「Gym 102956B」Beautiful Sequence Unraveling

    \(\mathcal{Description}\)   Link.   求长度为 \(n\),值域为 \([1,m]\) 的整数序列 \(\lang a_n\rang\) 的个数,满足 \(\not\ ...