为什么修复了Java堆的最大大小?

时间:2010-01-21 13:55:47

标签: java garbage-collection jvm heap

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

5 个答案:

答案 0 :(得分:23)

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

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

由于性能原因,这几乎可以肯定。我找不到一个很好的链接,详细说明了这一点,但这是一个非常好的引用来自Peter Kessler(full link - 一定要阅读我在搜索时发现的评论。我相信他在Sun的JVM上工作。

  

我们需要连续记忆的原因   堆的区域是我们有的   一堆副数据结构   索引(缩放)偏移量   堆的开始。例如,我们   使用a跟踪对象引用更新   “卡标记数组”,有一个字节   对于每512个字节的堆。什么时候我们   在我们拥有的堆中存储引用   标记中的相应字节   卡片阵列。我们正确地转移了   商店的目的地址和   用它来索引卡片标记数组。   有趣的解决算术游戏你   不能用Java做的(有   to :-)在C ++中玩。

这是在2004年 - 我不确定自那时以来发生了什么变化,但我很确定它仍然存在。如果您使用Process Explorer之类的工具,您可以看到Java应用程序的虚拟大小(添加虚拟大小和专用大小内存列)包括从启动点开始的总堆大小(加上其他所需空间) ,即使进程的“使用”内存在堆附近开始填满之前也没有...

答案 1 :(得分:8)

历史上存在这种限制的原因,这不允许浏览器中的Applet占用所有用户内存。从未有过这种限制的Microsoft VM实际上允许这样做,这可能导致对用户计算机的某种拒绝服务攻击。就在一年前,Sun在1.6.0 Update 10 VM中引入了一种方法,让applet指定他们想要多少内存(仅限于物理内存的某个固定份额),而不是总是将它们限制为64MB甚至在计算机上有8GB或更多可用的。

现在,自从JVM发展以来,当VM没有在浏览器中运行时应该可以摆脱这种限制,但是Sun显然从未认为它是如此高优先级的问题,即使已经有很多错误报告提交最终允许堆增长。

答案 2 :(得分:4)

我认为简短而狡猾的回答是因为Sun没有发现它值得花时间和成本来开发。

这个功能最引人注目的用例是桌面,IMO和Java在启动JVM的机制方面一直是桌面上的灾难。我怀疑那些最关心这些问题的人倾向于关注服务器端并查看最好留给本机包装器的任何其他细节。这是一个不幸的决定,但在决定申请的正确平台时,它应该只是决策点之一。

答案 3 :(得分:3)

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

如果将最大堆大小设置为例如盒子上的​​RAM量,则可以让VM有效地决定它需要多少内存(达到此限制)。这个问题是VM可以有效地削弱它运行的机器,因为它会在决定需要垃圾收集之前接管盒子上的所有内存。

当您指定最大堆大小时,您对VM说的是,您可以在需要开始垃圾收集之前使用此内存量。你不能拥有更多,因为如果你花费更多,那么盒子上运行的其他应用程序将会变慢,如果使用的数量超过此值,你将开始交换到磁盘。

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

答案 4 :(得分:2)

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

  

Java堆参数会影响垃圾回收的行为。增加堆大小支持更多对象创建。因为大堆需要更长的时间来填充,所以应用程序在垃圾收集发生之前运行的时间更长。但是,更大的堆也需要更长的时间来压缩并导致垃圾收集需要更长的时间。

     

JVM具有用于管理JVM存储的阈值。达到阈值时,将调用垃圾收集器以释放未使用的存储。因此,垃圾收集会导致Java性能显着下降。在更改初始和最大堆大小之前,您应该考虑以下信息:   在大多数情况下,您应将最大JVM堆大小设置为高于初始JVM堆大小的值。这允许JVM在初始堆的范围内的正常,稳定状态期间有效地操作,但是在高事务量期间通过将堆扩展到最大JVM堆大小来有效地操作。在极少数需要绝对最佳性能的情况下,您可能希望为初始和最大堆大小指定相同的值。这将消除JVM需要扩展或收缩JVM堆大小时发生的一些开销。确保该区域足以容纳指定的JVM堆。   注意使初始堆大小过大。虽然大堆大小最初通过延迟垃圾收集来提高性能,但是大堆大小最终会影响垃圾收集最终启动时的响应时间,因为收集过程需要更多时间。

所以,我猜你在运行时无法更改值的原因是因为它可能无济于事:要么堆中有足够的空间,要么没有。一旦用完,将触发GC循环。如果这不能释放空间,那么无论如何你都会被塞满。您需要捕获OutOfMemoryException,增加堆大小,然后重试计算,希望这次你有足够的内存。

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

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