若用户反馈线上服务请求无响应,可以按照以下步骤进行排查。
一、确认服务器内存使用情况
执行free
命令,看看服务器内存是否正常。
total used free shared buff/cache available
Mem: 16256764 7583240 2177924 2901216 6495600 5437168
Swap: 2097148 1280 2095868
看起来服务器内存是正常的。
二、确认服务器磁盘使用情况
执行df -h
命令,查看磁盘使用情况。
文件系统 容量 已用 可用 已用% 挂载点
xxxxxxxx 7.8G 0 7.8G 5% /xx
……
磁盘空间看起来也是充足的。
三、查看哪个进程占用内存多
输入top
命令,然后按 shift p
,最前面的就是占用内存最多的。
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
32297 xxxxxx 20 0 109.7g 4.7g 4.0g S 46.2 30.2 160554:47 java
这里可以获取到占用内存最多的pid。
四、查看线程的堆栈信息
执行jstack <pid>
,会显示线程的状态。
Full thread dump OpenJDK 64-Bit Server VM (17+35-2724 mixed mode, sharing):
Threads class SMR info:
_java_thread_list=0x00007f24080015b0, length=41, elements={
0x00007f266c0899e0, 0x00007f266c08b050, 0x00007f266c091d60, 0x00007f266c093280,
0x00007f266c0948a0, 0x00007f266c0964a0, 0x00007f266c097c00, 0x00007f266c0a12a0,
0x00007f266c0befe0, 0x00007f266c0fa7d0, 0x00007f266c23c6b0, 0x00007f266c26dab0,
0x00007f266c425430, 0x00007f266c5ce320, 0x00007f266c6a3ca0, 0x00007f266c6e9790,
0x00007f266c9297f0, 0x00007f266c92d740, 0x00007f266c95d4a0, 0x00007f266c027640,
0x00007f23b8005600, 0x00007f23dc55f2c0, 0x00007f23dc5705c0, 0x00007f23bc0b8bc0,
0x00007f23bc15ebf0, 0x00007f23bc1e0cd0, 0x00007f23b800b850, 0x00007f23b800c2e0,
0x00007f23b800d370, 0x00007f23b800e9d0, 0x00007f23b800fbd0, 0x00007f23b8010ee0,
0x00007f23b8012220, 0x00007f23c4439b80, 0x00007f23c4006500, 0x00007f23c46ae980,
0x00007f23544eeb00, 0x00007f237c406d00, 0x00007f23a829ba30, 0x00007f23c46b2560,
0x00007f2408000a90
}
"Reference Handler" #2 daemon prio=10 os_prio=0 cpu=1463269.24ms elapsed=23238405.12s tid=0x00007f266c0899e0 nid=0x7e38 waiting on condition [0x00007f26542be000]
java.lang.Thread.State: RUNNABLE
at java.lang.ref.Reference.waitForReferencePendingList(java.base@17/Native Method)
at java.lang.ref.Reference.processPendingReferences(java.base@17/Reference.java:253)
at java.lang.ref.Reference$ReferenceHandler.run(java.base@17/Reference.java:215)
如果有死锁,就会有下面这种:
java.lang.Thread.State: BLOCKED
at xxxxxx1
- waiting to lock <0xxxxxx>
- locked <1xxxxx>
java.lang.Thread.State: BLOCKED
at xxxxxx2
- waiting to lock <1xxxxxx>
- locked <0xxxxx>
五、查看堆内存占用情况
执行jmap -heap <pid>
命令,可以看到堆内存的使用情况。
Attaching to process ID 5064, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.271-b09
using thread-local object allocation.
Parallel GC with 8 thread(s)
Heap Configuration:
MinHeapFreeRatio = 0
MaxHeapFreeRatio = 100
MaxHeapSize = 536870912 (512.0MB)
NewSize = 268435456 (256.0MB)
MaxNewSize = 268435456 (256.0MB)
OldSize = 268435456 (256.0MB)
NewRatio = 2
SurvivorRatio = 8
MetaspaceSize = 134217728 (128.0MB)
CompressedClassSpaceSize = 260046848 (248.0MB)
MaxMetaspaceSize = 268435456 (256.0MB)
G1HeapRegionSize = 0 (0.0MB)
Heap Usage:
PS Young Generation
Eden Space:
capacity = 241172480 (230.0MB)
used = 94437072 (90.06221008300781MB)
free = 146735408 (139.9377899169922MB)
39.15748264478601% used
From Space:
capacity = 13107200 (12.5MB)
used = 3045992 (2.9048843383789062MB)
free = 10061208 (9.595115661621094MB)
23.23907470703125% used
To Space:
capacity = 12582912 (12.0MB)
used = 0 (0.0MB)
free = 12582912 (12.0MB)
0.0% used
PS Old Generation
capacity = 268435456 (256.0MB)
used = 84914376 (80.98065948486328MB)
free = 183521080 (175.01934051513672MB)
31.63307011127472% used
46153 interned Strings occupying 5019920 bytes.
这里可以看到伊甸园区、from区、to区和老年代的内存占用情况,如果老年代99.99%了,那就是堆内存溢出了。
六、查看堆内存中内存占用前10的对象信息
jmap -histo:live <pid> | head -n 10
执行这个命令,可以看到内存占用前10的对象信息。
num #instances #bytes class name
----------------------------------------------
1: 165329 18150792 [C
2: 163258 3918192 java.lang.String
3: 40481 3562328 java.lang.reflect.Method
4: 28014 2844936 [Ljava.lang.Object;
5: 67703 2166496 java.util.concurrent.ConcurrentHashMap$Node
6: 7919 2106384 [B
7: 17131 1934896 java.lang.Class
如果这里看到有自己写的类对象,那可能就可以找到问题了。
七、分析内存溢出问题
确定了是哪一个节点有问题,那么先把节点的流量切走。
如果第六步没分析出来是什么导致内存溢出,可以按如下步骤排查。
1. 导出dump文件
jmap -dump:format=b,live,file=<fileName>.hprof <pid>
执行该命令,可以导出名为fileName.hprof
的dump文件
2. 分析dump文件
这里介绍使用mat工具分析dump文件。
- 先点击mat工具的
window ---> preferences ---> memory Analyzer
,把keep unreachable objects
勾选上,勾上了会保留不可达对象; - 点击
file ---> open heap dump
,选择刚才的dump文件,等待几分钟,mat工具会生成一个默认的报告; - 默认报告里会列出problems,点击
details
就可以看到问题详情,一般会列出有问题的对象; - 选择有问题的对象,右键
Merge Shortest Paths to GC Roots ---> exclude weak references
; - 然后再
Java Basics ---> Thread Details
,这样就可以看到线程详情,线程详情中就会有错误信息,可以定位到在代码的哪一行报错了;
如果默认报告按照上面的方式定位不到具体代码,那可以尝试以下步骤:
- 在overview页点击histogram,然后按照retained heap排序;
- 然后选择默认排在前面的对象,右键
merge shortest path to GC Roots
; - 然后选择线程,
Java Basics ---> Thread Details
,一个个地看详情详情,一般看前几个就可以定位到问题了。