一次FGC导致CPU飙高的排查过程
今天测试团队反馈说,服务A的响应很慢,我在想,测试环境也会慢?于是我自己用postman请求了一下接口,真的很慢,竟然要2s左右,正常就50ms左右的。
于是去测试服务器看了一下,发现服务器负载很高,并且该服务A占了很高的cpu。先用top命令,看了load average,发现都到了1.5左右(双核cpu)了,并且有一个java进程(20798)占用cpu一直很高,如下图:
于是,用命令jps -l看了一下java的20798,刚好就是服务A。
究竟服务A在跑什么,毕竟是测试环境。于是使用top -Hp 20798看一下是哪个线程在跑,如下图:
发现线程20840占用cpu非常高,其他几乎都是0。通过以下命令输出该线程id(20840)的16进制:
printf "%x\n" 20840
输出如下:
线程id(20840)的16进制是5186。
然后使用以下命令打印出该线程的堆栈信息:
jstack -l 20798 | grep -A 20 5168
输入如下:
发现占用cpu的进程是jvm的GC线程,于是猜测是不是由于一直在进行FGC导致cpu飙高,于是使用以下命令看下FGC的频率和耗时:
jstat -gc 20798 1000
输出如下:
发现,果然是不断地在进行着FGC,并且每次FGC的时间一直在升高。是什么导致一直都在FGC呢?是有大对象一直在创建,回收不了?于是使用以下命令看下heap中的对象情况:
jmap -histo:live 20798 | head -20
输出如下:
发现一个业务类对象竟然有150w+个,并且占用了264M的堆大小,什么情况,并且这150w+个对象还是存活的(注意jmap使用的时候,已经带上了:live选项,只输出存活的对象),吓我一跳。于是赶紧使用以下命令打出线程堆栈来看一下:
jstack -l 20798 > jstack_tmp.txt
输出如下:
然后使用如下命令在输出的线程堆栈中根据对象类查找一下:
grep -C 30 'omments' jstack_tmp.txt
输出如下:
猜测是由于一下次从db load出了太多的CommentsEntity。
于是使用以下命令dump出heapdump出来重复确认一下:
jmap -dump:live,format=b,file=news_busy_live.hprof 20798
把heapdump文件news_busy_live.hprof下载到windows本地,使用mat工具进行分析,第一次打开发现打不开,毕竟news_busy_live.hprof有3G那么大,mat直接报OOM打不开,发现mat的配置文件MemoryAnalyzer.ini里面的配置-Xmx1024m,heap size才1G,太小了,于是改成-Xmx4096m,保存,重新打开mat,再打开news_busy_live.hprof文件即可,如下图:
发现mat已经帮我们分析出了内存泄漏的可以对象,233w+个对象(前面通过jmap命令输出的150W+个,是后面为了写文章而专门重现的操作,这里的233w+个是当时真的出问题的时候dump出来的heap dump文件),太恐怖了。
通过以下操作,查看
点击exclude all ....,因为弱引用,软引用,虚引用等都可以被GC回收的,所以exclude,输出如下:
发现一共有6个线程引用了那233w+个对象,于是去前面dump出来的线程堆栈跟踪以下这几个线程的情况,发现堆栈里面刚好这几个线程也是在处理comments相关的逻辑,这个是刚好碰巧,一般线程id都对不上的,毕竟线程处理完之后就释放了的。所以我们还是看回前面线程堆栈的信息,这里贴出根据关键字"omment"搜索出来的线程堆栈的信息,如下:
"XNIO-5 task-77" #248 prio=5 os_prio=0 tid=0x00007fc4511be800 nid=0x8f7 runnable [0x00007fc3e5af2000] java.lang.Thread.State: RUNNABLE ... at cn.xxxxxx.news.commons.redis.RedisUtil.setZSet(RedisUtil.java:1080) at cn.xxxxxx.news.service.impl.CommentsServiceV2Impl.setCommentIntoRedis(CommentsServiceV2Impl.java:1605) at cn.xxxxxx.news.service.impl.CommentsServiceV2Impl.loadCommentsFromDB(CommentsServiceV2Impl.java:386) ... at cn.xxxxxx.xxxs.controller.vxxx.xxxxController.getxxxxxx(NewsContentController.java:404) at cn.xxxxxx.xxx.controller.vxxx.xxxxxController$$FastClassBySpringCGLIB$$e7968481.invoke(<generated>) ... at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at java.lang.Thread.run(Thread.java:745) Locked ownable synchronizers: - <0x00000000f671ecd0> (a java.util.concurrent.ThreadPoolExecutor$Worker)
从上面的堆栈信息,结合前面的猜测(猜测是一次性从db load出太多的CommentsEntity),猜测应该是函数loadCommentsFromDB一次性从db load出太多CommentsEntity了。于是看了一下业务代码,发现load出来的commentsEntity会放到redis的某一个zset,于是使用redis destopmanger看一下这个zset的数据,发现这个zset有22w的数据,从中找出几条,发现对应的newsPk都是同一个,根据newsPk在db中找一下该newsPk的comments总记录,发现该newsPk的comments记录数是38w+条,那就是这个问题了,一次性从db中load了38w+的数据到内存。
一次性load那么多数据到内存,这肯定是一个慢查询,不管是db还是网络io,都肯定很慢。然后发现业务代码还会有一个for循环,把这个CommentsEntityList遍历一遍,一条一条放到redis,这也是一个非常慢的过程。
然后我去看了服务A的access log,发现在短时间内,请求了该newsPk多次数据,所以就导致了jvm的heap空间不够,然后出现不断FGC的现象,并且该newsPk的请求,由于超时,都在网关超时返回了。
为了验证这个问题,我把相关的redis缓存删除,然后调用该newsPk的接口获取数据,发现很慢,并且cpu立刻飚上去了,然后调多几次,并且不断地进行FGC,至此已经复现了该问题,和猜测的一样。等数据load到redis之后,再访问该接口,就很正常没问题。
上面发现问题的代码,找时间做一下优化才行,先重启服务A,让服务可用先。
欢迎关注微信公众号“ismallboy”,请扫码并关注以下公众号,并在公众号下面回复“FGC”,获得本文最新内容。

