今天测试团队反馈说,服务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飙高的排查过程的更多相关文章

  1. STORM在线业务实践-集群空闲CPU飙高问题排查

    源:http://daiwa.ninja/index.php/2015/07/18/storm-cpu-overload/ 2015-07-18AUTHORDAIWA STORM在线业务实践-集群空闲 ...

  2. 记一次yarn导致cpu飙高的异常排查经历

    yarn就先不介绍了,这次排坑经历还是有收获的,从日志到堆栈信息再到源码,很有意思,下面听我说 问题描述: 集群一台NodeManager的cpu负载飙高. 进程还在但是看日志已经不再向Resourc ...

  3. 生产系统CPU飙高问题排查

    现状 生产系统CPU占用过高,并且进行了报警 排查方法 执行top命令,查看是那个进程导致的,可以确定是pid为22168的java应用导致的 执行top -Hp命令,查看这个进程的那个线程导致cpu ...

  4. STORM在线业务实践-集群空闲CPU飙高问题排查(转)

    最近将公司的在线业务迁移到Storm集群上,上线后遇到低峰期CPU耗费严重的情况.在解决问题的过程中深入了解了storm的内部实现原理,并且解决了一个storm0.9-0.10版本一直存在的严重bug ...

  5. 记一次JAVA进程导致Kubernetes节点CPU飙高的排查与解决

    一.发现问题 在一次系统上线后,我们发现某几个节点在长时间运行后会出现CPU持续飙升的问题,导致的结果就是Kubernetes集群的这个节点会把所在的Pod进行驱逐(调度):如果调度到同样问题的节点上 ...

  6. mongoDB cpu飙高问题

    问题描述: 最近几天生产环境上的mongodb一直在报警,cpu飙高,其他如内存.iops.连接数.磁盘操作等都正常.通过定位业务,发现是由于mongodb的表其中一个查询未建立索引导致,110多W的 ...

  7. 你要偷偷学会排查线上CPU飙高的问题,然后惊艳所有人!

    GitHub 20k Star 的Java工程师成神之路,不来了解一下吗! GitHub 20k Star 的Java工程师成神之路,真的不来了解一下吗! GitHub 20k Star 的Java工 ...

  8. 【面试普通人VS高手系列】CPU飙高系统反应慢怎么排查?

    面试过程中,场景类的问题更容易检测出一个开发人员的基本能力. 这不,一个小伙伴去阿里面试,第一面就遇到了关于"CPU飙高系统反应慢怎么排查"的问题? 对于这个问题,我们来看看普通人 ...

  9. 系统CPU飙高,怎么排查?

    cpu是整个电脑的核心计算资源,对于一个应用进程来说,cpu的最小执行单元是线程. 导致cpu飙高的原因有几个方面: cpu上下文切换过多,对于cpu来说,同一时刻下每个cpu核心只能运行一个线程,如 ...

随机推荐

  1. OSG程序设计之osg::NodeVisitor

    本文所有内容来自<OpenSceneGraph三维渲染引擎设计与实践>一书. 本文主要讨论的是OSG中节点的访问. 对于节点的访问是从节点接收一个访问器开始的,用户执行某个节点的accep ...

  2. 戴尔服务器ipmi报错

    戴尔服务器ipmi配置完成,用浏览器打开报错 查看器已终止,网络已中断 原因:这个问题是java报错,用火狐打开报错 解决方法: 用IE打开就没问题,IE要用较高版本的,楼主的是win10-IE11

  3. Linux的vi和vim编辑器

    Linux中分为:一般模式,插入模式和底行模式 一般模式(通过按iaoIAO键)-->插入模式 插入模式(按Esc键)--> 一般模式 一般模式(通过按:键)-->底行模式 底行模式 ...

  4. dbcp数据源连接池

    一.数据源连接池 我们之前利用jdbc连接数据库,每次都要创建连接对象,销毁连接对象,如果并发访问量比较大,这样肯定比较辣 浪费数据库的效率,我们可以像之前mybatis中缓存查询到的数据一样,可以把 ...

  5. oracle如何实现去重和分页

    一:oracle实现去重: user数据表: 分两步:1.查询重复数据  2.删除重复数据 1.查询重复数据:在oracle中实现查询重复数据,可以借助于rowid这个伪列.oracle中每个表物理上 ...

  6. 利用css+js制作下拉列表

    利用文本框来制作,可以不影响给后台传数据.<!DOCTYPE html> <html> <head> <style> *{;;;} body{font- ...

  7. 【STM32系列汇总】小白博主的STM32实战快速进阶之路(持续更新)

    我把之前在学习和工作中使用STM32进行嵌入式开发的经验和教程等相关整理到这里,方便查阅学习,如果能帮助到您,请帮忙点个赞: 本文的宗旨 STM32 只是一个硬件平台,同样地他可以换成MSP430,N ...

  8. Java -> 构造器(构造方法)

    构造方法 我们对封装已经有了基本的了解,接下来我们来看一个新的问题,依然以Person为例,由于Person中的属性都被private了,外界无法直接访问属性,必须对外提供相应的set和get方法.当 ...

  9. STM32学习笔记——USART

    STM32的USART组件支持异步.同步.单线半双工.多处理器.IrDA.LIN.SmartCard等模式,本文介绍的是异步即UART模式. 总线通信有三种模型:轮询.中断和DMA.DMA对我来说是陌 ...

  10. PAT 1006 Sign In and Sign Out (25分) 字符串比较

    题目 At the beginning of every day, the first person who signs in the computer room will unlock the do ...