不同 GC 和 堆内存总结
GC 算法
标记-复制。优点:存活的对象越少,复制需要的空间就越小;而且复制后的对象们内存空间排布紧凑,避免空间碎片的问题。缺点:有一部分空间被浪费。如果存活对象大且多的话复制成本比较高。适用每次 GC 存活对象小而美的情况。年轻代
标记-清除。直接清除可回收对象,不进行内存整理。,优点:单次 STW 的时间可能要短一些。缺点:但是产生的内存碎片,可能导致内存总空间足够,但是没一块连续的空间存放对象的问题,内存利用率降低;对象放不下,可能触发额外的 GC。适用对象存活率高的情况。老年代
标记-清除-整理。STW 时间可能会稍长一些,内存碎片问题得到解决。适用对象存活率高的情况。
Serial GC
-XX:+UseSerialGC
串行 GC 单线程执行,在 GC 期间其他业务线程均暂停,暂停的时间长。
串行 GC 对年轻代采用标记复制算法。对老年代使用标记-清除-整理算法。
串行 GC 简单直接,在单核 CPU 环境下比较适用。
-XX:+UseParNewGC
ParNew 收集器,多线程版本的 Serial。配合 CMS 使用。
Parallel GC
-XX:UseParallelGC -XX:UseParallelOldGC
使用的 GC 算法和串行的一样。
默认的 GC 线程数是 CPU core 数,该收集器的目标更倾向于提高系统吞吐量,有时候单次的 GC 暂停时间较长。
CMS GC
-XX:UseConcMarkSweepGC
对老年代没有整理操作,使用 free-list 进行内存空间的管理。默认的核心线程数 CPU 核数 / 4。
可以和业务线程并发执行,GC 暂停时间少。
G1 GC
打破整个分区的理论,把内存划分成多个小块进行管理。对每个小块的垃圾数量进行预估,优先回收垃圾多的 Region。可预期的垃圾停顿时间。
验证总结
首先需要提到的一点是 GC 的时间和存活的对象数量有关,和堆内存的大小关系没有那么大。
配置堆内存 512M,YGC 后年轻代存活对象大概 20M。
串行 GC 利用单线程执行,GC 暂停的时间明显会比较长。在实际的测试下,在小堆内存空间的情况下,YGC 和并行 GC 的 YGC 差不多。FGC 使用的时间明显较长,大概是并行 GC 的一倍(存活对象 300M 左右)。老年代存活对象占用的空间大,整理移动的时间就长。
CMS GC 的老年代清理明显的暂停时间降低。在 GC 日志中有发现 concurrent mode failure 的情况。查询资料后明白,CMS 在 cleanup 是并发执行的,这时的对象引用关系发生改变,也可能有新的对象需要分配空间。如果没有预留足够的空间内存分配就会导致并发失败。可能重新 CMS ,或者 GC 退化成 Serial。
G1 GC 出现了 Humongous Allocation 因为大对象分配失败,触发了 initial-mark。也是重新标记,或者 GC 退化的问题。
堆内存越大,内存中可容纳的对象越多,GC 的次数随之减少,单次 GC 的暂停时间可能更长(取决于存活对象的数量)。
总的来说,注意不同 GC 策略采用的算法,以及设计的目的。比如 CMS 在于并发执行,提高系统响应。Parallel 更倾向于提高吞吐量;G1 GC 倾向于可配置可预估的暂停时间。
CMS - 老年代 没有整理,使用 free-list 管理回收内存;真正的 STW 时间小,但是步骤多,还有浮动垃圾,GC 退化问题。G1 GC 也存在 GC 退化问题。
配置堆内存的时候,注意 JVM 自身需要的内存和系统需要的内存,预留一定的空间。
-Xms -Xmx 直接一步到位,扩容的时候有性能的抖动。
年轻代和老年代的比例默认 1:2,新生代:from:to = 8:1:1,根据情况来调整。
根据对象晋升回收速率的计算,进行空间,晋升年龄的配置。
版权声明: 本文为 InfoQ 作者【学个球】的原创文章。
原文链接:【http://xie.infoq.cn/article/b0cb6610d5f96832af321ec98】。
本文遵守【CC-BY 4.0】协议,转载请保留原文出处及本版权声明。
评论