CPU占用过高、FGC频繁问题

本文针对Java应用中出现的CPU占用率过高及FGC频繁的问题进行深入分析。通过top命令发现GC线程占用CPU过高,并利用jstat确认FGC次数持续增长。进一步通过jmap定位到具体业务对象LibraryVehicle导致的老年代内存溢出,最终通过优化缓存机制解决了问题。

CPU占用过高、FGC频繁问题

  1. top 表现如下

    PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND                                                                                                                                                                    
    94569 root      20   0   11.1g   1.5g  16416 S  1868  2.5   4025:49 java 
    
  2. top -H -p 94569 查看

     PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND                                                                                                                                                                     
     94635 root      20   0   11.1g   1.5g  16416 R 99.9  2.5 157:45.41 java                                                                                                                                                                        
     94644 root      20   0   11.1g   1.5g  16416 R 99.9  2.5 157:39.16 java                                                                                                                                                                        
     94627 root      20   0   11.1g   1.5g  16416 R 99.9  2.5 157:38.31 java                                                                                                                                                                        
     94629 root      20   0   11.1g   1.5g  16416 R 99.9  2.5 157:41.29 java  
    
  3. printf “%x\n” pid 转16进制

      [root@rabbitmq1 CLEService]# printf “%x\n” pid
    	“171a9”
    
  4. jstack pid| grep 16进制线程pid

     [root@rabbitmq1 zz]# jstack 94569 | grep '0x171a9' -C5
    
    "GC task thread#2 (ParallelGC)" os_prio=0 tid=0x00007f2440062800 nid=0x171a6 runnable 
    
    "GC task thread#3 (ParallelGC)" os_prio=0 tid=0x00007f2440064000 nid=0x171a7 runnable 
    
    **"GC task thread#4 (ParallelGC)" os_prio=0 tid=0x00007f2440066000 nid=0x171a9 runnable** 
    
    "GC task thread#5 (ParallelGC)" os_prio=0 tid=0x00007f2440068000 nid=0x171aa runnable 
    
    "GC task thread#6 (ParallelGC)" os_prio=0 tid=0x00007f244006a000 nid=0x171ab runnable
    
  5. 看信息是GC线程,再分析GC相关

    [root@rabbitmq1 zz]# jstat -gcutil 94569 2000
      S0     S1     E      O      M     CCS    YGC     YGCT    **FGC**    FGCT     GCT   
      0.00   0.00 100.00  99.99  94.61  92.49  45461 1679.444  9431 10069.071 11748.515
      0.00   0.00 100.00  99.99  94.61  92.49  45461 1679.444  9432 10070.225 11749.668
      0.00   0.00 100.00 100.00  94.61  92.49  45461 1679.444  9434 10072.497 11751.941
      0.00   0.00 100.00  99.99  94.61  92.49  45461 1679.444  9436 10074.888 11754.332
    FGC次数一直在增加
    
  6. 查看内存中对象数据
    [root@rabbitmq1 zz]# jmap -histo:live 94569 > 111.txt
    或者
    [root@rabbitmq1 zz]# jmap -histo:live 94569 | head -n 20

    num     #instances         #bytes  class name
    

      1:       6612342      171271640  [B
      2:       4988325      137461760  [C
      3:       4987368      119696832  java.lang.String
      **4:        978912      117469440  com.dahuatech.jt.entity.LibraryVehicle**
      5:       2905088      116203520  java.math.BigDecimal
      6:        550281       57229112  [[B
      7:       1947276       46734624  java.lang.Long
      8:        550259       17608288  com.mysql.cj.protocol.a.result.ByteArrayRow
      9:         20068        9178256  [Ljava.lang.Object;
     10:        550259        8804144  com.mysql.cj.protocol.a.MysqlTextValueDecoder
     11:         31506        2772528  java.lang.reflect.Method
     12:         76432        2445824  java.util.concurrent.ConcurrentHashMap$Node
     13:         18228        2012784  java.lang.Class
     14:         26489        1059560  java.util.LinkedHashMap$Entry
     15:         10718         920456  [Ljava.util.HashMap$Node;
     16:         25643         820576  java.util.HashMap$Node
     17:           352         782768  [Ljava.util.concurrent.ConcurrentHashMap$Node;
    

    LibraryVehicle是业务对象,需要找到生成该对象的代码块

  7. 可以生成dump文件查看调用链
    jmap -dump:file=文件名.dump [pid]

  8. 使用工具查看大对象调用链

     堆遍历器-最大对象
    Session:	HPROF snapshot "1 - 副本.hprof"
    Time of export:	2021年6月30日 星期三 下午05时02分59秒 CST
     	 
    当前对象集: 	5,776个类的25,068,289个对象
    选择步骤: 	See below
    分组: 	无
    
    对象	保留大小
    com.test.jt.global.GlobalCache (0x258f)	 300 MB (35 %)
     298 MB (99.4%) static vehiclesCacheList   java.util.ArrayList
     298 MB (99.4%) elementData   java.lang.Object[ ]
     另一个550,259实例,总保留大小为294 MB,最大单个保留大小为928 字节
     1,883 kB (0.6%) static illegalDriverFaceInfoMap   java.util.concurrent.ConcurrentHashMap
     1,883 kB (0.6%) table   java.util.concurrent.ConcurrentHashMap$Node[ ]
    

    看到GlobalCache 类中vehiclesCacheList 过大,排查代码发现,该缓存2s定时任务刷新一次,库中数据为50万,大对象直接放老年代,一直FGC

  9. 修改为相关获取缓存逻辑从数据库中根据索引字段获取后,打包重启服务后正常

<think>我们正在处理用户关于如何分析和定位Java应用程序导致CPU和内存占用问题。根据用户提供的引用,我们可以总结出以下步骤: ### 定位CPU占用问题 1. **使用top命令找到占用的进程**:`top`命令可以查看系统资源占用情况,找出CPU占用的Java进程,记录其PID(进程ID)。 2. **查看进程中线程的CPU占用**:使用命令`ps -mp <pid> -o THREAD,tid,time,rss,size,%mem`查看该进程下所有线程的CPU占用情况,找出占用的线程ID(TID)。 3. **将TID转换为十六进制**:因为堆栈信息中的线程ID是十六进制,使用`printf "%x\n" <tid>`将占用线程的TID转换为十六进制格式。 4. **打印线程堆栈**:使用`jstack <pid> | grep <十六进制TID> -A 30`查看该线程的堆栈信息,定位具体代码位置。或者将整个堆栈输出到文件:`jstack <pid> > stack.log`。 ### 定位内存占用问题 1. **使用jmap分析内存对象**:执行`jmap -histo:live <pid>`查看存活对象的数量和大小,分析哪些对象占用了大量内存。或者生成堆转储文件:`jmap -dump:live,format=b,file=heapdump.hprof <pid>`。 2. **使用MAT工具分析**:将生成的堆转储文件(heapdump.hprof)导入MAT(Memory Analyzer Tool)工具中,分析内存泄漏的原因。 ### 其他工具 - **jstat**:可以查看垃圾回收情况,判断是否因频繁GC导致CPU。 - **VisualVM**:图形化工具,可以监控CPU、内存、线程等。 ### 注意事项 - 在生产环境中,执行这些命令可能会对应用性能产生一定影响,建议在低峰期进行。 - 分析堆栈信息时,注意线程状态(如RUNNABLE、BLOCKED),长时间运行或阻塞的线程可能导致问题。 根据以上内容,我们可以组织回答如下:</think>### 分析和定位Java应用程序CPU/内存占用的方法 以下是系统化的定位流程,结合Linux命令和JDK工具: --- #### **一、定位CPU占用问题** 1. **定位异常进程** ```bash top -c # 按P根据CPU排序,记录Java进程PID ``` ![top命令示例](https://example.com/top-cmd.png) *示例:找出CPU>200%的Java进程[^1]* 2. **分析进程内线程** ```bash ps -mp <PID> -o THREAD,tid,time,%cpu # 查看线程CPU占用 printf "%x\n" <占用TID> # 将十进制TID转十六进制 ``` *示例:线程TID 11520(转十六进制0x2d00)占用90% CPU[^3]* 3. **检查线程堆栈** ```bash jstack <PID> | grep -A 20 <0x2d00> # 查看该线程的堆栈 ``` 常见原因: - 死循环(如`while(true)`未休眠) - 锁竞争(大量`BLOCKED`线程) - 频繁GC(通过`jstat`验证) --- #### **二、定位内存占用问题** 1. **生成内存快照** ```bash jmap -dump:live,format=b,file=heap.hprof <PID> # 导出堆内存 ``` *文件较大时用`-F`强制导出(需JDK调试权限)[^3]* 2. **分析内存对象** ```bash jmap -histo:live <PID> | head -20 # 查看对象数量排序 ``` 输出示例: ``` num instances bytes class name ---------------------------------------- 1: 98234 1.2GB [B (byte数组) 2: 56000 800MB java.util.HashMap$Node ``` 3. **使用MAT工具分析** 加载`heap.hprof`文件,检查: - **Dominator Tree**:找出内存占用最大的对象 - **Leak Suspects**:自动检测内存泄漏点 ![MAT分析示例](https://example.com/mat-screenshot.png) --- #### **三、辅助诊断工具 | 工具 | 命令/用法 | 作用 | |---------------|-------------------------------|------------------------------| | **jstat** | `jstat -gcutil <PID> 2s` | 监控GC频率和耗时 | | **VisualVM** | 图形化连接进程 | 实时监控CPU/内存/线程 | | **Arthas** | `thread -n 3` | 在线诊断线程阻塞问题 | --- #### **四、常见问题场景** - **CPU飙升**: - 代码死循环(如递归未设出口) - 频繁Full GC(检查`jstat`中`FGC`次数) - 正则表达式回溯(如`(a+)+b`匹配长字符串) - **内存泄漏**: - 静态集合持续增长(如`static HashMap`) - 未关闭资源(数据库连接、文件流) - 缓存未设过期(如Guava Cache) > ⚠️ 生产环境建议: > 1. 使用`nohup`执行诊断命令避免断开 > 2. 快照文件传输前用`tar -zcf`压缩 > 3. 负载时优先采样分析(`jcmd <PID> Thread.print`) ---
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值