9

我已经研究了大多数可用的方法来找出一个java进程真正使用了多少内存。到目前为止,我可以说我知道分配的总内存可能是以下一项或多项:

  • 堆内存(据说由我的 -XX:MaxHeapSize=4096m 控制)
  • 永久内存(据说由我的 -XX:MaxPermSize=1024m 控制)
  • 保留代码缓存(应该由我的 -XX:ReservedCodeCacheSize=256m 控制)
  • N of Threads * Thread Size (应该由我的 -XX:ThreadStackSize=1024 控制)

但是结果与 linux 告诉我的差别太大了,我发现任何一种方法都可以用来获取 java 进程的内存消耗。

在我的情况下,它是在 Ubuntu 11.10 x86_64 机器、JVM 1.6_u26 64 位上运行的 Tomcat 实例,它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,2MB。但我也知道这只是内存的一部分,是实时 RAM 内存中的一部分。VIRT bytop和 Size by nmon(两者应该代表相同)告诉我进程是 7530MB(或者确切地说是 7710952KB by nmon)。这与预期的最大值相差太大:高于最大值 2009MB,并且根据 jmap 和 jstat 堆内存分配甚至没有达到峰值(2048-OldSpace + 1534-Eden_+_Survivors)。

top还告诉我代码堆栈是 36KB(公平,对于初始 catalina 启动器),数据堆栈是 7.​​3GB(代表其余部分)。

这个 tomcat 服务器实例是唯一在这台机器上运行的实例,并且一直处于不稳定状态。需要每三天左右重新启动一次,因为机器有 7647544k RAM 可用,并且没有交换(出于性能原因)。我对限制进行了数学计算,并期望进程遵循它们,我发现为机器上运行的所有其他服务留出相当好的安全余量(除了 ssh 和 top 本身之外,没有一个应该打扰):7468 - 5521 = 1947。这对于“安全边际”来说几乎是太多了。

所以,我想了解所有这些内存从哪里使用,以及为什么不遵守限制。如果缺少任何信息,我很乐意提供。

4

2 回答 2

5

简单明了,JVM 使用的内存比-Xms-Xmx和其他命令行参数中提供的更多。

这是一篇关于 JVM 如何分配和管理内存的非常详细的文章,它并不像您在问题中的假设所期望的那么简单,值得全面阅读。

许多实现中的 ThreadStack 大小都有最小限制,这些限制因操作系统和有时 JVM 版本而异;如果您将限制设置为低于 JVM 或操作系统的本机操作系统限制,则忽略线程堆栈设置(有时必须设置 *nix 上的 ulimit)。其他命令行选项的工作方式相同,当提供的值太小时,默认为更高的值。不要假设传入的所有值都代表实际使用的值。

类加载器和 Tomcat 不止一个,它们会占用大量不易记录的内存。JIT 会占用大量内存,以空间换时间,这在大多数情况下是一个很好的权衡。

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

于 2012-05-30T15:11:07.860 回答
3

虚拟内存是使用的地址空间量,我不会太在意,因为您有一个 64 位应用程序。驻留是实际使用的主内存量。

您可以添加到您的列表

  • 包括 JVM 在内的共享库。~ 0.5 克
  • direct memory (0 to 4G) 最大值默认与堆最大值相同。
  • 内存映射文件。没有限制。

你可以做一个pmap告诉你有多少地址空间被用于什么目的。

顺便说一句:由于内存映射文件,我有一个进程在顶部显示 640G VIRT。它可以比居民的大小高很多。;)

于 2012-05-30T15:13:30.610 回答