Java进程内存比指定的限制要大得多

9
我已经调查了大部分可用的方法来找出Java进程实际使用了多少内存。 到目前为止,我可以说我知道总共分配的内存可能是以下一项或多项:
堆内存(应该由-XX:MaxHeapSize = 4096m控制)
永久内存(应该由-XX:MaxPermSize = 1024m控制)
保留代码缓存(应该由-XX:ReservedCodeCacheSize = 256m控制)
N个线程*线程大小(应该由-XX:ThreadStackSize = 1024控制)
但是,我发现通过所有我找到的方法来获取Java进程的内存消耗和Linux告诉我的结果相差太大。
在我的情况下,这是一个位于Ubuntu 11.10 x86_64机器上运行的Tomcat实例,JVM 1.6_u26 64位。ps -ALcf | grep org.apache.catalina.startup.Bootstrap | wc -l告诉我有145个线程或进程正在运行,并且与同一根进程(Tomcat)链接。
所有这些加起来应该给我最大总内存: (4096MB)+(1024MB)+(256MB)+ 145 *(1024KB)= 5521MB。 jmap -heap PID告诉我的内容,ManagementFactory.memoryMXBean。(heapMemoryUsage + nonHeapMemoryUsage).getCommitted()告诉我的内容和理论值都是相等的。
现在到Linux方面,top和nmon都告诉我,这个进程分配的ResidentMemory是5.8GB->大约5939.2 MB。但是我也知道这只是内存的一部分,即在实时RAM内存中的部分。top的VIRT和nmon的Size(两者都应该代表相同)告诉我,该进程为7530MB(或者精确地说是7710952KB通过nmon)。 这与预期的最大值相差太大:比最大值大了2009MB,并且根据jmap和jstat,堆内存分配甚至没有达到峰值(2048-OldSpace + 1534-Eden_+_Survivors)。
top还告诉我代码栈为36KB(对于初始catalina starter来说很好),数据栈为7.3GB(表示其他部分)。
这个Tomcat服务器实例是唯一在这台机器上运行的,并且出现了一些不稳定的情况。每三天左右需要重新启动,因为机器只有7647544k可用内存,而没有交换空间(出于性能原因)。我计算了限制条件,希望进程遵循它们,我发现这是一个非常好的安全边际,适用于在机器上运行的所有其他服务(除了ssh和top本身,不应该有其他麻烦):7468-5521=1947。这几乎是一个过高的“安全边际”。
所以,我想了解所有那些内存被使用了,并且为什么没有遵守限制。如果缺少任何信息,我将很乐意提供。

在您的顶部分解中,您忘记提到JNI分配堆外存储器。您的应用程序是否使用JNI? - Yoni Roit
我不知道是否有一种确定的方法(有吗?),但根据我所了解的整个应用程序,我会认为它不是这样。 至少我可以保证没有显式使用它。 应用程序连接到外部数据库(不在同一台机器上),不知道是否直接使用JNI。 无论如何,整个池的缓存位于另一侧,即数据库端,因此不应计入。 - fredgalvao
可能是重复的问题:在Ubuntu上限制JVM进程内存 - user177800
RSS与Java堆不同。VIRT包括mmaped文件。 - J-16 SDiZ
2个回答

5

简而言之,JVM使用的内存比-Xms-Xmx等命令行参数提供的内存多。

这里有一篇非常详细的文章介绍了JVM如何分配和管理内存,它并不像你在问题描述中预期的那么简单,值得仔细阅读。

在许多实现中,ThreadStack大小有最小限制,这些限制因操作系统和JVM版本而异;如果将限制设置低于JVM或操作系统的本机限制,则会忽略ThreadStack设置(在*nix上必须有时要设置 ulimit)。其他命令行选项也是这样工作的,当提供的值过小时,它们会默默地默认为更高的值。不要假设传递的所有值都代表实际使用的值。

类加载器(Tomcat有一个以上)消耗了很多难以记录的内存。JIT使用了大量内存,以时间换空间,这通常是一个很好的折衷方案。

你引用的数字非常接近我的预期。


1
感谢您的澄清和提供的链接,但我仍然无法接受额外36%的内存,因为我无法直接控制它,这不符合我的期望。无论如何还是非常感谢,现在我有一些提示可以通过对Java进程进行其他配置来降低这个数字。除此之外,您对XX:MaxDirectMemorySize有什么建议吗? - fredgalvao
调整那个设置可能会引起更多问题,而不是解决问题。默认值为64MB(https://dev59.com/K2865IYBdhLWcg3whe9f),相对于JVM的其余部分,特别是基于Tomcat的应用程序来说,这只是微不足道的数量。 - user177800

3
虚拟内存是使用的地址空间,由于您使用的是64位应用程序,我不会太担心这个问题。驻留内存是实际使用的主存量。
您可以将以下内容添加到列表中:
- 共享库,包括JVM。~0.5G - 直接内存(0到4G)的最大值默认与堆最大值相同。 - 内存映射文件。没有限制。
您可以运行"pmap"命令来查看用于哪些目的使用了多少地址空间。
顺便说一下,我有一个进程在"top"中显示640G虚拟内存,原因是内存映射文件。它的大小可能比驻留大小要高得多。 ;)

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