很多参数会影响 “代” 的大小。下图说明了堆中“已提交空间”和“虚拟空间”之间的区别。JVM 初始化时会为堆预留整个空间。可以通过 -Xmx 选项指定这个预留空间的大小。如果参数 -Xms 的值比 -Xmx 小,那么不会把所有的预留空间都提交给 JVM。这些未提交的空间在图中被标为 “virtual”(虚拟空间)。堆的不同部分(新生代和老年代)可根据需要增长到虚拟空间的极限。
某些参数是堆中 一部分与另一部分 的比率。如,NewRatio 表示老年代与新生代的相对大小。
堆的总容量
以下关于 堆的增长和收缩 及 默认堆大小 不适用于并行GC。(可查看《并行GC》了解 并行GC 堆大小调整 与 默认堆大小 的信息。)但是控制 “堆的总大小” 和 “‘代’的大小” 的参数适用于 并行GC。
影响 GC 性能的最重要因素是 总可用内存。因为 GC 操作发生在“代”满时,吞吐量 与 可用内存量 成反比。
默认情况下,JVM 会增大或缩小“代”,以尝试将 可用空间的比例 保持在特定范围内。这个“范围”是个百分比,可以通过参数 -XX:MinHeapFreeRatio= 和 -XX:MaxHeapFreeRatio= 指定。
-Xms 设定了堆的最小容量,-Xmx 设定了堆的最大容量。
下表展示了这些参数在 64位 Solaris(SPARC平台版)上的默认值。
参数 | 默认值 |
MinHeapFreeRatio | 40 |
MaxHeapFreeRatio | 70 |
-Xms | 6656K |
-Xmx | 通过计算得到 |
根据这些参数,如果某个“代”中的可用空间低于40%,则 JVM 会扩大该“代”的容量以保持40%的可用空间,直到达到该“代”的容量上限。
类似的,如果可用空间超过70%,那么“代”会被缩小,使得其只有70%的可用空间,当然也取决于它的容量下限。
正如上表所示,堆大小的默认最大值是 JVM 计算得到的。该计算方法之前被用在 并行GC 上,现在所有 GC 都在用。其中关于堆大小最大值的上限计算在 32位平台 和 64位平台 中是不同的(《堆容量 的 默认值》)。对客户端 JVM 也有类似的计算,这导致它的最大堆大小 小于 服务端 JVM。
一般准则
以下是关于 服务器应用 堆大小 的一般准则:
-
除非暂停时间太长,否则尝试分配尽可能多的内存。默认值通常都太小了。
-
将 -Xms 和 -Xmx 设置为相同值,可以移除大小调整策略,从而提高可预测性。但是如果你选择不当,JVM 无法补偿。
-
通常,如果增加了CPU核数,就可以增大内存,因为内存分配是可以并行的。(以此提高内存分配效率)
新生代
在总可用内存之后,影响 GC 性能的最重要因素是 新生代的份额。
新生代越大,Minor GC 的频率就越小。但是,对于有限的堆容量,更大的新生代意味着更小的老年代,这会增大 Major GC 的频率。
最佳选择取决于应用程序所分配对象的寿命分布。
默认情况下,新生代的容量由参数 NewRatio 控制。如,-XX:NewRatio=3 表示新生代与老年代的比例为 1:3 。也就是说,新生代(Eden 和 2个 Survivor)占总堆的 1/4 。
参数 NewSize 和 MaxNewSize 确定了新生代容量的 下限和上限。将这两个参数设置为相同的值 可以 固定新生代的大小。就如同 -Xms 和 -Xmx 相同时会固定堆的总容量。对于 NewRatio 的整数倍调整来说,这种更细粒度的新生代调整是很有用的。
Survivor
你可以使用参数 SurvivorRatio 来调整 Survivor 的容量,但这通常对性能并不重要。
例,-XX:SurvivorRatio=6 表示将 Eden 和 一个Survivor 的比例设置为 1:6 。也就是说,每个 Survivor 都是 Eden 的 1/6 ,也就是新生代的 1/8 (不是 1/7,因为有 2个Survivor)。
-
如果 Survivor 太小,GC 复制对象时会直接溢出到老年代。
-
如果 Survivor 太大,它们也是毫无用处地空着。
每次 GC 时,JVM 都会选择一个阈值,来表示对象被转到 老年代 前可以复制的次数。这个阈值的选择是为了让 Survivor 保持半满(有2个Survivor)。命令行选项 -XX:+PrintTenuringDistribution 可用于显示此阈值 及新生代中对象的“年龄”(被复制的次数)。(注意:此选项并不是所有 GC 都适用。)这对于观察对象寿命分布是很用的。
下表展示了这些参数在 64位 Solaris 上的默认值。
参数 | Server JVM 中的默认值 |
NewRatio | 2 |
NewSize | 1310M |
MaxNewSize | 无限制 |
SurvivorRatio | 8 |
新生代的最大容量 是根据 堆的总容量 和 NewRatio 参数 计算得到的。MaxNewSize 的默认值 “无限制” 表示 “除非通过命令行设置了 MaxNewSize,否则不受限制”。
一般准则
以下是针对服务器应用的一般准则:
-
先确定可以提供给 JVM 的 堆容量 最大值。然后根据新生代的规模来规划性能指标,找到最佳设置。
注意:堆容量 的最大值应小于机器上的内存数量,以免过多的内存换页与抖动(短时间内大量换页操作就是抖动)。
-
如果堆的总容量是固定的,那么增大新生代就需要减小老年代。
保持老年代足够大,让它在任何时刻都能存放应用程序的所有存活数据,外加一些空闲空间(10%~20% 或 更多)。
-
给新生代充足的内存。
-
如果增加了CPU核数,就可以增大内存,因为内存分配是可以并行的。