Java-8-垃圾收集调优指南3-调整分代大小(译)

分代的内存布局

有几个参数会影响分代的大小。图 4-1 “Heap Parameters” 图解了堆中 committed space 和 virtual space 之间的不同。虚拟机初始化时,会预留整个堆空间。预留的堆空间可以使用 -Xmx 选项指定。如果 -Xms 参数的值小于 -Xmx 参数的值,那么并非整个预留空间都立即被投入到虚拟机中。在该图中,未投入的空间(uncommitted space)被贴上 “virtual” 标签。堆的不同部分(新生代、老生代)可以根据需要增长至虚拟空间(virtual space)的界限内。

一些参数是堆中一部分与另一部分的比例。例如,参数 NewRatio 表示老生代对新生代的相对大小。

Figure 4-1 Heap Parameters

Description of “Figure 4-1 Heap Parameters”

该图由排成一行的六个矩形组成,从左到右依次被标记为:

1. Eden
2. Survivor
3. Survivor
4. Virtual
5. Tenured
6. Virtual

矩形分组的标签如下:

  • 总大小:矩形 1 到 6
  • Committed vs. Virtual:Committed 由矩形 1-3 和 5 组成;Virtual 由矩形 4 和 6 组成。
  • 老生代和新生代的比例:老生代由矩形 5-6 组成;新生代由矩形 1-4 组成。
  • Eden和一个Survivor的比例:Eden 是矩形 1;Survivor 是矩形 2。

调整堆大小

下述有关堆的增大和缩小以及默认堆大小的讨论并不适用于并行收集器(parallel collector)。(要了解并行收集器的默认堆大小以及为其调整大小的细节,请看 The Parallel CollectorParallel Collector Ergonomics 部分)不过,控制堆总大小和分代大小的参数仍然适用于并行收集器。

最重要的影响垃圾收集参数是总的可用内存。因为当分代装满时,垃圾收集就会发生,吞吐量与可用内存数量成反比。

缺省情况下,虚拟机在每次收集时会增大或缩小堆,为了在每次收集时尽力保持空闲空间与活对象的比例在指定的范围内。目标范围由 -XX:MinHeapFreeRatio=<minimum>-XX:MaxHeapFreeRatio=<maximum> 参数指定的百分数设定。堆总大小的界限由 -Xms<min>-Xmx<max> 设定。64 位 Solaris 操作系统 (SPARC Platform Edition) 平台上的默认参数如表 4-1 所示:

Table 4-1 Default Parameters for 64-Bit Solaris Operating System

Parameter Default Value
MinHeapFreeRatio 40
MaxHeapFreeRatio 70
-Xms 6656k
-Xmx calculated

有了这些参数,如果分代中空闲空间的百分比降至 40% 以下,分代就会扩大以维持 40% 的空闲空间,直至该分代被允许的最大大小。类似地,如果空闲空间超过 70%,分代将被缩小,为了保持仅 70% 的空间是空闲的,但这还要视分代的最小大小而定。

正如表 4-1 指出的,默认的最大堆大小是 JVM 计算得出的值。Java SE 中针对并行收集器和 Server JVM 的计算现在已经用于所有的垃圾收集器。计算的一部分是最大堆大小的上限值,这个值在 32 位和 64 位平台上是不同的。请看 The Parallel CollectorDefault Heap Size 部分。针对 Client JVM 也有一个类似的计算,该计算会产生比 Server JVM 更小的最大堆大小。

下面是有关服务端应用的堆大小的普遍指导方针:

  • 除非你认为暂停时间有问题,否则可尝试允许分配尽可能多的内存给虚拟机。默认大小通常太小。

  • -Xms-Xmx 设置为相同的值就能通过从虚拟机中删除最重要的调整大小的决策逻辑来增加可预测性(predictability)。但是,如果你的选择很糟糕,虚拟机就不能弥补你的过失了。

  • 通常,增加处理器数量的同时也要增加内存,因为分配操作是可以并行的。

新生代

在总的可用内存之后,第二个最影响垃圾收集性能的因素是专用于新生代的堆的比例。新生代越大,minor 收集发生的频次越低。但是,对于有限的堆大小,更大的新生代就意味着更小的老生代,这会增加 major 收集的频率。最佳选择取决于应用程序中对象生命周期的分布。

缺省情况下,新生代的大小由 NewRatio 参数控制。例如,-XX:NewRatio=3 意思是新生代和老生代的比例是 1:3。换句话说,eden 和两个 survivor 加在一起的大小是整个堆的 1/4。

参数 NewSizeMaxNewSize 限定了新生代的上下界。将它们设置为相同的值就固定了新生代的大小,正如参数 -Xms-Xmx 值相同就固定了整个堆的大小。以比 NewRatio 允许的整数倍更精细的粒度调整新生代是有用的。

调整 survivor 空间的大小

使用参数 SurvivorRatio 可以调整 survivor 空间的大小,但这对于性能并不重要。例如,-XX:SurvivorRatio=6 将一个 survivor 空间和 eden 的比例设置为 1:6。换句话说,每个 survivor 空间是 eden 的 1/6,新生代的 1/8(不是 1/7,因为有两个 survivor 空间)。

如果 survivor 空间太小,复制收集(copying collection)会直接溢出到老生代。如果 survivor 太大,它们就会毫无益处地空着。每次垃圾收集时,虚拟机会选择一个阈值,这就是一个对象在被移至老生代之前可被复制的次数。选择这个阈值是为了保持两个 survivor 的一半是满的。命令行选项 -XX:+PrintTenuringDistribution 可用来显示这个阈值和新生代中对象的年龄。观察应用的生命周期分布也是有用的。

表 4-2 为 64 位 Solaris 提供了默认参数值:

Table 4-2 Default Parameter Values for Survivor Space Sizing

Parameter Server JVM Default Value
NewRatio 2
NewSize 1310M
MaxNewSize not limited
SurvivorRatio 8

新生代的最大大小将从整个堆的最大大小和参数 NewRatio 的值计算得到。MaxNewSize 参数的默认值 “not limited” 意思是计算得到的值并不受 MaxNewSize 的限制,除非在命令行给 MaxNewSize 指定了一个值。

下面是针对服务端应用的普遍指导方针:

  • 首先决定你能承担的分配给虚拟机的最大堆大小。然后绘制 性能指标相对于新生代大小的 图表,以找出最佳设置。

    • 注意,最大堆大小应该始终小于机器上安装的内存数量,以避免过度的缺页错误(page faults)和抖动(thrashing)。
  • 如果总的堆大小确定了,那么增加新生代的大小就需要减小老生代的大小。保持老生代足够大以容纳应用在任何给定时间使用的所有活数据,加上一些闲散零碎的(slack)空间(10 到 20% 或更多)。

  • 取决于之前说明的关于老生代的约束:

    • 准许分配很多内存给新生代。
    • 增加处理器数量的同时增加新生代的大小,因为分配操作是可以并行的。

垃圾收集选项参考文档

Oracle 文档中心:http://docs.oracle.com

Java 文档中心:http://docs.oracle.com/en/java/

垃圾收集选项很多,有任何疑问可参考 Java SE Tools Reference for UNIX -> java command -> Advanced Garbage Collection Options

参考资源

原文 Java 8 Garbage Collection Tuning Guide: Sizing the Generations

Java Garbage Collection Basics

Getting Started with the G1 Garbage Collector

0%