解释jmap产生的数据可能有堆外泄漏

9

我两周前开始寻找一个不断增长的Java内存。我正在使用以下命令来防止堆栈过度增长,并进行一些调试。

我在Ubuntu 16.04上运行,使用Oracle Java 8,因为OpenJDK 8没有我需要使jemaloc提供正确数据的调试符号。

-XX:NativeMemoryTracking=detail -XX:+UseG1GC -XX:+UseStringDeduplication -Xms64m -Xmx256m -XX:MaxMetaspaceSize=128m -Xss256k

如您所见,我的Xmx设置为256m。然而,top目前显示我的进程为1.1G。

经过使用JProfiler和JVisualVm以及我在谷歌上找到的其他许多东西后,我得出结论,这必须是一个非堆问题。

经过长时间的搜索,我发现了jemaloc,并且我阅读的有关它的文章似乎很有希望。但是我现在遇到了一些解释数据的问题,并且需要找出问题的根源。

内存使用情况

jemaloc图形

本机内存跟踪数据

Native Memory Tracking:

Total: reserved=1678MB, committed=498MB
-                 Java Heap (reserved=256MB, committed=256MB)
                            (mmap: reserved=256MB, committed=256MB)

-                     Class (reserved=1103MB, committed=89MB)
                            (classes #14604)
                            (malloc=3MB #32346)
                            (mmap: reserved=1100MB, committed=85MB)

-                    Thread (reserved=26MB, committed=26MB)
                            (thread #53)
                            (stack: reserved=26MB, committed=26MB)

-                      Code (reserved=261MB, committed=96MB)
                            (malloc=17MB #17740)
                            (mmap: reserved=244MB, committed=79MB)

-                        GC (reserved=1MB, committed=1MB)
                            (mmap: reserved=1MB, committed=1MB)

-                  Internal (reserved=6MB, committed=6MB)
                            (malloc=6MB #48332)

-                    Symbol (reserved=19MB, committed=19MB)
                            (malloc=16MB #168491)
                            (arena=4MB #1)

-    Native Memory Tracking (reserved=5MB, committed=5MB)
                            (tracking overhead=4MB)
1个回答

42

检查进程内存映射

本地内存跟踪仅记录Java虚拟机的结构,但不计算内存映射文件或由共享库分配的本地内存(包括Java类库的本地代码)。此外,NMT不跟踪malloc标准libc分配器的任何内部碎片。

首先,要分析Java进程的堆外使用情况,请查看其完整的内存映射:

pmap -X <pid>

这将揭示内存是由映射文件还是匿名区域使用。
更改标准分配器
如果您看到多个匿名区域的数量为64 MB,则可能是malloc竞技场的迹象。已知libc malloc在某些系统上存在过度虚拟内存使用问题。在这种情况下,使用jemalloctcmalloc作为替代(即使没有分析功能)可能成为一种解决方案。
分析本机分配
不幸的是,jemalloc分析器对Java一无所知;图表在最后一个本机函数处中断,因此输出可能看起来令人困惑。在您的情况下,jemalloc建议问题可能与类加载和System.loadLibrary有关,但没有完整的图片很难确定。 Async-profiler允许在Java上下文中跟踪本机分配。运行
./profiler.sh -d <duration> -e malloc -f malloc.svg <pid>

这将生成一个火焰图,显示malloc调用情况,例如:

Malloc Flame Graph

这只是一个示例,演示了java.util.zip.GZIPOutputStream如何成为本地内存分配的源。当然,你的情况会有所不同。

请注意malloc调用本身并不意味着存在内存泄漏。例如,内存可能会被分配,然后很快释放。图表只是提示要查看的位置。

为了找到RSS增加的位置,您可能需要跟踪mprotectmmap调用。可以使用async-profiler以类似的方式完成:

./profiler.sh -d <duration> -e mprotect -f mprotect.svg <pid>
./profiler.sh -d <duration> -e mmap -f mmap.svg <pid>

注意代理库

我注意到你的jemalloc图表中有cbClassPrepareclassTrack_processUnloads函数。这意味着你正在使用jdwp调试代理。这可能是过度内存分配的原因 - 我以前曾看到jdwp中存在内存泄漏问题。任何通过-agentlib-agentpath-javaagent选项启用的其他代理库也是嫌疑对象,因为它们的本地内存使用情况不受JVM跟踪。


mprotectmmap生成的.jfr文件中没有内存信息,只有CPU分析。这是否是预期的?@apangin - expert
1
@专家 当您选择-e mprotect-e mmap时,您将对这些函数的调用进行分析,而不是CPU采样。在这里,分配配置文件是无关紧要的。所以是的,这是预期的。 - apangin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接