2010-01-21 140 views
33

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

+0

类似的问题:“为什么(星期日)JVM对内存使用一个固定的上限( -Xmx)?” - http://stackoverflow.com/questions/3358328/why-does-the-sun-jvm-have-a-fixed-upper-limit-for-memory-usage-xmx – sleske 2012-02-28 12:24:14

回答

23

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

即使它不是全部立即使用,为整个堆的地址空间在启动时保留。如果它不能为您传递的-Xmx的值保留足够大的连续地址空间块,则它将无法启动。这就是为什么在32位Windows上分配大于1.4GB的堆是很困难的 - 因为很难找到这种大小或更大的连续地址空间,因为某些DLL喜欢在某些地方加载,分割地址空间。因为地址空间非常多,所以当你使用64位时,这并不是一个问题。

这几乎肯定是出于性能的考虑。我无法找到一个很好的链接,详细说明这进一步,但这里是从彼得·凯斯勒一个相当不错的报价(full link - 一定要阅读注释),我发现搜索时。我相信他在Sun的JVM上工作。

我们需要为堆的连续存储器 区域的原因是,我们有一个 一束被 索引由(缩放)偏移量从 开始堆的侧的数据结构。例如,我们 跟踪与 “卡片标记阵列”,其具有用于每个512个字节堆的一个字节 对象引用的更新。当我们 存储在堆的参考,我们有 来标记 卡片标记阵列中的对应字节。我们右移商店的 目的地址和 使用该索引卡片标记阵列。 趣味算术解决游戏中,你 不能用Java做,你去(有 在C :-)发挥++。

这是在2004年 - 我不确定自那时以来发生了什么变化,但我确信它仍然成立。如果你使用像Process Explorer的一个工具,你可以看到,虚拟大小(添加虚拟大小和私人大小的内存列)的Java应用程序包括从启动点的总堆大小(加上其他所需的空间,毫无疑问) ,即使在过程中“拿来主义”将是没有在附近直到堆开始填满记忆...

4

我认为这个简短而尖锐的答案是因为Sun没有发现开发它所花费的时间和成本。

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

+0

如果你可以成长,一个非常合理的要求也能够缩小,而Sun JVM真的不喜欢放弃内存。 Microsoft JVM可以使用系统中的所有内存。 – 2010-01-21 15:32:03

+0

@托尔比约恩Ravn的安德森,它既可以收缩,虽然这是一个有点最近和不良记录的能力(以及一些在桌面上真的糟糕到已丢失)。请参阅-XX:MaxHeapFreeRatio和-XX:MinHeapFreeRatio http://stackoverflow.com/questions/763295/setting-jvm-heap-size-at-runtime/763305#763305 – Yishai 2010-01-21 16:03:22

+0

未记录的XX选项不计数:) – 2010-01-21 16:58:27

3

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

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

当您指定最大堆大小时,您对VM所说的内容是,在您需要开始垃圾收集之前,您可以使用此内存量。你不能有更多,因为如果你采取更多的话,那么运行在盒子上的其他应用程序将会减速,如果你使用的不止这些,你将开始交换到磁盘。

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

+0

我认为是服务器,那里有一个管理员在一个晴朗的答案谁了解如何调整JVM(或者至少您可以合理预期这一点)并设置适当的最大值以便与其他进程一起使用。然而,在桌面上你问一个用户去鼓捣配置文件和启动脚本,他们有没有经验或理解的东西,和Sun目前的答案是 - 用C编写 – Yishai 2010-01-21 15:11:07

+0

@Yishai一个发射器:太阳VM默认对于大多数应用程序来说相当合理的值,iirc的默认值也会根据可用RAM进行调整。对于默认值有问题的应用程序,安装脚本可以很容易地创建一个启动脚本(不是c启动程序),它可以设置另一个最大值。编辑:顺便说一句,我也不喜欢它,我相信如果他们想要,Sun可以删除它。 – Fredrik 2010-01-21 16:17:22

+0

@Fredrik,如果用户安装脚本后增加RAM他们的系统上运行?某些桌面应用程序希望在可用时使用更多可用RAM(例如,用于缓存)。 – Yishai 2010-01-21 18:36:08

2

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

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

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

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

一般来说,除非你需要它,所以,如果你认为你可能需要扩大在运行时内存,你可以只指定一个较大的最大堆大小VM将不使用的最大堆大小。

我承认这有点不尽人意,似乎有点懒,因为我可以想象一个合理的垃圾收集策略,当GC未能释放足够空间时会增加堆大小。尽管我的想象力转化为高性能的GC实现是另一个问题,但是;)

8

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

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

+1

你是对的,也有错误报告要求是:http://bugs.sun.com/view_bug.do?bug_id=4741914,http://bugs.sun.com/view_bug.do?bug_id=4408373 – 2010-01-21 16:29:28

+0

+1这是唯一对我有意义的答案,包括链接问题的答案。只需花上几个小时追逐与Crashplan一个问题,简单的修复是次数增加只是一个不起眼的参数,在一个不起眼的设置文件。谁选择了256MB的上限,让我的备份失败了几周而没有注意到,并且拥有充足的系统内存。 – 2014-09-20 12:38:53

相关问题