频繁FGC的真凶原来是它

CodeAdventurerX
• 阅读 1742

频繁FGC的真凶原来是它

上周排查了一个线上问题,主要现象是CPU占用过高,jvm old区占用过高,同时频繁fgc,我简单排查了下就草草收场了,但是过后我对这个问题又进行了复查,发现问题没有那么简单,下面跟着我一起分析一下到底是怎么回事?

一定要先读完上篇文章cpu使用率过高和jvm old占用过高排查过程

复查过程

复查原因

事后再看dump文件注意到最大的对象是一个ArrayList,里面几乎都是ElasticSearchStatusException对象

频繁FGC的真凶原来是它

频繁FGC的真凶原来是它

可是发生这个异常的操作上次已经被我定位到了,数据漏斗只有产品、运营等内部人员使用,通过使用频率推测,不应该有那么多对象。我猜想是不是代码中存在死循环,但没有找到。没办法只能在测试环境进行场景复现了。

复原现场

通过上次排查到是es查询了不存在的索引导致异常,所以就把查询es的索引写死一个不存在的,最初尝试写个单测,但一直不能复现问题,所以只好部署到测试环境,在本地通过远程debug 调试程序

远程debug到断点时,发现源码对不上

频繁FGC的真凶原来是它

然后发现有可选择的源码,这里是关键

频繁FGC的真凶原来是它

从org.apache.commons.lang:2.5jar包切换到springsource.org.apache.commons.lang:2.1.0包后,竟然能够和测试环境对得上,可是代码中明明引用的commons.lang:2.5的包,这里说明在项目中类加载的时候,ExceptionUtils这个class文件并不是从commons.lang中加载的,而是从springsource包中加载的 关于类文件加载的问题我们先放到后面,先找代码的问题

看到这里,眼尖的朋友应该已经发现了bug的所在,那么恭喜你。如果没发现的朋友,不要着急,跟我一步一步来。我们继续debug跟进代码的 getCause方法,可以看到通过遍历异常名字的数组验证是否在抛出的异常中存在

频繁FGC的真凶原来是它

这些异常方法中的getRootCause方法,存在ElasticSearchStatusException的父类ElasticsearchException中

频繁FGC的真凶原来是它

我们看下这个方法,主要找最根本的异常原因,有则返回,没有就返回当前的异常

频繁FGC的真凶原来是它

继续跟代码,cause不为null,返回这个异常

频繁FGC的真凶原来是它

bug代码定位

这个getThrowables方法,里面有个while循环,判断条件只进行了非空判断,不为null就添加到list中,注意观察我截图的时刻,list的大小 8万多,其实远远不止会看开头dump文件的大对象,是一个ArrayList,里面有大量的ElasticSearchStatusException对象

频繁FGC的真凶原来是它

其实到这里已经定位到了FGC的真凶,判断条件没有排除返回的异常是已经添加到list中的异常,所以会一直循环添加,造成堆内存占用满了,FGC回收不掉这些对象,因为ArrayList一直持有他们的引用

正确代码应该如下面这样,所以开源工具库也是会有bug的,用的时候多加注意

public static Throwable[] getThrowables(Throwable throwable) {
        List list = new ArrayList();
        // 这里的判断条件应该加上 list.contains(throwable) == false
        while (throwable != null && list.contains(throwable) == false) {
            list.add(throwable);
            throwable = ExceptionUtils.getCause(throwable);
        }
        return (Throwable[]) list.toArray(new Throwable[list.size()]);
    }

本来我们就没想用springsource的方法,只是类加载的时候加载错了,那看下commons.lang包下的方法是否正确呢?可以看到这个包的方法是正确的,考虑到了这个问题

频繁FGC的真凶原来是它

springsource的commons.lang包在2.2版本已经修复了这个问题 jar包最好引用最新的

频繁FGC的真凶原来是它

class文件加载问题

上面我们留了一个jvm加载class文件的问题,我们知道jvm加载class的时候,如果存在包名和类名完全一样,先加载一个后,另外的就不会再被加载了。

经查看两个ExceptionUtils确实包名类名完全一致

频繁FGC的真凶原来是它
其实通过前面的debug和代码分析,已经能确定项目加载的ExceptionUtils.class文件来自springsource包,但还是想通过一定手段验证一下。

其实jvm提供类类似的功能参数,修改项目启动脚本,添加jvm参数 -XX:+TraceClassLoading 然后重启项目并继让异常重现【这里要让触发异常重现,是因为是运行时异常】,并查看日志,查看ExceptionUtils.class的加载信息
频繁FGC的真凶原来是它

可以看到确实和我们推测的一样

其实这里还可以深入研究jvm的类加载机制,类加载器加载顺序,双亲委派模型等

如何解决

通过 mvn dependency:tree 查看jar包依赖情况,排除掉不用的jar包

结尾

到这里这个问题的排查应该告一段落了,从排查过程中学到了不少,场景复现,梳理思路,用文章分享出来虽说码字不易很耗时,但这是对自己的一种总结,里面还是有很多乐趣与收获的。

