JVM的最大堆大小可以动态调整吗?

28

JVM的-Xmx参数可以设置JVM的最大堆大小,但是否有一种方法使该值动态变化?换句话说,我想告诉JVM:“看,如果需要,就从系统中获取RAM,直到系统用尽为止。”

提问的两个原因: 首先,涉及的应用程序根据用户操作使用的内存范围非常广泛,因此概念上的最小和最大值相差很远。其次,似乎JVM在启动时从虚拟内存中保留了最大堆空间。这个特殊的应用程序在各种硬件上运行,所以选择“一刀切”的最大堆空间很难,因为它必须足够低以在低端硬件上运行,但我们真的希望能够利用真正强大的机器。


1
可能是重复问题:https://dev59.com/J3RA5IYBdhLWcg3w9y50 - Vineet Reynolds
1
与以下相关:https://dev59.com/r3I95IYBdhLWcg3w-C9b - Vineet Reynolds
1
这些问题的区别在于意图; 我自己不想有任何关系,我只是希望JVM可以神奇地为我完成它。 :) - Electrons_Ahoy
好的,如果您选择默认设置,JVM会根据系统可用内存进行一些“魔法”。http://download.oracle.com/javase/6/docs/technotes/guides/vm/gc-ergonomics.html - Vineet Reynolds
以下是热点问题的RFE参考链接:http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4408373 - Luke Quinane
不...那太明智和灵活了。为什么要做有用的事情,当Java可以让你提前猜测呢? - Basic
4个回答

13

但是,有没有一种方法可以使该值动态变化?

实际上是不行的。最大堆大小在JVM启动时设置,并且无法增加。

实际上,您可以将最大堆大小设置为平台允许的最大值,并让JVM随着需要而增加堆大小。这样做存在明显的风险;即您的应用程序将使用所有内存并导致用户的计算机停顿。但这个风险是问题固有的一部分。

编辑

值得注意的是,有各种-XX... GC调优选项,可让您微调JVM扩展堆的方式(最多到最大值)。

另一个可能性是将应用程序分成两个部分。应用程序的第一部分完成确定“问题”大小所需的所有准备工作。然后它会计算出适当的最大堆大小,并在新的JVM中启动内存消耗量大的第二部分应用程序。

  • 如果应用程序可以按上述方式合理地进行分区,则此方法才有效。

  • 只有在可以计算问题大小的情况下,此方法才有效。在某些情况下,计算问题大小就等于计算结果。

  • 不清楚您是否会获得比仅允许堆增长到最大大小更好的整体性能。


1
我不想去翻旧账,但你能不能创建一个本地库,在完全hacky的方式下,通过realloc和设置适当的变量来调整堆大小? - Ryan Amos
这是不切实际的。首先,Java堆不是从malloc分配的空间中分配的。如果你能解决这个问题,你将遇到各种复杂情况。如果你真的想要改变最大堆大小的能力,你需要修改JVM/GC。这将是很多工作。 - Stephen C

7

它并不会。虽然它可能会且应该这样做:

-Xmx90%  // 90% of physical memory

然而,一个默认的隐式100%可能不是一个好主意。
使用非GC语言编写的程序会非常谨慎地管理其内存,它将尽快修剪任何垃圾。假设它负责及时处理垃圾,允许它获取任何请求的内存是有道理的。
GC语言则不同。它仅在必要时收集垃圾。只要还有空间,它就不关心垃圾是否存在。如果能够获得它想要的所有内存,它将获取计算机中的所有内存。
因此,GC程序员不再需要担心处理每个垃圾,但他仍然必须对可容忍的垃圾/活动对象比率有一个大致的了解,并使用-Xmx指令来指示GC。

只是补充一下,这个限制并不是GC语言固有的,只是Java在实现上的一个可疑选择的结果。 - Basic
1
微软的Java实现(很久以前)没有-Xmx的概念,可以分配所需的内存。 - Thorbjørn Ravn Andersen
Java 假设它是唯一运行的程序有点粗鲁,我不相信 GC 是不同的。任何语言,无论是否使用 GC,都没有理由简单地使用所有可用内存。认为动态适应太难了吗?那就运行 GC,检查有多少内存是“活着的”,然后当分配使用的内存加倍时再次运行 GC。问题解决了。 - Shane Day

3
这是一个JDK增强提案(JEP)8204088。 “动态最大内存限制”,建议引入“CurrentMaxHeapSize”:
引入一个新的动态用户定义变量 CurrentMaxHeapSize,用于动态限制已提交内存(即堆大小)的增长。该变量以字节为单位定义,限制堆的扩展大小。它可以在启动时设置并在运行时更改。无论何时定义,它的值必须始终等于或低于 MaxHeapSize(Xmx - 限制堆的增长的启动选项)。与 MaxHeapSize 不同,CurrentMaxHeapSize 可以在运行时动态更改。
预期的使用方式是使用非常保守的 Xmx 值(对内存占用影响很小)设置 JVM,然后使用 CurrentMaxHeapSize 动态限制堆的大小。
尽管没有迹象表明这个功能正在积极开发中,但它是一个相对较新的 JEP(来自2018年),因此我仍然会记在心里。
公司 Jelastic (jelastic.com) 已经为 G1 垃圾收集器制作了一个工作原型。
请查看http://mail.openjdk.java.net/pipermail/hotspot-gc-dev/2018-May/022077.html中的描述,以及OpenJDK的补丁列表http://cr.openjdk.java.net/~tschatzl/jelastic/cmx/

3

基本上,使用纯Java无法适应各种用户的硬件:这时一点shell / batch脚本可能会派上用场。

我在OS X和Linux上做到了这一点:我有一个小的bash shell脚本,负责根据应用程序运行的硬件找到正确的JVM参数,然后调用JVM。

请注意,如果您提供桌面Java应用程序,则可能希望使用像izpack这样的软件来为用户提供安装程序:

http://izpack.org

我不知道Java Web Start是否可以根据用户配置提供不同的JVM参数(可能不行,而且如果您计划提供专业外观的桌面应用程序,JWS确实非常糟糕)。

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