Java进程内存使用情况(jcmd vs pmap)

11

我有一个运行在Java 8中的Java应用程序,它位于Docker容器内。该进程启动了Jetty 9服务器,并部署了Web应用程序。传递了以下JVM选项:-Xms768m -Xmx768m

最近我注意到该进程消耗了大量的内存:

$ ps aux 1
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
app          1  0.1 48.9 5268992 2989492 ?     Ssl  Sep23   4:47 java -server ...

$ pmap -x 1
Address           Kbytes     RSS   Dirty Mode  Mapping
...
total kB         5280504 2994384 2980776

$ jcmd 1 VM.native_memory summary
1:

Native Memory Tracking:

Total: reserved=1378791KB, committed=1049931KB
-                 Java Heap (reserved=786432KB, committed=786432KB)
                            (mmap: reserved=786432KB, committed=786432KB) 

-                     Class (reserved=220113KB, committed=101073KB)
                            (classes #17246)
                            (malloc=7121KB #25927) 
                            (mmap: reserved=212992KB, committed=93952KB) 

-                    Thread (reserved=47684KB, committed=47684KB)
                            (thread #47)
                            (stack: reserved=47288KB, committed=47288KB)
                            (malloc=150KB #236) 
                            (arena=246KB #92)

-                      Code (reserved=257980KB, committed=48160KB)
                            (malloc=8380KB #11150) 
                            (mmap: reserved=249600KB, committed=39780KB) 

-                        GC (reserved=34513KB, committed=34513KB)
                            (malloc=5777KB #280) 
                            (mmap: reserved=28736KB, committed=28736KB) 

-                  Compiler (reserved=276KB, committed=276KB)
                            (malloc=146KB #398) 
                            (arena=131KB #3)

-                  Internal (reserved=8247KB, committed=8247KB)
                            (malloc=8215KB #20172) 
                            (mmap: reserved=32KB, committed=32KB) 

-                    Symbol (reserved=19338KB, committed=19338KB)
                            (malloc=16805KB #184025) 
                            (arena=2533KB #1)

-    Native Memory Tracking (reserved=4019KB, committed=4019KB)
                            (malloc=186KB #2933) 
                            (tracking overhead=3833KB)

-               Arena Chunk (reserved=187KB, committed=187KB)
                            (malloc=187KB) 

您可以看到,RSS(2.8GB)和VM本机内存统计显示的实际上有很大区别(1.0GB已提交,1.3GB保留)。

为什么会有这么大的差异?我知道RSS还显示了共享库的内存分配,但在对pmap详细输出进行分析后,我意识到这不是共享库的问题,而是由一些被称为[ anon ]结构的东西消耗了内存。为什么JVM会分配这么多匿名内存块呢?

我搜索并找到了以下主题:为什么JVM报告的已分配内存比Linux进程驻留集大小更大?然而,那里描述的情况是不同的,因为RSS显示的内存使用量少于JVM统计数据。我有相反的情况,无法找出原因。


我有同样的问题,我有一个使用Xmx 1.5g(Oracle Jvm)的Java进程,在TOP命令中看到它消耗了约3.1g的内存,但是当我在openjdk中运行相同的应用程序时,它只消耗了约2.3gb的内存(仍然高于分配的xmx),我还没有找到答案。如果您找到解决方案,请告诉我。谢谢。 - Benak Raj
3个回答

5

我曾经遇到过一个与我们的Apache Spark作业相似的问题:我们将应用程序提交为fat jar。分析线程转储后,我们发现Hibernate是罪魁祸首。我们在应用程序启动时加载Hibernate类,而实际上使用了java.util.zip.Inflater.inflateBytes来读取Hibernate类文件,这导致我们的本地常驻内存使用量超过1.5GB。有关此问题的一个bug已在Hibernate中提出(https://hibernate.atlassian.net/browse/HHH-10938?attachmentOrder=desc),评论中提供的补丁对我们有效。希望这可以帮助您。


我们正在使用Jetty,并部署包含所有依赖项的“fat” WAR。如果我们部署“thin” JAR并将所有依赖项复制到Jetty libs中,它就可以解决问题。 看起来大多数Web应用程序服务器都有类似的问题。 - Konrad
@Konrad,您能否解释一下“thin jar”和“复制所有依赖项”的含义?我正在使用Jetty并且遇到了高的非堆内存使用率。 - cppcoder
所谓“瘦JAR包”,是指它仅包含以下内容:
  • 编译好的类
  • 资源
而其他依赖项(所有其他JAR文件)都被复制到Jetty lib目录中。在这种情况下,当Jetty启动应用程序时,它不必在应用程序JAR / WAR内查找依赖项,也不必将其提取到某种临时目录中。它只需转到自己的lib目录,就可以找到依赖项。
- Konrad

2

1
NMT仅跟踪JVM管理的内存部分,不跟踪由本地第三方库或内存映射/直接字节缓冲区使用的内存。

1
你能推荐一种简单的方法来查看吗?我快速查看了进程内存转储,但没有发现任何可疑的东西。 - Konrad
visualvm可以监控DBB的使用情况。https://blogs.oracle.com/alanb/entry/monitoring_direct_buffers - the8472
我无法看到NIO映射/直接字节缓冲区的任何重要用途。您可以在此处查看直接缓冲区的内存使用情况:direct。对于映射缓冲区,它显示为0。 - Konrad
你有什么其他的想法可以检查吗? - Konrad
也许你应该发布你的pmap -x输出 - the8472

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