如何排查java应用中CPU使用率高或内存占用高的问题
如何排查java应用中CPU使用率高或内存占用高的问题?这类问题的排查步骤基本通用的。现在通过一个具体的例子来说明。
问题描述
最近有个线上项目每天0点过后CPU使用率会上升至200%到300%。
排查过程
本节内容是对排查过程的复盘,过程记录会比较详细。如果想知道具体的命令操作,可以直接看总结部分内容。
1)当CPU再次暴涨的时候,首先我们可以通过top -c
查看CPU使用率高的进程的PID。
2)然后使用top -p PID -H
查看CPU使用率高的线程信息。如果CPU使用率高的线程是比较固定的,那么我们记下对应线程的PID。 执行top -p 14639 -H
得出下图结果:
记下4个线程的PID: 14643、14644、14641、14642
3)接下来通过jstack PID > xxx.log
输出java应用当前堆栈信息到文件。
4)第2步中,我们记下了CPU使用率高的线程PID,现在将4个线程的PID转成16进制: 3933、3934、3931、3932。接着在jstack输出的堆栈文件里,搜索nid等于3933、3934、3931、3932的线程信息。如下图:
从图中可以看出,对应的是GC线程。GC消耗大,那就有可能是由于内存不足,频繁执行Full GC导致的。
再使用jstat -gc PID
查看jvm的GC情况,连续执行4次jstat -gc 14639
命令,发现FGC的数值变化比较快。这就说明Full GC确实执行很频繁。如下图:
5)从第1步的截图中,可以看到CPU高的时候整个项目的内存占用1.3G左右。既然是内存问题,那么就需要使用jmap -histo:live PID > xxx.log
分析下jvm内存存活对象的统计情况。如下图:
从图中可以看出,byte对象([B)内存占用特别高,而且出现了一个具体的类:ByteArrayRow。这是一个jdbc做查询时候封装数据用的一个类,这个类里包含有byte数组。通过这个统计结果初步怀疑是做数据库查询时候,查询了太多内容到了内存,导致了内存不足。由于统计中没有出现具体的业务类,所以就以为只是请求量比较大,导致的内存消耗过大。当时暂时将jvm的堆内存增大到2G。
6)应用jvm堆内存调大之后,到了0点还是出现了CPU高涨的问题。
内存占用了2G多,按照目前项目的请求量来说,2G内存不可能被占满了,所以说明并不是请求量大导致的结果,而是由于某块代码查询数据量过大导致的问题。
7)再次运行jmap -histo:live PID > xxx.log
将内存对象统计情况输出到文件。结果如下图:
这次的输出结果出现了业务类MiniProgram_User_Info,那就可以针对这个业务类去排查异常代码的位置了。不过,除非比较清楚这个类具体使用的地方,否则即使出现了具体的类名还是比较难定位异常代码的位置。
这时候,我们可以使用jmap -dump:live,format=b,file=xxx.hprof PID
命令来输出内存对象的明细,来定位具体方法位置。这个命令是将内存里的所有信息都输出出来,输出的文件大小和内存大小基本一致。而且这个命令会导致应用暂时挂起,所以谨慎使用。
8)这次将内存明细输出之后,dump文件大小为2G。用jdk自带的jhat命令可以分析。之前分析其他dump文件用jhat还是比较方便的。不过,分析这次的dump文件,给了10G运行内存给jhat命令才勉强打开了文件:jhat -J-mx10G -port 7170
。而且内存对象比较多,查找问题不方便。最后找到了一款神器: jprofiler。用jprofier分析dump文件需要的运行内存比较少,而且问题定位很方便。很快就定位出了内存中的大对象,占用了1G多内存的对象:
大对象对应的线程堆栈:
如上图,至此问题已经定位完成了。最后排查代码,最终发现凌晨时候,会将数据库里100多万条数据查询出来。内存不足导致频繁GC,结果就是CPU使用率暴涨。
总结
一、在排查问题的过程中针对CPU的问题,使用以下命令组合来排查问题
1、查看问题进程,得到进程PID:
top -c
2、查看进程里的线程明细,并手动记下CPU异常的线程PID:
top -p PID -H
3、使用jdk提供jstack命令打印出项目堆栈:
jstack pid > xxx.log
线程PID转成16进制,与堆栈中的nid对应,定位问题代码位置。
二、针对内存问题,使用以下命令组合来排查问题:
1、查看内存中的存活对象统计,找出业务相关的类名:
jmap -histo:live PID > xxx.log
2、通过简单的统计还是没法定位问题的话,就输出内存明细来分析。这个命令会将内存里的所有信息都输出,输出的文件大小和内存大小基本一致。而且会导致应用暂时挂起,所以谨慎使用。
jmap -dump:live,format=b,file=xxx.hprof PID
3、 最后对dump出来的文件进行分析。文件大小不是很大的话,使用jdk自带的jhat命令即可:
jhat -J-mx2G -port 7170
4、dump文件太大的话,可以使用jprofiler工具来分析。jprofiler工具的使用,这里不做详细介绍,有兴趣可以搜索一下。
三、需要分析GC情况,可以使用以下命令:
jstat -gc PID
这里简单介绍一下java8里面这个命令得出的列表各个列的含义:
S0C:第一个幸存区的大小
S1C:第二个幸存区的大小
S0U:第一个幸存区的使用大小
S1U:第二个幸存区的使用大小
EC:伊甸园区的大小
EU:伊甸园区的使用大小
OC:老年代大小
OU:老年代使用大小
MC:方法区大小
MU:方法区使用大小
CCSC:压缩类空间大小
CCSU:压缩类空间使用大小
YGC:年轻代垃圾回收次数
YGCT:年轻代垃圾回收消耗时间
FGC:老年代垃圾回收次数
FGCT:老年代垃圾回收消耗时间
GCT:垃圾回收消耗总时间
一般会比较关注YGC和FGC的次数。
内容补充
1、jstack输出的堆栈文件可以上传到下面这个网站,这个网站可以对堆栈内容进行统计汇总,方便我们做分析:http://fastthread.io/index.jsp
2、排查过程小节中的第5步,jmap命令执行完后没有输出业务类,而第7步在却有。这个是因为第5步操作的时候只有1G多的内存,代码还没执行到业务对象的封装,内存就不够了,后续的代码无法被执行到。第7步操作的时候内存调整到2G,所以有部分业务对象已经被创建了。
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
从架构演进的角度聊聊Spring Cloud都做了些什么?
传统架构发展史 单体架构 单体架构在小微企业比较常见,典型代表就是一个应用、一个数据库、一个web容器就可以跑起来,比如我们开发的开源软件云收藏,就是标准的单体架构。 在两种情况下可能会选择单体架构:一是在企业发展的初期,为了保证快速上线,采用此种方案较为简单灵活;二是传统企业中垂直度较高,访问压力较小的业务。在这种模式下对技术要求较低,方便各层次开发人员接手,也能满足客户需求。 下面是单体架构的架构图: 在单体架构中,技术选型非常灵活,优先满足快速上线的要求,也便于快速跟进市场。 垂直架构 在单体架构发展一段时间后,公司的业务模式得到了认可,交易量也慢慢的大起来,这时候有些企业为了应对更大的流量,就会对原有的业务进行拆分,比如说:后台系统、前端系统、交易系统等。 在这一阶段往往会将系统分为不同的层级,每个层级有对应的职责,UI层负责和用户进行交互、业务逻辑层负责具体的业务功能、数据库层负责和上层进行数据交换和存储。 下面是垂直架构的架构图: 在这个阶段SSH(struts+spring+hibernate)是项目的关键技术,Struts负责web层逻辑控制、Spring负责业务层管理...
- 下一篇
完全跨域的单点登录(SSO)解决方案源码解析
本文介绍的是一种PHP的开源SSO解决方案,可完全跨域,实现较简洁,源码地址:https://github.com/legalthings/sso 实现原理 一共分为3个角色: Client- 用户的浏览器 Broker - 用户访问的网站 Server - 保存用户信息和凭据的地方 每个Broker有一个ID和密码,Broker和Server事先已知道。 当Client第一次访问Broker时,它会创建一个随机令牌,该令牌存储在cookie中。然后Broker将Client重定向到Server,传递Broker的ID和令牌。Server使用Broker的ID、密码和令牌创建哈希,此哈希作为Key键保存当前用户会话的ID。之后Server会将Client重定向回Broker。 Broker可以使用令牌(来自cookie)、自己的ID和密码创建相同的哈希。在执行请求时包含此哈希。 Server收到请求会提取哈希,然后根据哈希获取之前保存的用户会话ID,然后将其设置成当前会话ID。因此,Broker和Client使用相同的会话。当另一个Broker加入时,它也将使用相同的会话。它们可以共享...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- Linux系统CentOS6、CentOS7手动修改IP地址
- Docker安装Oracle12C,快速搭建Oracle学习环境
- CentOS8安装MyCat,轻松搞定数据库的读写分离、垂直分库、水平分库
- CentOS6,CentOS7官方镜像安装Oracle11G
- CentOS7安装Docker,走上虚拟化容器引擎之路
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- 设置Eclipse缩进为4个空格,增强代码规范
- Eclipse初始化配置,告别卡顿、闪退、编译时间过长
- 2048小游戏-低调大师作品