Tomcat内存消耗超过堆+ permgen空间

我观察到操作系统所说的内容与jVisualVM所说的内容之间的Tomcat RAM消耗不匹配。

从htop开始,Tomcat JVM拥有993 MB的驻留内存

从jVisualVM,Tomcat JVM正在使用

  • 堆最大值:1,070,399,488 B
  • 堆大小: 298.438.656 B
  • 堆使用:可变,介于170MB和270MB之间
  • PermGen Max:268,435,456 B
  • PermGen尺寸: 248,872,960 B
  • PermGen使用:略有变化,约150MB

从我的理解,操作系统内存消耗应该是堆大小+ PermGen大小〜= 522 MB。 但这比我观察到的要少471 MB

任何人都知道我在这里错过了什么?

PS:我知道我的最大堆远远高于使用的,但我认为如果JVM不使用它(即堆大小较低)应该没有效果。

谢谢! 渣子

从我的理解,操作系统内存消耗应该是堆大小+ PermGen大小〜= 522 MB。 但这比我观察到的要少471 MB。 任何人都知道我在这里错过了什么?

如果我理解你所看到的问题是内存碎片和其他领域的JVM内存开销的结合。 我们经常看到生产程序的内存使用量是我们从内存设置中看到的2倍。

内存碎片可能意味着虽然JVM认为操作系统已经给它一些字节数,但由于内存子系统的优化,必须给出一定的额外字节数。

就JVM开销而言,标准内存配置中不包含许多其他存储区域。 这是一个很好的讨论 。 去引用:

以下是不属于垃圾收集堆的事物的示例,但它们是进程所需的内存的一部分:

  • 用于实现JVM的代码
  • 用于实现JVM的数据结构的C手动堆
  • 系统中所有线程的堆栈(app + JVM)
  • 缓存的Java字节码(用于库和应用程序)
  • JITed机器代码(用于库和应用程序)
  • 所有已加载类的静态变量

我们要记住的第一件事是: JVM process heap (OS process) = Java object heap + [Permanent space + Code generation + Socket buffers + Thread stacks + Direct memory space + JNI code + JNI allocated memory + Garbage collection] ,在这个“集合”中,permSpace通常是最大块。

鉴于此,我想这里的关键是JVM选项-XX:MinFreeHeapRatio=n ,其中n是0到100,它指定如果堆的剩余空间少于n% ,则应该扩展堆。 默认情况下它通常为40(Sun),因此当JVM分配内存时,它足以获得40%的空闲( 如果你有-Xms == -Xmx则不适用 )。 它的“双选项”,-XX:MaxHeapFreeRatio通常默认为70(太阳)。

因此,在Sun JVM中,每个垃圾收集中的活动对象的比率保持在40-70%之间。 如果在GC之后少于40%的堆是空闲的,则扩展堆。 因此,假设您正在运行Sun JVM,我猜测“java对象堆”的大小已经达到大约445Mb的峰值,因此产生了大约740 Mb的扩展“对象堆”(以保证40%免费) 。 然后,(对象堆)+(perm空间)= 740 + 250 = 990 Mb。

也许您可以尝试输出GC详细信息或使用jconsole来validation堆大小的演变。

PS:在处理这样的问题时,最好发布操作系统和JVM细节。

在应用程序启动期间,JVM将保留大致等于堆大小值(-Xmx)大小的内存以及其他内容的内存。 这可以防止JVM以后返回操作系统以保留更多内存。

即使您的应用程序仅使用298mb的堆空间,仍然会为操作系统保留993mb。 您需要阅读更多有关保留与已提交内存的内容。

在谈论垃圾收集时,您将阅读的大多数文章将从堆透视而不是操作系统级别引用分配。 通过在启动时为应用程序保留内存,垃圾收集可以在自己的空间中工作。

如果您需要更多详细信息,请阅读文章Tuning Garbage Collection以下是文档中的一些重要内容

在初始化时,除非需要,否则实际上保留了最大地址空间但未分配给物理内存。

另请参阅文件中的第3.2(iv)节

在初始化虚拟机时,将保留堆的整个空间。 可以使用-Xmx选项指定保留空间的大小。 如果-Xms参数的值小于-Xmx参数的值,则不会立即将所有保留的空间提交给​​虚拟机。

操作系统将报告JVM使用的内存+程序使用的内存。 因此它总是高于JVM报告的内存使用量。 JVM本身需要一定量的内存才能执行程序,操作系统无法区分。

不幸的是,使用系统内存工具并不是一种非常精确的跟踪程序内存消耗的方法。 JVM通常会分配大块内存,因此对象创建很快,但这并不意味着您的程序正在消耗该内存。

了解程序实际执行的更好方法是运行jconsole并查看其中的内存使用情况。 这是一个非常简单的工具,用于查看易于设置的内存。