问题发现场景

某天突然收到线上应用的gc时间过长的告警,刚开始只有一台机器偶尔报一下,后续其他机器也纷纷告警,具体告警的阈值是应用10分钟内ygc的总时长达到了6.6s。

初步排除过程

  1. 按照gc问题常规排查流程,还是先保留现场,jmap -dump:format=b,file=temp.dump pid。
  2. 查看下gc日志,发现出问题的时候的单次ygc耗时几乎有200ms以上了。正常来说单次ygc在100ms以下,基本可以认为应用是比较健康的。所以这时候已经可以确定告警的原因就是ygc比较慢。
  3. jvisualvm打开刚刚dump的文件看下能不能发现什么东西,看了下,也看不出什么特殊的,因为本身dump的时候会触发一次full gc,dump下来的堆里面的内容只有1G左右(jvm参数堆内存配置的是4G)如下图,也没发现什么特殊的东西
  4. 然后看下ygc近期耗时的走势图,下图纵坐标每10分钟gc总耗时(单位:s),横坐标日期,可以看到在2月22号应用重启后gc总耗时降下来了,然后随着时间推移,gc变得越来越慢,并且这个变慢的过程非常缓慢,正常情况下重启一次到应用触发gc告警,需要1至2周才能出现。

进一步排查

  1. 网上搜了下有没有相关案例,相关资料也非常少,然后看到 了http://zhuanlan.51cto.com/art/201706/543485.htm 笨神的一篇文章,这篇文章简单总结起来就是使用jdk中的1.8的nashorn js引擎使用不当触发了底层JVM的一个缺陷。然后回到我这边来,发现和我这边的场景也挺类似的,应用也大量使用了nashorn 引擎来执行javascript脚本,所以我初步猜测也是nashorn引擎使用不当导致。
  2. 为了验证我以上的想法,找运维加了-XX:+PrintReferenceGC参数,经过一段时间观察,应用重启后,观察了一段时间,发现gc日志中JNI Weak Reference处理时长变得越来越长。而且占用整个ygc时长的大部分。
  3. 再回到刚刚dump的那张图里面,能看到实例数排在前面的也有nashorn引擎相关的内容,如下图,现在几乎可以断定问题出在的执行某个javascript脚本。
  4. 现在确认了出问题的大致方向。但是该应用执行的javascript脚本也有10多个,所以还没发直接定位到是哪个脚本导致的。所以接下来就是定位具体的脚本了。初步想法是直接根据上图的中的jdk.nashorn.internal.ir.IdenNode通过引用链找到可疑的js脚本对应的String,尝试了很多次发现都失败了。主要本身对jdk.nashorn包下类不是很熟悉,再加上引用链都比较长,所以找了很久都没有找到这个类和脚本的应用关系。
  5. 于是换了一种思路,内存中,脚本肯定会以String对象存在,String底层采用char[]来存储字符。所以直接找char[]实例中内容为js脚本的,但是这里又遇到一个问题,看上面的dump文件图,会发现char[]实例数当前内存有100w+,这里就抓住了部分js脚本长度比较长的一个特点。直接根据size正序排列,长度前10的字符串,就直接就找到了一个脚本,顺着引用链会轻易发现,js脚本的内容都是保存在Source$RawData对象中的,如下图
  6. 然后回到VisualVM的Classes栏目,直接搜索Source$RawData,可以看到有241个实例,如下图,这241个,然后找了出现频率比较高的几个js脚本,然后看了对应脚本的调用方式,发现其中一个脚本每次执行都是通过ScriptEngine.eval这种方式来执行,就造成了``JNIHandleBlock```,不断增长的问题,最终导致ygc时,处理JNI Weak Reference的时间越来越长。
  7. 如何解决:修改了这个脚本的调用方式。不用每次执行eval方法,换成Bindings的方式调用。
  8. 修改后,经过一周的观察。ygc时间以及区域稳定了,如下图

总结

  1. 小插曲:其实这个问题在18年10月份左右都出现了,早期也考虑彻底解决过,也探索了不少方法。比如下:

    • 最开始的时候怀疑是G1 收集器的问题,直接把G1收集器改回CMS收集器,其中调整该参数的过程中也发生了一个小问题,具体如下。

      • 从G1改到CMS改回来的参数设置堆空间大小相关的参数变成了-Xms4000m -Xmx4000m -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=70 -XX:MaxDirectMemorySize=512m -XX:+UseCMSInitiatingOccupancyOnly -XX:SurvivorRatio=8 -XX:+ExplicitGCInvokesConcurrent -XX:MetaspaceSize=512m -XX:MaxMetaspaceSize=512m -XX:-OmitStackTraceInFastThrow -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/var/www/logs/gc-%t.log -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=10m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/www/logs -Djava.io.tmpdir=/var/www/tmp -Dio.netty.availableProcessors=4 -XX:ParallelGCThreads=4 -Dpinpoint.applicationName=crawler-worker.product-bank这样,其中-Xms4000m是初始化堆大小,-Xmx4000m是最大堆大小,然后重启应用,重启后,就收到ygc频繁的告警,然后用jstat -gc pid 3000看了下,发现了奇怪的地方(如下图)年轻代总容量才300多m(S0C+S1C+EC),而年老大总容量(OC)有3700多m,这种情况就直接导致了,直接分配对象空间的eden区域很容易就占满了,而直接触发ygc,而导致这个问题的原因呢,是忘记配置-Xmn1024m参数导致,这个参数就是制定年轻代的大小,这里的大小配置成整个堆的1/4至1/2都是合理的,加上这个参数后,刚启动应用就ygc时间过长的问题就得到了解决。
    • 后面发现也没什么效果,又怀疑是堆空间年轻代的空间设置小了。之前整个堆4000M,年轻代设置的1000M。后面把年轻代的空间调整至1200M,发现也没什么效果。在这个过程中,发现也没什么效果,再加上这个过程非常缓慢,重启一次应用也能撑个1至2周,所以也拖到了现在也就是19年2月底,算是彻底解决了这个问题。
  2. 个人觉得ygc缓慢相关的问题不太好排查,相比full gc问题或者OOM的相关问题,本身ygc带给我们的东西不够多,并且dump下来的信息,也不是保证有用的,可能也是问题被掩盖后的一些无关信息。
  3. 在排查gc相关问题,个人觉得需要对整个jvm堆内存的划分,以及gc的一系列流程,有所了解和掌握,才能够快速的定位和排查问题。

参考文章

版权声明
作者:wycm
出处:https://www.cnblogs.com/w-y-c-m/p/10499505.html
您的支持是对博主最大的鼓励,感谢您的认真阅读。
本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

一次ygc越来越慢的问题排查过程的更多相关文章

  1. 解Bug之路-记一次中间件导致的慢SQL排查过程

    解Bug之路-记一次中间件导致的慢SQL排查过程 前言 最近发现线上出现一个奇葩的问题,这问题让笔者定位了好长时间,期间排查问题的过程还是挺有意思的,正好博客也好久不更新了,就以此为素材写出了本篇文章 ...

  2. 解Bug之路-记一次存储故障的排查过程

    解Bug之路-记一次存储故障的排查过程 高可用真是一丝细节都不得马虎.平时跑的好好的系统,在相应硬件出现故障时就会引发出潜在的Bug.偏偏这些故障在应用层的表现稀奇古怪,很难让人联想到是硬件出了问题, ...

  3. 一次kibana服务失败的排查过程

    公司在kubernetes集群上稳定运行数月的kibana服务于昨天下午突然无法正常提供服务,访问kibana地址后提示如下信息: 排查过程: 看到提示后,第一反应肯定是检查elasticsearch ...

  4. 记一次生产环境Nginx日志骤增的问题排查过程

    摘要:众所周知,Nginx是目前最流行的Web Server之一,也广泛应用于负载均衡.反向代理等服务,但使用过程中可能因为对Nginx工作原理.变量含义理解错误,或是参数配置不当导致Nginx工作异 ...

  5. 干货!一次kafka卡顿事故排查过程

    由于一次功能上线后,导致某数据量急剧下滑,给我们紧张的呢!排查过程也是个学习过程(这其中有大部分是领导们的功劳,不过分享给大家应该也不犯法吧,ᐓ) 1. 确认问题的真实性? 被数据部门告知,某数据量下 ...

  6. Linux(2)---记录一次线上服务 CPU 100%的排查过程

    Linux(2)---记录一次线上服务 CPU 100%的排查过程 当时产生CPU飙升接近100%的原因是因为项目中的websocket时时断开又重连导致CPU飙升接近100% .如何排查的呢 是通过 ...

  7. 神奇的Java僵尸(defunct)进程问题排查过程

    现象描述 大概1个月多以前 在启动脚本中增加了tail -f 用来启动后追踪日志判断是否启动成功 后发现无法执行shutdown.sh(卡住 利用curl) 然后无奈使用kill -9 但通过ps - ...

  8. Connection refused 排查过程

    Connection refused 排查过程 connection refused  排查  起因 今天在连接 rabbitmq 时,报 Connection refused (如下图),借此机会记 ...

  9. JDBC连接泄露问题的排查过程总结

    当前使用的Spring JDBC版本是5.0.0.RC1,HikariCP版本是3.1.0. 今天测试同学反馈在前端页面点击次数多了,就报500错误,数据显示不出来.于是我在后台服务日志中观察发现Hi ...

随机推荐

  1. 可供选择CSS框架

    在这里你有一个很酷的框架,收集创建的CSS布局. 如果你不喜欢框架,宁愿使用自己的手写代码以促进自己的发展,请跳过本篇文章. 我想有一个建设性的意见,那就是有选择的使用其优点避开其缺点. 就个人而言, ...

  2. 20155321 2016-2017-2 《Java程序设计》第六周学习总结

    20155321 2016-2017-2 <Java程序设计>第六周学习总结 教材学习内容总结 第十章 IO 流 IO 流用来处理设备之间的数据传输 Java对数据的操作是通过流的方式 J ...

  3. 【leetcode 简单】 第一百五十题 两个列表的最小索引总和

    假设Andy和Doris想在晚餐时选择一家餐厅,并且他们都有一个表示最喜爱餐厅的列表,每个餐厅的名字用字符串表示. 你需要帮助他们用最少的索引和找出他们共同喜爱的餐厅. 如果答案不止一个,则输出所有答 ...

  4. 快速修改Matlab默认启动路径(Windows/Mac)

    如何修改Matlab启动路径/Windows or Mac 控制台内输入一下两行命令,之后重启MATLAB即可   newpath = '你所要设定的路径'; userpath(newpath)   ...

  5. MUI上拉加载下拉刷新

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...

  6. mysql高可用架构 -> MHA配置VIP漂移-05

    VIP漂移的两种方式 1)通过keepalived的方式,管理虚拟IP的漂移 2)通过MHA自带脚本方式,管理虚拟IP的漂移 MHA脚本方式 虚拟ip漂移的脚本下载地址 -> wget http ...

  7. restful API 规范(转)

    1. URI URI 表示资源,资源一般对应服务器端领域模型中的实体类. URI规范 不用大写: 用中杠-不用下杠_: 参数列表要encode: URI中的名词表示资源集合,使用复数形式. 资源集合 ...

  8. Ubuntu下使用Nginx+uWSGI+Flask(初体验)

    Ubuntu 18.04,Nginx 1.14.0, uWSGI 2.0.17.1,Flask, 前言 Windows不支持uWSGI!为了上线自己的项目,只能选择Linux. 自己前面开发了一个Fl ...

  9. HTML5 localStorage、sessionStorage 作用域

    一.localStorage localStorage有效期:永不失效,除非web应用主动删除. localStorage作用域:localStorage的作用域是限定在文档源级别的.文档源通过协议. ...

  10. JQuery中DOM事件合成用法

    jQuery有两个合成事件——hover()方法和toggle()方法 类似前面讲过的ready()方法,hover()方法和toggle()方法都属于jQuery自定义的方法. hover()方法: ...