现象: 某个服务上线后,发现运行几天时间后,就没有响应了,也没有日志输出。之前的日志里面也没有任何异常。
猜测 : 进程也在,nginx等日志都正常,数据库也正常,怀疑就是服务有内存泄露。
排查思路
1. 生产环境不能影响正常服务,出现问题后,运维第一时间做了重启,现场就没了。然后用写了shell先定时重启(多台咱不间断提供服务)。
2. 接下来,就使用jmap看内存的对象数量了,总体来时只增不减的就是有问题的。同时,也做了个单独的测试环境,运行起来服务,准备重现问题。
3. 一下以重现的测试环境为例,首先找到进程号(假设是1302)
(1) 使用 jmap -heap 1302 先看一下堆的情况
Attaching to process ID 1302, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.131-b11
using thread-local object allocation.
Parallel GC with 8 thread(s)
Heap Configuration:
MinHeapFreeRatio = 0
MaxHeapFreeRatio = 100
MaxHeapSize = 4164943872 (3972.0MB)
NewSize = 87031808 (83.0MB)
MaxNewSize = 1388314624 (1324.0MB)
OldSize = 175112192 (167.0MB)
NewRatio = 2
SurvivorRatio = 8
MetaspaceSize = 21807104 (20.796875MB)
CompressedClassSpaceSize = 1073741824 (1024.0MB)
MaxMetaspaceSize = 17592186044415 MB
G1HeapRegionSize = 0 (0.0MB)
Heap Usage:
PS Young Generation
Eden Space:
capacity = 9437184 (9.0MB)
used = 9100328 (8.678749084472656MB)
free = 336856 (0.32125091552734375MB)
96.43054538302951% used
From Space:
capacity = 2621440 (2.5MB)
used = 0 (0.0MB)
free = 2621440 (2.5MB)
0.0% used
To Space:
capacity = 3145728 (3.0MB)
used = 0 (0.0MB)
free = 3145728 (3.0MB)
0.0% used
PS Old Generation
capacity = 2776629248 (2648.0MB)
used = 2776562952 (2647.9367752075195MB)
free = 66296 (0.06322479248046875MB)
99.99761235677944% used
26258 interned Strings occupying 3126264 bytes.
可以看到, PS Old Generation的空间已经占尽了,程序无响应就是这个情况的直接后果。
(2) 那就打印一下内存的对象占用情况,内容较多,就把内容输出到一个文本。
jmap -histo -F ${pid} > /xxx/logs/jmap.log
内容如下:
Attaching to process ID 1302, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.131-b11
Object Histogram:
num #instances #bytes Class description
--------------------------------------------------------------------------
1: 7393486 177443664 java.lang.String
2: 3653036 146121440 org.apache.kafka.clients.consumer.ConsumerRecord
3: 3653040 87672960 java.util.concurrent.LinkedBlockingQueue$Node
看了一下,ConsumerRecord 数量很多,和业务逻辑不符合(备注:线上环境可以持续关注这个对象的数量,如果持续的只增加不回收,就要注意了),且不被回收减少。
(3) 去项目程序看,使用这个对象的地方,经过排查,项目程序并无引用问题。使用这个对象的地方是,kafka的消费者Listener,对象由spring-kafka提供。于是怀疑是第三方spring kafka的bug。
(4) 经查, 我找到了这个两个issue。
https://github.com/spring-projects/spring-kafka/pull/162
https://github.com/spring-projects/spring-kafka/issues/161
查看issue的内容, 确认了一下版本号,我们用的依赖jar, 是在这个issue解决之前的。那这个对我们有影响,符合逻辑推演。因为我们的队列量不大不小,一直不回收,就会有这个问题。
(5) 更新版本,重新上线或测试环境, 观察heap使用情况。
参考:
JDK内置工具使用: http://blog.csdn.net/fenglibing/article/details/6411924
jvm查内存泄漏利器 - jmap: http://www.wujianjun.org/2016/09/21/jvm-tools-jmap/
FIXING SUN.JVM.HOTSPOT.DEBUGGER.DEBUGGEREXCEPTION: CAN’T ATTACH TO THE PROCESS: https://zenidas.wordpress.com/recipes/fixing-sun-jvm-hotspot-debugger-debuggerexception-cant-attach-to-the-process/
使用堆外内存: http://www.raychase.net/1526