【问题标题】:Java process memory is much bigger than specified limitsJava 进程内存远大于指定的限制
【发布时间】:2012-06-04 19:27:03
【问题描述】:

我已经研究了大多数可用的方法来找出一个 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 机器上的 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 端,topnmon 都告诉我这个进程分配的 ResidentMemory 是 5.8GB -> 大约 5939,2MB。但我也知道这只是内存的一部分,是实时 RAM 内存中的一部分。 top 的 VIRT 和 nmon 的 Size(两者应该代表相同)告诉我该进程是 7530MB(或 nmon 准确地说是 7710952KB)。 这与预期的最大值相差太大:比最大值高出 2009MB,根据 jmap 和 jstat,堆内存分配甚至没有达到峰值(2048-OldSpace + 1534-Eden__Survivors)。

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

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

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

【问题讨论】:

  • 在顶部的细分中,您忘记提到 JNI 在堆外分配内存。您正在分析的应用程序是否可能正在使用 JNI?
  • 我不知道是否有办法确定(有吗?),但我认为这不是我对整个应用程序的了解。至少我可以保证没有明确使用它。应用程序连接到外部数据库(不在同一台机器上),不知道是否可以直接使用 JNI。无论如何,整个池的缓存在另一边,在数据库端,所以不应该算在内。
  • RSS 与 Java 堆不同。 VIRT 包含映射文件

标签: java linux memory process


【解决方案1】:

Plain and simple the JVM uses more memory than what is supplied in -Xms and -Xmx and the other command line parameters.

这是一个very detailed article on how the JVM allocates and manages memory,它并不像您对问题的假设所期望的那么简单,值得全面阅读。

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

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

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

【讨论】:

  • 感谢您的澄清和链接,但我仍然无法接受大约 36% 的额外内存,我无法直接将其控制为“我所期望的”。无论如何,再次感谢,我现在有一些关于如何根据我在这个 java 进程中所做的其他配置来降低这个数字的提示。除此之外,关于 XX:MaxDirectMemorySize 有什么要说的吗?
  • 搞乱该设置可能只会导致更多问题。 The default is 64MB 与 JVM 的其余部分相比微不足道,更不用说基于 Tomcat 的应用程序了。
【解决方案2】:

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

您可以添加到您的列表中

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

您可以通过pmap 向您展示有多少地址空间用于什么目的。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-06-19
    • 1970-01-01
    • 2011-04-25
    • 2013-03-29
    • 2011-01-24
    • 2011-12-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多