我们最近对生产系统的观察告诉我们,Java容器的驻留内存使用量增长了。关于这个问题,我们已经做了一些调查,以了解为什么java进程使用像pmap这样的本机工具消耗比Heap + Thread Stacks + Shared Objects + Code Cache +等更多的内存。因此,我们发现了一些由本机进程(可能使用malloc / mmap)分配的64M内存块(成对):
0000000000400000 4K r-x-- /usr/java/jdk1.7.0_17/bin/java
0000000000600000 4K rw--- /usr/java/jdk1.7.0_17/bin/java
0000000001d39000 4108K rw--- [ anon ]
0000000710000000 96000K rw--- [ anon ]
0000000715dc0000 39104K ----- [ anon ]
00000007183f0000 127040K rw--- [ anon ]
0000000720000000 3670016K rw--- [ anon ]
00007fe930000000 62876K rw--- [ anon ]
00007fe933d67000 2660K ----- [ anon ]
00007fe934000000 20232K rw--- [ anon ]
00007fe9353c2000 45304K ----- [ anon ]
00007fe938000000 65512K rw--- [ anon ]
00007fe93bffa000 24K ----- [ anon ]
00007fe940000000 65504K rw--- [ anon ]
00007fe943ff8000 32K ----- [ anon ]
00007fe948000000 61852K rw--- [ anon ]
00007fe94bc67000 3684K ----- [ anon ]
00007fe950000000 64428K rw--- [ anon ]
00007fe953eeb000 1108K ----- [ anon ]
00007fe958000000 42748K rw--- [ anon ]
00007fe95a9bf000 22788K ----- [ anon ]
00007fe960000000 8080K rw--- [ anon ]
00007fe9607e4000 57456K ----- [ anon ]
00007fe968000000 65536K rw--- [ anon ]
00007fe970000000 22388K rw--- [ anon ]
00007fe9715dd000 43148K ----- [ anon ]
00007fe978000000 60972K rw--- [ anon ]
00007fe97bb8b000 4564K ----- [ anon ]
00007fe980000000 65528K rw--- [ anon ]
00007fe983ffe000 8K ----- [ anon ]
00007fe988000000 14080K rw--- [ anon ]
00007fe988dc0000 51456K ----- [ anon ]
00007fe98c000000 12076K rw--- [ anon ]
00007fe98cbcb000 53460K ----- [ anon ]
我用0000000720000000解释该行3670016K指的是堆空间,我们使用JVM参数" -Xmx"定义其大小。在那之后,对开始,其总和是64M。 我们使用的是CentOS版本5.10(最终版)64位arch和JDK 1.7.0_17。
问题是,那些街区是什么?哪个子系统分配这些?
更新:我们不使用JIT和/或JNI本机代码调用。
答案 0 :(得分:14)
我遇到了同样的问题。这是glibc> = 2.10
的已知问题解决方法是设置此env变量
export MALLOC_ARENA_MAX=4
IBM关于设置MALLOC_ARENA_MAX的文章 https://www.ibm.com/developerworks/community/blogs/kevgrig/entry/linux_glibc_2_10_rhel_6_malloc_may_show_excessive_virtual_memory_usage?lang=en
Google for MALLOC_ARENA_MAX或在SO上搜索它以查找大量参考资料。
您可能还想调整其他malloc选项以优化分配内存的低碎片:
# tune glibc memory allocation, optimize for low fragmentation
# limit the number of arenas
export MALLOC_ARENA_MAX=2
# disable dynamic mmap threshold, see M_MMAP_THRESHOLD in "man mallopt"
export MALLOC_MMAP_THRESHOLD_=131072
export MALLOC_TRIM_THRESHOLD_=131072
export MALLOC_TOP_PAD_=131072
export MALLOC_MMAP_MAX_=65536
答案 1 :(得分:5)
也可能存在本机内存泄漏。一个常见问题是由于未关闭ZipInputStream
/ GZIPInputStream
而导致的本机内存泄漏。
打开ZipInputStream
的典型方式是致电Class.getResource
/ ClassLoader.getResource
并在openConnection().getInputStream()
实例上调用java.net.URL
或致电{ {1}} / Class.getResourceAsStream
。必须确保这些流始终关闭。
检查Zip * Stream泄漏的一种方法是获取堆转储并使用" zip"," Inflater"来搜索任何类的实例。或" Deflater"在名字里。这在许多堆转储分析工具中是可能的,例如Yourkit Java Profiler,JProfiler或Eclipse MAT。它还值得检查最终状态中的对象,因为在某些情况下,只有在完成后才释放内存。检查可能使用本机库的类很有用。这也适用于TLS / ssl库。
您可以使用jemalloc通过指定ClassLoader.getResourceAsStream
环境变量中的设置启用malloc采样分析来调试本机内存泄漏。此博客文章中提供了详细说明:http://www.evanjones.ca/java-native-leak-bug.html。 This blog post还有关于使用jemalloc调试java应用程序中的本机内存泄漏的信息。
同一博客还包含有关其他native memory leak related to ByteBuffers的信息。 Java 8u102有一个特殊的系统属性MALLOC_CONF
,用于限制该博客帖子中描述的缓存问题。
jdk.nio.maxCachedBufferSize
总是检查打开文件句柄以查看内存泄漏是否是由大量mmap:ed文件引起的。在Linux上-Djdk.nio.maxCachedBufferSize=262144
可用于列出打开的文件和打开套接字:
lsof
该过程的内存映射报告还可以帮助调查本机内存泄漏
lsof -Pan -p PID
对于在Docker中运行的Java进程,应该可以在" host"上执行lsof或pmap命令。您可以使用此命令找到容器化过程的PID
pmap -x PID
获取线程转储(或使用jconsole / JMX)检查线程数也很有用,因为每个线程为其堆栈消耗1MB的本机内存。大量线程会占用大量内存。
还有Native Memory Tracking (NMT) in the JVM。这可能有助于检查JVM本身是否正在耗尽本机内存。
jattach tool也可以在容器化(docker)环境中用于触发来自主机的threaddumps或heapdumps。它还能够运行控制NMT所需的jcmd命令。