FullGC排查手册

7 阅读2分钟

FullGC 如何排查

在生产环境中,Full GC 是 JVM 性能问题中最常见、最严重的问题之一
典型表现:

  • 服务 响应变慢
  • CPU 突然飙高
  • 接口超时
  • 服务卡顿甚至假死

因此大厂通常有一套 标准化 FullGC 排查流程


一、JVM 问题排查全景图

JVM问题
   │
   ├─ GC问题
   │     ├ jstat
   │     ├ jmap
   │     └ MAT
   │
   ├─ CPU高
   │     ├ top
   │     └ jstack
   │
   ├─ 线程问题
   │     └ jstack
   │
   └─ 实时诊断
         └ Arthas

二、JVM排查常用命令

命令作用
jps -l查看 Java 进程
jstat -gcutil pid查看 GC 使用率
jmap -heap pid查看堆结构
jmap -histo pid查看对象数量
jmap -dumpdump 堆
jstack pid查看线程
jcmd pid GC.heap_info查看堆
jcmd pid GC.class_histogram对象统计

三、第一步:确认是否发生 Full GC

使用命令:

jstat -gcutil <pid> 1000

含义:

jstat -gcutil pid interval
参数含义
jstatJVM 监控工具
-gcutil查看 GC 内存使用率
pidJava 进程ID
interval采样间隔(ms)

jstat 输出示例

S0     S1     E      O      M     CCS    YGC    YGCT   FGC   FGCT    GCT
0.00   0.00  20.35  80.12  92.14  88.23  1234   15.23   3     2.34   17.57

字段含义

字段含义
S0Survivor0 使用率
S1Survivor1 使用率
EEden 使用率
OOld 老年代使用率
MMetaspace
CCS压缩类空间
YGCYoung GC 次数
YGCTYoung GC 总时间
FGCFull GC 次数
FGCTFull GC 总时间
GCTGC 总时间

四、重点排查三个指标

1 Old区使用率(O)

O = Old区使用率

如果:

O > 90%

说明:

老年代即将满
马上可能 Full GC

2 Full GC 次数(FGC)

如果:

FGC 不断增长

例如:

1 → 2 → 3 → 4 → 5

说明:

Full GC 正在频繁发生

3 Full GC 耗时(FGCT)

FGCT = Full GC 总耗时

如果:

FGCT 快速增长

例如:

30s → 60s → 120s

说明:

Full GC 非常耗时

五、第二步:查看对象分布

执行:

jmap -histo:live <pid> | head -20

示例:

num     #instances     #bytes      class name
1       1000000        800MB       java.lang.String
2       500000         400MB       HashMap$Node

说明:

String 占用内存过多

可能原因:

  • 大量缓存
  • Map未释放
  • String拼接

六、第三步:查看堆结构

执行:

jmap -heap <pid>

示例输出:

Heap Configuration:
   MaxHeapSize      = 4096MB
   NewSize          = 125MB
   OldSize          = 333MB
   NewRatio         = 2
   SurvivorRatio    = 8

含义

参数含义
MaxHeapSize最大堆
NewSize新生代
OldSize老年代
NewRatio新生代:老年代
SurvivorRatioEden:Survivor

七、第四步:Dump堆内存

执行:

jmap -dump:live,format=b,file=heap.hprof <pid>

生成文件:

heap.hprof

这个文件包含:

JVM 所有对象

八、第五步:使用 MAT 分析

打开工具:

Eclipse MAT

导入:

heap.hprof

MAT 排查口诀

Histogram
   ↓
大对象
   ↓
Dominator Tree
   ↓
谁持有对象
   ↓
Path to GC Root
   ↓
代码

1 Histogram

查看:

对象数量排行

例如:

java.lang.String
HashMap
byte[]

2 Dominator Tree

查看:

谁占用最多内存

3 Path to GC Root

查看:

谁引用了对象

找到:

真正内存泄漏代码

九、CPU 100% 排查

第一步:找CPU线程

top -Hp <pid>

输出:

PID   TID   %CPU
1000  3456  99%

说明:

线程 3456 CPU占用最高

第二步:转16进制

printf "%x\n" 3456

输出:

d80

第三步:找线程栈

jstack <pid> | grep d80

找到:

nid=0xd80

看到代码:

OrderService.calculate()

说明:

这里CPU100%

十、线程问题排查

执行:

jstack <pid>

示例:

"main" #1 prio=5 os_prio=0 tid=0x0000000001a0a000 nid=0x3b4 runnable
   java.lang.Thread.State: RUNNABLE
        at com.demo.OrderService.createOrder(OrderService.java:50)

字段说明

字段含义
main线程名
prio优先级
tidJVM线程ID
nidOS线程ID
runnable状态

线程状态

状态含义
RUNNABLE运行
BLOCKED等待锁
WAITING等待
TIMED_WAITING定时等待
NEW新建
TERMINATED结束

十一、死锁识别

如果死锁:

Found one Java-level deadlock

示例:

Thread-1 等待 Thread-2
Thread-2 等待 Thread-1

十二、Arthas 实时诊断

下载

curl -O https://arthas.aliyun.com/arthas-boot.jar

wget https://arthas.aliyun.com/arthas-boot.jar

启动

java -jar arthas-boot.jar

输出:

* [1]: 1000 order-service.jar

选择:

1

进入:

[arthas@1000]$

十三、Arthas 常用命令

命令作用
dashboardJVM整体状态
thread查看线程
jvmJVM信息
memory内存
gcGC情况
sc查看类
sm查看方法
watch方法参数
trace方法耗时
stack调用栈

dashboard

dashboard

可以看到:

  • CPU
  • Heap
  • Thread
  • GC

thread

查看线程:

thread

查看 CPU 高线程:

thread -n 5

memory

查看内存:

memory

示例:

Heap used: 5GB
max: 8GB

trace(排查慢接口)

trace com.example.OrderService createOrder

输出:

createOrder()
   +---validateUser() 5ms
   +---queryProduct() 20ms
   +---saveOrder() 150ms

说明:

saveOrder慢

十四、Full GC 排查完整流程

1 确认GC
   jstat -gcutil

2 查看对象
   jmap -histo

3 查看堆结构
   jmap -heap

4 dump内存
   jmap -dump

5 MAT分析
   Dominator Tree
   Path to GC Root

6 实时分析
   Arthas