影响垃圾收集性能的两个最重要的因素是总可用内存和专用于年轻代的堆的比例。

总堆

影响垃圾收集性能的最重要因素是总可用内存。因为收集在代填满时发生,所以吞吐量与可用内存量成反比。

影响代大小的堆选项

许多选项会影响生成大小。图 4-1说明了堆中已提交空间和虚拟空间之间的区别。在虚拟机初始化时,为堆保留了整个空间。保留空间的大小可以用-Xmx选项指定。如果-Xms参数值小于参数值-Xmx,则并非所有保留的空间都会立即提交给虚拟机。未提交的空间在此图中标记为“虚拟”。堆的不同部分,即老年代和年轻代,可以根据需要增长到虚拟空间的极限。一些参数是堆的一部分与另一部分的比率。例如,该参数–XX:NewRatio表示老年代与年轻代的相对大小。
影响垃圾收集性能的因素 - 图1
图 4-1 堆选项

堆大小的默认选项值

默认情况下,虚拟机会在每个集合处增大或缩小堆,以尝试将每个集合处的可用空间与活动对象的比例保持在特定范围内。此目标范围由选项-XX:MinHeapFreeRatio=和-XX:MaxHeapFreeRatio=设置为百分比,并且总大小的下界–Xms和上界为–Xmx。64 位 Solaris 操作系统(SPARC 平台版)的默认选项如表 4-1所示。

表 4-1 64 位 Solaris 操作系统的默认选项

选项 默认值
-XX:MinHeapFreeRatio 40
-XX:MaxHeapFreeRatio 70
-Xms 6656 KB
-Xmx calculated

使用这些选项,如果一代中的可用空间百分比低于 40%,则该代将扩展以保持 40% 的可用空间,直至该代的最大允许大小。类似地,如果空闲空间超过 70%,那么生成会收缩,因此只有 70% 的空间是空闲的,受限于生成的最小大小。

如表 4-1中所述,默认的最大堆大小是由 JVM 计算的值。Java SE 中用于并行收集器和服务器 JVM 的计算现在用于所有垃圾收集器。部分计算是 64 位平台的最大堆大小上限。请参阅并行收集器默认堆大小。客户端 JVM 有类似的计算,这导致最大堆大小小于服务器 JVM。

以下是有关服务器应用程序堆大小的一般准则:

  • 除非您遇到暂停问题,否则请尝试为虚拟机授予尽可能多的内存。默认大小通常太小。
  • 通过从虚拟机中删除最重要的大小调整决策,将-Xms和设置-Xmx为相同的值可提高可预测性。但是,如果您做出了错误的选择,虚拟机将无法进行补偿。
  • 通常,随着处理器数量的增加而增加内存,因为分配可以并行进行。

通过最小化 Java 堆大小来保护动态占用空间

如果您需要最小化应用程序的动态内存占用(执行期间消耗的最大 RAM),则可以通过最小化 Java 堆大小来实现。Java SE Embedded 应用程序可能需要这样做。

通过使用命令行选项和降低选项-XX:MaxHeapFreeRatio(默认值为 70%)和-XX:MinHeapFreeRatio(默认值为 40%)的值来最小化 Java 堆大小。降低至低至 10%,并已显示成功减少堆大小而不会造成太多性能下降;但是,结果可能会因您的应用程序而有很大差异。为这些参数尝试不同的值,直到它们尽可能低,但仍保持可接受的性能。此外,您可以指定-XX:-ShrinkHeapInSteps,这会立即将 Java 堆减小到目标大小(由参数指定-XX:MaxHeapFreeRatio)。使用此设置可能会导致性能下降。默认情况下,Java 运行时会逐步将 Java 堆减小到目标大小;这个过程需要多个垃圾回收周期。

年轻一代

在总可用内存之后,影响垃圾收集性能的第二大因素是专用于年轻代的堆的比例。年轻代越大,小回收发生的频率就越低。然而,堆的大小是有界的,较大的年轻代意味着较小的老年代,这将增加major收集的频率。最佳选择取决于应用程序分配的对象的生命周期分布。

年轻代大小选项

默认情况下,年轻代大小由选项控制-XX:NewRatio。例如,设置-XX:NewRatio=3意味着年轻代和年老代的比例为 1:3。换句话说,Eden区和幸存者空间的总大小将是总堆大小的四分之一。选项-XX:NewSize和-XX:MaxNewSize约束年轻代大小从下到上。将这些设置为相同的值会修复年轻代,就像设置-Xms和设置-Xmx为相同的值会修复总堆大小一样。这对于以比 -XX:NewRatio 允许的整数倍更细的粒度调整年轻代很有用。

幸存者空间大小

您可以使用该选项-XX:SurvivorRatio来调整幸存者空间的大小,但这通常对性能并不重要。例如,-XX:SurvivorRatio=6将 eden 和 Survivor 空间之间的比率设置为 1:6。换句话说,每个幸存者空间将是伊甸园大小的六分之一,因此是年轻代大小的八分之一(不是七分之一,因为有两个幸存者空间)。如果幸存者空间太小,那么复制集合会直接溢出到老年代。如果幸存者空间太大,那么它们就是无用的空。在每次垃圾回收时,虚拟机都会选择一个阈值数,即对象在变老之前可以来回交换复制的次数。选择这个阈值是为了让幸存者保持半满。你可以使用日志配置-Xlog:gc,age来显示这个阈值和新生代对象的年龄。它对于观察应用程序的生命周期分布也很有用。

表 4-2 Survivor Space Sizing 的默认选项值

选项 服务器JVM默认值
-XX:NewRatio 2
-XX:NewSize 1310 MB
-XX:MaxNewSize 不限
-XX:SurvivorRatio 8

年轻代的最大大小由总堆的最大大小和该-XX:NewRatio参数的值计算得出。默认的 -XX:MaxNewSize 意味着计算值不受限制,除非在-XX:MaxNewSize在选项上指定了值。

以下是服务器应用程序的一般准则:首先确定您可以为虚拟机提供的最大堆大小。然后,针对年轻代大小的性能指标以找到最佳设置。请注意,最大堆大小应始终小于机器上安装的内存量,以避免过多的页面错误和抖动。如果总堆大小是固定的,那么增加年轻代大小需要减少老年代大小。保持老年代足够大,以容纳应用程序在任何给定时间使用的所有实时数据,再加上一些空闲空间(10% 到 20% 或更多)。受之前对老年代的约束:给予年轻代足够的内存。随着处理器数量的增加,增加年轻代的大小,因为分配可以并行化。