记录一次生产内存泄漏问题排查~

1,634 阅读3分钟

事件背景

生产环境上,有一个应用部署的容器上,总是一两天自动重启一次,怀疑应该是内存溢出/泄漏导致。

排查过程分析

1、首先从pinpoint 监控上查看JVM的内存信息

image.png

如上图所示,Java应用设置的堆内存为1.2G,metaspace 256M,在应用重启之前,此时heap的内存为800m左右,非堆内存占用也非常的稳定。此时从监控数据看,可以排除是应用程序本身发生内存溢出,而且重启的时候也没有dump文件的生成。

2、此时来看K8S容器的监控

image.png

如上图所示,重启时容器内的内存已经到达了100%,然后去看容器的日志,确实是在应用申请内存时显示内存不足,然后触发了OOM ,此时容器发起了应用评分,然后把Java应用kill掉了。到此时应该可以判定,应该该应用有内存泄漏引起的,并且是堆外内存。

3、添加NMT追踪内存 NMT(Native Memory tracking)是一种Java HotSpot VM功能,可跟踪Java HotSpot VM的内部内存使用情况(jdk8+)。 使用方法: 开启

在JVM启动参数中添加-XX:NativeMemoryTracking=detail

查看 jcmd 进程id VM.native_memory summary scale=MB

然后重启项目,观察容器内存监控,当容器内存监控到达98% 时,通过jcmd 命令查看内存追踪

Native Memory Tracking:

Total: reserved=6065MB +2914MB, committed=4917MB +2961MB

-                 Java Heap (reserved=1228MB, committed=1228MB)
                            (mmap: reserved=1228MB, committed=1228MB)
 
-                     Class (reserved=1192MB +32MB, committed=188MB +36MB)
                            (classes #28148 +4283)
                            (malloc=6MB +2MB #77459 +30082)
                            (mmap: reserved=1186MB +30MB, committed=182MB +34MB)
 
-                    Thread (reserved=3141MB +2794MB, committed=3141MB +2794MB)
                            (thread #6173 +5497)
                            (stack: reserved=3114MB +2770MB, committed=3114MB +2770MB)
                            (malloc=20MB +18MB #30865 +27485)
                            (arena=7MB +6 #12343 +10994)
 
-                      Code (reserved=266MB +9MB, committed=121MB +53MB)
                            (malloc=22MB +9MB #29403 +11126)
                            (mmap: reserved=244MB, committed=99MB +43MB)
 
-                        GC (reserved=9MB, committed=9MB)
                            (malloc=5MB #2822 +1372)
                            (mmap: reserved=4MB, committed=4MB)
 
-                  Compiler (reserved=3MB +2MB, committed=3MB +2MB)
                            (malloc=3MB +2MB #4822 +2882)
 
-                  Internal (reserved=145MB +71MB, committed=145MB +71MB)
                            (malloc=145MB +71MB #117204 +76609)
 
-                    Symbol (reserved=29MB +1MB, committed=29MB +1MB)
                            (malloc=26MB +1MB #290132 +15483)
                            (arena=3MB #1)
 
-    Native Memory Tracking (reserved=10MB +4MB, committed=10MB +4MB)
                            (malloc=1MB +1MB #15058 +9675)
                            (tracking overhead=9MB +3MB)
 
-                   Unknown (reserved=42MB, committed=42MB)
                            (mmap: reserved=42MB, committed=42MB)

可以看到,thread 的内存比启动时申请的内存多了巨多。 这时可以判断为 是应用程序的线程堆栈泄漏了,现在就是要来找到是哪个线程引起的。

4、启动容器的arthas ,使用命令 thread -n 20 把占用最多的前20线程的堆栈信息打印出来,就信息中就找到了内存泄漏的可疑线程。

最后附上一张JVM内存划分图:

32e244ffcafacdde5edb900f9ba4f187_a4ecb6aac4d89948d2db01c7b03957f5.png

图片来源:blog.csdn.net/weixin_3625…