线上CPU100%,频繁FullGC排查套路笔记

本文总结了五种常见导致线上功能缓慢的问题及排查思路,包括内存溢出、CPU过高、阻塞操作、线程WAITING和死锁状态。通过top、jstack等工具分析,定位并解决线上系统运行缓慢或不可用的情况。

背景介绍

对于线上系统突然产生的运行缓慢问题,如果该问题导致线上系统不可用,那么首先需要做的就是,导出 jstack 和内存信息,然后重启系统,尽快保证系统的可用性。

这种情况可能的原因主要有两种:

  • 代码中某个位置读取数据量较大,导致系统内存耗尽,从而导致 Full GC 次数过多,系统缓慢。
  • 代码中有比较耗 CPU 的操作,导致 CPU 过高,系统运行缓慢。

相对来说,这是出现频率最高的两种线上问题,而且它们会直接导致系统不可用。

另外有几种情况也会导致某个功能运行缓慢,但是不至于导致系统不可用:

  • 代码某个位置有阻塞性的操作,导致该功能调用整体比较耗时,但出现是比较随机的。
  • 某个线程由于某种原因而进入 WAITING 状态,此时该功能整体不可用,但是无法复现。
  • 由于锁使用不当,导致多个线程进入死锁状态,从而导致系统整体比较缓慢。

具体情况及定位步骤

简要的说,我们进行线上日志分析时,主要可以分为如下情况:

1、 CPU 比较高
  • 通过 top 命令查看 CPU 情况,如果 CPU 比较高,则通过 top -Hp 命令查看当前进程的各个线程运行情况。

  • 找出 CPU 过高的线程之后,将其线程 id 转换为十六进制的表现形式(printf “%x” ),然后在 jstack 日志中查看该线程主要在进行的工作(jstack 进程pid | grep 十六进制的线程tid 如:jstack 1956 | grep -A10 7b9:显示含有线程号为【7b9】的那一行以及其后10行的数据)。

这里又分为两种情况:

  • 如果是正常的用户线程,则通过该线程的堆栈信息查看其具体是在哪处用户代码处运行比较消耗 CPU。(jstack -l pid(进程pid) > 文件路径名)
  • 如果该线程是 VM Thread,则通过 jstat -gcutil 命令监控当前系统的 GC 状况。
    然后通过 jmap dump:format=b,file= 导出系统当前的内存数据。导出之后将内存情况放到 Eclipse 的 Mat 工具中进行分析即可得出内存中主要是什么对象比较消耗内存,进而可以处理相关代码。
2、 CPU 并不高
  • 如果通过 top 命令看到 CPU 并不高,并且系统内存占用率也比较低。此时就可以考虑是否是由于另外三种情况导致的问题。

具体的可以根据具体情况分析:

  • 如果是接口调用比较耗时,并且是不定时出现,则可以通过压测的方式加大阻塞点出现的频率,从而通过 jstack 查看堆栈信息,找到阻塞点。
  • 如果是某个功能突然出现停滞的状况,这种情况也无法复现,此时可以通过多次导出 jstack 日志的方式对比哪些用户线程是一直都处于等待状态,这些线程就是可能存在问题的线程。
  • 如果通过 jstack 可以查看到死锁状态,则可以检查产生死锁的两个线程的具体阻塞点,从而处理相应的问题。

本文主要总结了五种常见的导致线上功能缓慢的问题,以及排查思路。当然,线上的问题出现的形式是多种多样的,也不一定局限于这几种情况。

参考

https://blog.youkuaiyun.com/keketrtr/article/details/54289134
https://blog.youkuaiyun.com/jerry024/article/details/8507589

### 排查和解决Java应用中频繁Full GC问题的方法 频繁Full GC会导致系统性能下降,影响服务的稳定性和响应时间。以下是排查和解决频繁Full GC问题的具体方法: #### 1. 分析GC日志以定位问题 通过开启GC日志,可以详细了解JVM垃圾回收的行为和触发条件。启用GC日志的参数如下: ```bash -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/path/to/gc.log ``` 分析GC日志时,重点关注以下内容: - Full GC发生的频率和持续时间。 - 每次Full GC后内存使用情况。 - 老年代、新生代和元空间的内存分配是否合理[^1]。 #### 2. 堆内存不足的排查化 如果堆内存不足以容纳所有对象,则会频繁触发Full GC。可以通过以下方式检查和化: - 使用`jstat`工具监控堆内存使用情况: ```bash jstat -gc <pid> 1000 5 ``` - 如果发现堆内存使用率接近上限,考虑增加堆内存大小: ```bash -Xms<size> -Xmx<size> ``` 例如:`-Xms4g -Xmx4g`将堆内存设置为4GB[^1]。 #### 3. 大对象创建的排查化 大对象直接进入老年代,可能导致老年代快速填满并触发Full GC排查方法包括: - 检查代码中是否存在大量大对象的创建。 - 使用`jmap`生成堆转储文件并分析: ```bash jmap -dump:live,format=b,file=heap.dump <pid> ``` - 使用工具如Eclipse MAT或VisualVM分析堆转储文件,找出占用内存较大的对象[^2]。 #### 4. 内存泄漏的排查与修复 内存泄漏是导致频繁Full GC的常见原因。以下是排查步骤: - 使用`jconsole`或`jvisualvm`监控内存使用趋势。 - 如果内存使用量持续增长且无法释放,可能存在内存泄漏。 - 通过堆转储文件分析泄漏源,重点关注长时间存活的对象及其引用链[^1]。 #### 5. 空间分配担保机制的检查 JVM在Minor GC时可能会尝试将部分对象晋升到老年代。如果老年代空间不足,且未开启空间分配担保机制,则会直接触发Full GC。检查方法如下: - 确保启用了空间分配担保机制: ```bash -XX:+HandlePromotionFailure ``` - 如果老年代最大连续可用空间小于历次晋升平均大小,则需要调整老年代大小或化对象分配策略[^2]。 #### 6. JVM参数调 根据应用的实际需求,调整JVM参数以减少Full GC的发生: - 调整新生代和老年代比例: ```bash -XX:NewRatio=<value> ``` 例如:`-XX:NewRatio=2`表示新生代与老年代的比例为1:2。 - 启用G1垃圾收集器以降低Full GC频率: ```bash -XX:+UseG1GC ``` ### 示例代码:启动JVM时添加GC日志和参数化 ```bash java -Xms4g -Xmx4g -XX:NewRatio=2 -XX:+UseG1GC -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/path/to/gc.log -jar your-application.jar ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值