一次FGC导致CPU飙高的排查过程的更多相关文章
- STORM在线业务实践-集群空闲CPU飙高问题排查
源:http://daiwa.ninja/index.php/2015/07/18/storm-cpu-overload/ 2015-07-18AUTHORDAIWA STORM在线业务实践-集群空闲 ...
- 记一次yarn导致cpu飙高的异常排查经历
yarn就先不介绍了,这次排坑经历还是有收获的,从日志到堆栈信息再到源码,很有意思,下面听我说 问题描述: 集群一台NodeManager的cpu负载飙高. 进程还在但是看日志已经不再向Resourc ...
- 生产系统CPU飙高问题排查
现状 生产系统CPU占用过高,并且进行了报警 排查方法 执行top命令,查看是那个进程导致的,可以确定是pid为22168的java应用导致的 执行top -Hp命令,查看这个进程的那个线程导致cpu ...
- STORM在线业务实践-集群空闲CPU飙高问题排查(转)
最近将公司的在线业务迁移到Storm集群上,上线后遇到低峰期CPU耗费严重的情况.在解决问题的过程中深入了解了storm的内部实现原理,并且解决了一个storm0.9-0.10版本一直存在的严重bug ...
- 记一次JAVA进程导致Kubernetes节点CPU飙高的排查与解决
一.发现问题 在一次系统上线后,我们发现某几个节点在长时间运行后会出现CPU持续飙升的问题,导致的结果就是Kubernetes集群的这个节点会把所在的Pod进行驱逐(调度):如果调度到同样问题的节点上 ...
- mongoDB cpu飙高问题
问题描述: 最近几天生产环境上的mongodb一直在报警,cpu飙高,其他如内存.iops.连接数.磁盘操作等都正常.通过定位业务,发现是由于mongodb的表其中一个查询未建立索引导致,110多W的 ...
- 你要偷偷学会排查线上CPU飙高的问题,然后惊艳所有人!
GitHub 20k Star 的Java工程师成神之路,不来了解一下吗! GitHub 20k Star 的Java工程师成神之路,真的不来了解一下吗! GitHub 20k Star 的Java工 ...
- 【面试普通人VS高手系列】CPU飙高系统反应慢怎么排查?
面试过程中,场景类的问题更容易检测出一个开发人员的基本能力. 这不,一个小伙伴去阿里面试,第一面就遇到了关于"CPU飙高系统反应慢怎么排查"的问题? 对于这个问题,我们来看看普通人 ...
- 系统CPU飙高,怎么排查?
cpu是整个电脑的核心计算资源,对于一个应用进程来说,cpu的最小执行单元是线程. 导致cpu飙高的原因有几个方面: cpu上下文切换过多,对于cpu来说,同一时刻下每个cpu核心只能运行一个线程,如 ...
随机推荐
- Python爬虫(二)爬百度贴吧楼主发言
爬取电影吧一个帖子里的所有楼主发言: # python2 # -*- coding: utf-8 -*- import urllib2 import string import re class Ba ...
- 在html中使用vue组件
最近在维护公司的项目,当我拿到项目时,发现他用了vue. 但是~~仅仅是引用vue.js文件,整体的架构还是html那种,没有用webpack! 当时觉得~哇~原来还可以这样! 然后了解了业务逻辑和代 ...
- TestNG测试用例重跑详解及实践优化
测试用例运行稳定性是自动化质量的一个重要指标,在运行中需要尽可能的剔除非bug造成的测试用例执行失败,对于失败用例进行重跑是常用策略之一.一种重跑策略是所有用例运行结束后对失败用例重跑,另一种重跑策略 ...
- 从一条数据说起——InnoDB存储数据结构
本篇博客参考掘金小册--MySQL 是怎样运行的:从根儿上理解 MySQL 先给大家讲一个故事,我刚参加工作,在一个小作坊里面当[码畜](尽管现在也是),有一天老板从我背后走过,说了一句举世震惊的话: ...
- 区间dp E - Multiplication Puzzle POJ - 1651
E - Multiplication Puzzle POJ - 1651 这个题目没有特别简单,但是也没有我想象之中的那么难,这个题目时区间dp,因为我们是要对区间进行考虑的. 但是呢,这个也和动态 ...
- tp5中提示错误A non well formed numeric value encountered
问题因为自动完成时间导致的 原来我的数据库是这样的 修改成下面这样就好了
- 循环结构(for、while)
3.4用for语句实现循环结构 什么是循环结构 for语句 1.什么是循环结构? 循环结构又称为重复结构,是利用计算机运算速度快以及能进行逻辑控制的特点来重复执行某些操作.重复执行的部分称为循环体. ...
- linux下在用python向文件写入数据时'\n'不起作用
网上翻看一圈,大家都说利用write写数据换行,在linux下用'\n',windows下利用'\r\n',可是尝试了一下,'\n'在windows底下可换行,在linux底下居然不起作用,最后利用' ...
- Coursera课程笔记----P4E.Capstone----Week 2&3
Building a Search Engine(week 2&3) Search Engine Architecture Web Crawling Index Building Search ...
- Linux内核驱动学习(九)GPIO外部输入的处理
文章目录 前言 设备树 两个结构体 gpio_platform_data gpio_demo_device 两种方式 轮询 外部中断 总结 附录 前言 前面是如何操作GPIO进行输出,这里我重新实现了 ...