代码之家  ›  专栏  ›  技术社区  ›  Matt McHenry

为什么Java堆的最大大小是固定的?

  •  32
  • Matt McHenry  · 技术社区  · 16 年前

    信息技术 is not possible 在VM启动后增加Java堆的最大大小。技术原因是什么?垃圾收集算法是否依赖于使用固定数量的内存?还是出于安全原因,通过消耗所有可用内存来防止Java应用程序拒绝系统上的其他应用程序?

    5 回复  |  直到 9 年前
        1
  •  23
  •   Bill the Lizard    14 年前

    据我所知,在Sun的JVM中,整个堆必须分配在一个连续的地址空间中。我认为,对于较大的堆值,在启动后很难添加到地址空间中,同时确保地址空间保持连续。您可能需要在启动时获得它,或者根本不需要。因此,它是固定的。

    即使没有立即全部使用,整个堆的地址空间也会在启动时保留。如果它不能为传递给它的-Xmx值保留足够大的连续地址空间块,它将无法启动。这就是为什么很难分配>1.4GB堆在32位Windows上-因为很难找到这样大小或更大的连续地址空间,因为有些DLL喜欢在某些地方加载,从而使地址空间碎片化。当您使用64位时,这并不是一个真正的问题,因为有更多的地址空间。

    这几乎肯定是出于性能原因。我找不到一个很好的链接来详细说明这一点,但这里引用了彼得·凯斯勒(Peter Kessler)的话( full link -请务必阅读我在搜索时找到的注释)。我相信他在Sun的JVM上工作。

    我们需要连续内存的原因 一堆边数据结构 按与基准面的偏移量(缩放)编制索引 堆的开始。例如,我们 使用 具有一个字节的“卡标记数组” 对于堆的每个512字节。当我们 在我们拥有的堆中存储一个引用 要在中标记相应的字节,请执行以下操作: 卡片标记阵列。我们右移 存储和存储的目标地址 有趣的算术游戏 在Java中不能做到你能做到的

        2
  •  8
  •   x4u    16 年前

    历史上,这种限制有一个原因,那就是不允许浏览器中的小程序占用所有用户的内存。从来没有这样限制的微软虚拟机实际上允许这样做,这可能导致对用户计算机的某种拒绝服务攻击。就在一年前,Sun在1.6.0 Update 10 VM中引入了一种方法,让小应用程序指定它们需要多少内存(限制在物理内存的某个固定份额),而不是总是将它们限制在64MB,即使在具有8GB或更多可用内存的计算机上也是如此。

    现在,由于JVM已经进化,当VM不在浏览器中运行时,应该可以摆脱这个限制,但是Sun显然从未考虑过这样的高优先级问题,尽管已经提交了许多错误报告,最终允许堆增长。

        3
  •  4
  •   Yishai    16 年前

    我认为简短、刺耳的答案是因为Sun觉得它不值得花时间和成本来开发。

    这种特性最引人注目的用例是在桌面上,IMO,而Java在启动JVM的机制上一直是桌面上的一场灾难。我怀疑那些对这些问题想得最多的人倾向于关注服务器端,并查看最好留给本机包装器的任何其他细节。这是一个不幸的决定,但在为应用程序选择合适的平台时,这应该只是决定点之一。

        4
  •  3
  •   Michael Wiles    16 年前

    我的直觉是,它与操作系统上运行的其他应用程序的内存管理有关。

    例如,如果您将最大堆大小设置为机箱上的RAM量,那么实际上可以让VM决定它需要多少内存(达到此限制)。这样做的问题是,虚拟机可能会有效地削弱它正在运行的机器,因为在它决定需要进行垃圾收集之前,它将接管机器上的所有内存。

    还要注意,它们是关于内存的两个值,即“当前堆大小”和“最大堆大小”。当前堆大小是堆大小当前使用的内存量,如果需要更多内存,则可以调整堆大小,但不能将堆大小调整到最大堆大小的值以上。

        5
  •  2
  •   Community Mohan Dere    6 年前

    来自IBM的 performance tuning tips (因此可能不直接适用于Sun的虚拟机)

    JVM具有用于管理JVM存储的阈值。当达到阈值时,将调用垃圾收集器以释放未使用的存储。因此,垃圾收集可能会导致Java性能显著降低。在更改初始堆和最大堆大小之前,应考虑以下信息: 小心使初始堆大小过大。虽然较大的堆大小最初通过延迟垃圾收集来提高性能,但较大的堆大小最终会影响垃圾收集最终启动时的响应时间,因为收集过程需要更多的时间。

    所以,我猜您不能在运行时更改该值的原因是因为它可能没有帮助:要么堆中有足够的空间,要么没有。一旦用完,将触发GC循环。如果这还不能腾出空间,你就已经吃饱了。您需要捕获OutOfMemoryException,增加堆大小,然后重试计算,希望这次您有足够的内存。

    通常,除非您需要,否则VM不会使用最大堆大小,因此,如果您认为可能需要在运行时扩展内存,可以只指定一个较大的最大堆大小。

    我承认这有点不令人满意,而且似乎有点懒惰,因为我可以想象一种合理的垃圾收集策略,当GC无法释放足够的空间时,它会增加堆的大小。不过,我的想象力是否转化为高性能GC实现是另一回事;)