后续会继续分享一些和广告系统相关的文章,敬请期待!

欢迎关注公众号 【每天晒白牙】,获取最新文章,我们一起交流,共同进步!
点赞
收藏
评论区
推荐文章
捉虫大师 捉虫大师
3年前
dubbo 配置 loadbalance 不生效?撸一把源码
背景很久之前我给业务方写了一个dubboloadbalance的扩展(为了叙述方便,这个loadbalance扩展就叫它XLB吧),这两天业务方反馈说XLB不生效了我心想,不可能啊,都用了大半年了排查于是我登上不生效的consumer机器进行排查,还好我留了一手,当XLB加载时,会打印一行日志看了下这个服务,并没有打印日志,说明
Wesley13 Wesley13
3年前
Apache不能启动解决办法
Apache不能启动解决办法这是我这两天频繁遇到的问题。Apache服务器还真是问题少年!任何点改动都可能导致它无法使用。原因一:80端口占用例如IIS,另外就是迅雷。我的apache服务器就是被迅雷害得无法启用!原因二:软件冲突装了某些软件会使apache无法启动如Dr.com你打开网络连接TcpIp属性高级WINS标签把net
Stella981 Stella981
3年前
FastDFS合并存储的一个深层次bug排查
FastDFSV3引入合并存储(trunkfile)特性后,有用户反馈上传文件提示trunk空间被占用的问题。我在测试环境中经过一通测试,在极其偶然的情况下也能重现这个问题。然后就开始排查这个问题。  FastDFS一个group(存储分组)内有一台storageserver被选举为trunkserver,用于管理和分配该组的trunk可用
Stella981 Stella981
3年前
Arthas排查Kubernetes中的应用频繁挂掉重启问题
前言其实最终定位到的问题还是蛮好解决的,但是因为应用在Kubernetes容器中的特殊性,导致在使用Arthas过程中出现了各种问题,所以单独成文和大家分享下。照例先讲下问题发生的背景,一个很老的web系统部署在tomcat容器里。近期打成了镜像丢到了Kubernetes环境中运行,总是各种挂,在Kubernetes层面定位了很久没找到具体问题,但
Wesley13 Wesley13
3年前
JAVA 线上故障排查
线上故障主要会包括CPU、磁盘、内存以及网络问题,而大多数故障可能会包含不止一个层面的问题,所以进行排查时候尽量四个方面依次排查一遍。同时例如jstack、jmap等工具也是不囿于一个方面的问题的,基本上出问题就是df、free、top三连,然后依次jstack、jmap伺候,具体问题具体分析即可。CPU一般来讲我们首先会排查
Stella981 Stella981
3年前
Executors使用不当引起的内存泄漏
线上服务内存溢出这周刚上班突然有一个项目内存溢出了,排查了半天终于找到问题所在,在此记录下,防止后面再次出现类似的情况。先简单说下当出现内存溢出之后,我是如何排查的,首先通过jstack打印出堆栈信息,然后通过分析工具对这些文件进行分析,根据分析结果我们就可以知道大概是由于什么问题引起的。关于jstack如何使用,大家可以先看看这篇文章
Wesley13 Wesley13
3年前
CPU占用过高排查实战 原来这么简单
!(https://oscimg.oschina.net/oscnet/upecde49f072daad4ccf4988b4453e55c28ab.png)代码介绍:!(https://oscimg.oschina.net/oscnet/up28784447fefd4f9275af310a958c2d92057.png)jdk提供的工
咕咕鸡 咕咕鸡
2年前
记一次线上FGC问题排查
本文记录一次线上GC问题的排查过程与思路,希望对各位读者有所帮助。过程中也走了一些弯路,现在有时间沉淀下来思考并总结出来分享给大家,希望对大家今后排查线上GC问题有帮助。
性能测试监控指标及分析调优 | 京东云技术团队
一、哪些因素会成为系统的瓶颈?1、CPU,如果存在大量的计算,他们会长时间不间断的占用CPU资源,导致其他资源无法争夺到CPU而响应缓慢,从而带来系统性能问题,例如频繁的FullGC,以及多线程造成的上下文频繁的切换,都会导致CPU繁忙,一般情况下CPU使
当小白遇到FullGC | 京东云技术团队
本文记录了一次排查FullGC导致的TP99过高过程,介绍了一些排查时思路,线索以及工具的使用,希望能够帮助一些新手在排查问题没有很好的思路时,提供一些思路,让小白也能轻松解决FullGC问题
记一次生产慢sql索引优化及思考 | 京东云技术团队
一问题重现夜黑风高的某一晚,突然收到一条运营后台数据库慢sql的报警,耗时竟然达到了60s。看了一下,还好不是很频繁,内心会更加从容排查问题,应该是特定条件下没有走到索引导致,如果频繁出现慢查询,可能会将数据库连接池打满,导致数据库不可用,从而导致应用不可
CodeAdventurerX
CodeAdventurerX
Lv1
至少以后跟等风来随雨过也陪着我
文章
8
粉丝
0
获赞
0