fgc java,频繁FGC的真凶原来是它

fgc java,频繁FGC的真凶原来是它频繁FGC的真凶原来是它上周排查了一个线上问题,主要现象是CPU占用过高,jvmold区占用过高,同时频繁fgc,我简单排查了下就草草收场了,但是过后我对这个问题又进行了复查,发现问题没有那么简单,下面跟着我一起分析一下到底是怎么回事?复查过程复查原因事后再看dump文件注意到最大的对象是一个ArrayList,里面几乎都是ElasticSearchStatusException对象可是发生…

大家好,又见面了,我是你们的朋友全栈君。

频繁FGC的真凶原来是它

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

复查过程

复查原因

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

3433b1e1133710e660352201fea35a70.png

58551d19789b286d7e6539a56cedea33.png

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

复原现场

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

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

c8a2f567c8eaa480e422a03d5ade0578.png

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

aa33ffc79658c6c4c78222ce71d4caf1.png

从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方法,可以看到通过遍历异常名字的数组验证是否在抛出的异常中存在

9f2ff918be628df7fbf773afd098c3f0.png

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

2ca578b02b19e8f893a5a922d5d13bcc.png

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

9e61512595bb5391972bbfaea6e4cad4.png

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

e0a6ad42f77b4ea34cd199d86e8e1be6.png

bug代码定位

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

e7ac8910d6f99a7c37b9ae8de1b579ba.png

其实到这里已经定位到了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包下的方法是否正确呢?可以看到这个包的方法是正确的,考虑到了这个问题

32439673fb8c1888e9e1b52e3b167aeb.png

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

09235f4863fb916adbc3e01667c8e7e7.png

class文件加载问题

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

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

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

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

3d4757dde3200055f462183491317fc6.png

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

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

如何解决

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

结尾

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

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

欢迎关注公众号 【每天晒白牙】,获取最新文章,我们一起交流,共同进步!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/150779.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(1)


相关推荐

  • 单调栈用法_栈函数

    单调栈用法_栈函数单调栈,是指栈内元素从栈底到栈顶单调递增或单调递减的栈。简单来讲,单调栈=单调+栈,它同时满足两个特性:单调性、栈。以单调递增栈来讲解单调栈原理。假设当前元素为x,(1)若x<栈顶元素,那就不满足单调递增性,这时将栈中元素y弹出,若此时条件仍然不满足,则继续弹出栈顶元素,直到满足条件,再将x入栈;(2)若x>=栈顶元素,满足单调递增性,将x入栈;如此不断重复以上步骤,直到所有满足条件的元素都入栈。以一个具体例子[3,5,2,6,8]为例:(1)首先将3入栈,此时栈中元素为[3];(2

  • C语言冒泡排序和选择排序_选择排序和冒泡排序哪个快

    C语言冒泡排序和选择排序_选择排序和冒泡排序哪个快实例1 冒泡法排序数组中有N个整数,用冒泡法将它们从小到大(或从大到小)排序。实例解析:排序是非常重要且很常用的一种操作,有冒泡排序、选择排序、插入排序、希尔排序、快速排序、堆排序等多种方法。这里我们先简单介绍前三种排序算法和代码的实现,其余算法将在后续课程《数据结构》中学习到。冒泡法排序是C语言教材中已经介绍过的排序方法,与其他排序方法比较起来,冒泡法效率是最低的,但因其算法

    2022年10月18日
  • JavaScript—-什么是纯函数

    JavaScript—-什么是纯函数定义简单来说,一个函数的返回结果只依赖于它的参数,并且在执行过程里面没有副作用,我们就把这个函数叫做纯函数。这么说肯定比较抽象,我们把它掰开来看:函数的返回结果只依赖于它的参数。函数执行过程里面没有副作用。consta=1constfoo=(b)=&gt;a+bfoo(2)//=&gt;3foo函数不是一个纯函数,因为它返回的结果依赖于外部变量…

    2022年10月23日
  • 如何将本地文件通过终端上传到linux服务器 /服务器/阿里云「建议收藏」

    如何将本地文件通过终端上传到linux服务器 /服务器/阿里云「建议收藏」scp-P端口c://xxxx.txtuser@ip:/home/root注意:-P大写-i公钥(我是将文件上传到阿里云)(1)在本地的终端下,而不是在服务器上。在本地的终端上才能将本地的文件拷入服务器。(2)scp-rlocalfile.txtusername@192.168.0.1:/home/username/其中,1)scp是命令,-r是参…

  • guns spring 单元测试[通俗易懂]

    guns spring 单元测试[通俗易懂]guns spring 单元测试

  • Nginx反向代理缓存(proxy_cache配置)

    Nginx反向代理缓存(proxy_cache配置)

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号