2周前,我开始搜索不断增长的Java内存。我正在使用以下命令来防止堆增长过多,并进行一些调试。
我正在使用Oracle Java 8在Ubuntu 16.04上运行,因为openjdk 8没有使jemaloc提供正确数据所需的调试符号
port
您可以看到我的Xmx设置为256m。但是-XX:NativeMemoryTracking=detail -XX:+UseG1GC -XX:+UseStringDeduplication -Xms64m -Xmx256m -XX:MaxMetaspaceSize=128m -Xss256k
当前显示我的进程为1.1G
使用JProfiler和JVisualVm我以及在Google上可以找到的许多其他东西后,我得出的结论是,这肯定是一个堆外问题。
经过大量搜索,我发现top
,而且我读到的有关该文章的前景似乎很好。但是我现在在解释这些数据时遇到一些问题。并找出如何找出问题的根源。
本机内存跟踪数据
jemaloc
答案 0 :(得分:8)
本机内存跟踪仅说明Java虚拟机的结构,但不计算内存映射文件或共享库分配的本机内存(包括Java类库的本机代码)。此外,NMT不跟踪标准libc分配器malloc
的任何内部碎片。
首先,要分析Java进程的堆外使用情况,请查看其完整内存映射:
pmap -X <pid>
这将阐明映射文件还是匿名区域使用内存。
如果您看到多个匿名区域变为64 MB,则可能是malloc舞台的标志。已知Libc malloc在某些系统上与excessive virtual memory usage有问题。在这种情况下,使用jemalloc
或tcmalloc
作为即插即用替换(即使没有概要分析功能)也可能成为解决方案。
不幸的是,jemalloc探查器对Java一无所知。该图在最后一个本机函数处中断,因此输出可能会造成混淆。在您的情况下,jemalloc建议问题可能与类加载和System.loadLibrary
有关,但是如果没有完整图片,很难确定。
Async-profiler允许在Java上下文中跟踪本机分配。运行
./profiler.sh -d <duration> -e malloc -f malloc.svg <pid>
这将产生malloc
个呼叫中的Flame Graph,例如:
这只是一个示例,展示了java.util.zip.GZIPOutputStream
如何成为本地内存分配的来源。当然,您的情况会有所不同。
注意,malloc
本身并不意味着内存泄漏。例如。可以分配内存,然后不久释放。该图只是在哪里查看的提示。
为了找到RSS增加的地方,您可能想跟踪mprotect
或mmap
的电话。这可以通过async-profiler以类似的方式完成:
./profiler.sh -d <duration> -e mprotect -f mprotect.svg <pid>
./profiler.sh -d <duration> -e mmap -f mmap.svg <pid>
我注意到您的jemalloc图中有cbClassPrepare
和classTrack_processUnloads
函数。这意味着您正在使用jdwp
调试代理。这绝对是内存分配过多的原因-我以前在jdwp
中曾见过内存泄漏。通过-agentlib
,-agentpath
或-javaagent
选项启用的任何其他代理库也是可疑的,因为JVM不会跟踪其本机内存使用情况。