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并查看其中的内存使用情况。 这是一个非常简单的工具,用于查看易于设置的内存。