JVM堆参数

67

在阅读了关于此主题的已经提出的问题以及进行了大量搜索之后,我仍然无法清楚地看到 -Xms 选项的区别。

我的问题是: java -Xms=512m -Xmx=512mjava -Xms=64m -Xmx=512m 之间有什么区别?

现在我有以下答案:

唯一的区别在于应用程序运行期间运行的垃圾回收次数和内存分配次数。我是正确的吗?

以下是我这个答案的原因:

-Xms 选项设置为 512m 并不会导致我的应用程序在启动后真正使用 512M 物理内存。我猜这与现代操作系统虚拟内存管理和惰性页面分配有关。(我注意到将 -Xms 设置为 512M64M 对于 Linux 上的 top 或 Windows 上的任务管理器报告的初始使用内存没有任何影响)

有人可以帮我理解这个 Xms 选项的影响或指向可以帮助我理解它的链接吗?

6个回答

38

JVM将以初始堆级别的内存使用启动。如果最大堆高于此级别,则当内存要求超过其当前内存时,它将增长到最大堆大小。

因此,

  • -Xms512m -Xmx512m

JVM从512M开始,永远不会调整大小。

  • -Xms64m -Xmx512m

JVM从64M开始,如果内存要求超过64,则增长(最高上限为512)。


我知道这一点。但正如我在之前的评论中所说,“Java将从初始堆级别开始使用内存”究竟是什么意思?由于我的512兆字节系统能够启动其他几个应用程序而没有交换并且报告我Java应用程序只使用了几兆字节,因此这些内存似乎没有被有效分配。 - Manuel Selva
4
通常情况下,如果您知道启动应用程序时需要消耗特定数量的内存,则会将初始堆大小设置为较大值。这可以防止JVM在启动时多次重新调整堆大小,从而节省执行时间。这种优化方法很常见,可以使Eclipse运行更快,避免昂贵的分配周期。 - dhable
谢谢你的回答,让我想到了另一个问题: 将Xms值设置为与Xmx相同的值是否存在“风险”或缺点? - Manuel Selva
2
据我理解,唯一的风险就是您分配了太多的虚拟内存而导致内存不足 - 但如果您的应用程序确实需要该内存,那么这种情况无论如何都会发生。 - Turismo
2
我认为真正的语法是-Xms64m而不是-Xms=64m。如果我错了,请回滚! - Burkhard

34

总结该链接后找到的信息: JVM分配由-Xms指定的内存量,但通常在需要时操作系统才分配真正的物理页面。因此,JVM按照Xms指定的虚拟内存分配,但只在需要时分配物理内存。

您可以使用Sysinternals的Process Explorer而不是Windows任务管理器来查看此情况。

因此,使用-Xms64M和-Xms512M之间存在实际差异。 但我认为最重要的区别就是您已经指出的:如果您确实需要512MB但仅从64MB开始,则垃圾收集器将更频繁地运行。


13
我认为真正的语法是-Xms64m而不是-Xms=64m。如果我错了,请回滚! - Burkhard
2
在研究JVM配置的一些细节时遇到了这个。这里接受的答案与我对这些值的理解非常不同。是的,大多数操作系统只有在触发次要页面错误时才会使物理页面可用,因为应用程序尝试访问其已分配的内存 - 但Xms指定了JVM分配的初始内存量 - 这将放置一个上限,用于分配不需要应用程序分配更多内存的页面数量 - 在Java的情况下,仅在GC没有提供足够的内存时,它才会分配更多的内存(最多到Xmx)。 - symcbean
@symcbean 我有点困惑。似乎你的理解与答案中的并没有太大区别。你只是用了不同的词汇来描述完全相同的行为。 - Turismo
内存使用的上限由Xmx设置,而不是Xms。 - symcbean
2
@symcbean 这个问题是关于不同的Xms值之间的不同行为以及为什么操作系统没有显示出差异。根据我的回答(并在您的评论中得到确认),操作系统只分配所需的物理内存,而不是应用程序最初分配的整个内存。Java将分配更多的内存直到达到Xmx中的值这一事实从未受到质疑。 - Turismo

15
除了标准的堆参数-Xms-Xmx外,还需要了解-XX:PermSize-XX:MaxPermSize,它们用于指定Perm Gen空间的大小。因为即使在堆的其他代中有空间,如果Perm Gen空间已满,内存也会耗尽。此链接还提供了一些重要的JVM参数的概述。important JVM parameters

5
JVM会自适应地调整堆大小,它会尝试找到最适合您的应用程序的最佳堆大小。-Xms和-Xmx只是指定JVM可以操作和调整堆的范围。如果-Xms和-Xmx具有相同的值,则JVM的堆大小将保持在该值不变。
通常最好只设置-Xmx,让JVM找到最佳的堆大小,除非您有特定的原因需要在JVM启动时给它一个大堆。
至于JVM实际何时从操作系统请求内存,我认为这取决于平台和JVM的实现。我想象它不会在您的应用程序实际需要内存之前请求内存。-Xmx和-Xms只是保留内存。

4
如果您写了: -Xms512m -Xmx512m 当它启动时,Java会在那个时刻为其进程分配512MB的RAM,并且无法增加。
-Xms64m -Xmx512m 当它启动时,Java仅为其进程分配64MB的RAM,但Java可以在占用512MB内存的同时增加其内存占用量。
我认为第二种情况更好,因为它为Java提供了自动内存管理。

我知道。但是“Java在那些时刻分配512m”究竟意味着什么?由于我的512m字节系统能够启动其他几个应用程序而没有交换并且报告我只有少量兆字节被我的Java应用程序使用,因此这个内存似乎没有有效地分配。 - Manuel Selva
1
为什么这个被踩了?除了最后一句可能不正确之外,它在技术上是正确的。在高性能的世界中,通常更明智的做法是使用一个单独的brk()让Java分配所有需要的内存。在其他情况下,随用随分配内存可能更明智。 - Fredrik
我没有自己对这个答案进行负面评价。关于您的评论,我有一个与我向丹提出的问题相同的问题:将Xms值设置为与Xmx相同的值是否存在任何“风险”或缺点?谢谢Manu - Manuel Selva
2
将它们设置为相同的值是实现最佳性能的推荐方式。缺点是您需要更加小心,以免将它们设置得太高,因为这样可能会影响系统的整体性能。 - Fredrik
1
我认为真正的语法是-Xms64m而不是-Xms=64m。如果我错了,请回滚! - Burkhard

0

我在 scala 中创建了这个玩具示例,my_file.scala

object MyObject {

    def main(args: Array[String]) {
        var ab = ArrayBuffer.empty[Int]

        for (i <- 0 to 100 * 1000 * 1000) {
            ab += i
            if (i % 10000 == 0) {
                println("On : %s".format(i))
            }
        }
    }
}

我使用以下方式运行它:

scala -J-Xms500m -J-Xmx7g my_file.scala

并且

scala -J-Xms7g -J-Xmx7g my_file.scala

-Xms500m版本中,肯定会出现明显的暂停。我确定短暂停顿是垃圾回收运行,而长暂停则是堆分配。


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