最近分析HotSpot VM GC日志,就各种收集器的名称搞晕掉了,幸好参考R大(RednaxelaFX )一些回复和文章。整理在此文,以方便自已日后查阅,也可让有需要的同学少走弯路,追本溯源,一切从DefNew的来因说起。
DefNew: 是使用-XX:+UseSerialGC(新生代,老年代都使用串行回收收集器)
ParNew: 是使用-XX:+UseParNewGC(新生代使用并行收集器,老年代使用串行回收收集器)或者-XX:+UseConcMarkSweepGC(新生代使用并行收集器,老年代使用CMS)。
原本HotSpotVM里并没有并行GC,当时只有NewGeneration。新生代,老年代都使用串行回收收集。后来准备加入新生代并行GC,就把NewGeneration改名为DefNewGeneration,然后把新加的并行版叫做ParNewGeneration。
DefNewGeneration、ParNewGeneration都在Hotspot VM”分代式GC框架“内。但后来有个开发不愿意被这个框加憋着(证明了那句:所有壮举都不是在框架内产生的),自已硬写了个新的并行GC。测试后效果还不错。于是这个也放入VM的GC中。这就是我们现在看到的ParallelScavenge。
这个时候就出现个两个新生代的并行GC收集器:ParNewGeneration,ParallelScavenge。
(R大: Scavenge或者叫scavenging GC,其实就是copying GC的另一种叫法而已。HotSpot VM里的GC都是在minor GC收集器里用scavenging的,DefNew、ParNew和ParallelScavenge都是,只不过DefNew是串行的copying GC,而后两者是并行的copying GC。 由此名字就可以知道,“ParallelScavenge”的初衷就是把“scavenge”给并行化。换句话说就是把minor GC并行化。至于full GC,那不是当初关注的重点。 )
把GC并行化的目的是想提高GC速度,也就是提高吞吐量(throughput)。所以其实ParNew与ParallelScavenge都可叫做Throughput GC。
但是在HotSpot VM的术语里“Throughput GC”通常特指“ParallelScavenge”。
ParallelScavenge和ParNew都是并行GC,主要是并行收集young gen,目的和性能其实都差不多。最明显的区别有下面几点:
- ParallelScavenge以前是广度优先顺序来遍历对象图的,JDK6的时候改为默认用深度优先顺序遍历,并留有一个UseDepthFirstScavengeOrder参数来选择是用深度还是广度优先。在JDK6u18之后这个参数被去掉,ParallelScavenge变为只用深度优先遍历。ParNew则是一直都只用广度优先顺序来遍历。
- ParallelScavenge完整实现了adaptive size policy,而ParNew及“分代式GC框架”内的其它GC都没有实现完(倒不是不能实现,就是麻烦+没人力资源去做)。所以千万千万别在用ParNew+CMS的组合下用UseAdaptiveSizePolicy,请只在使用UseParallelGC或UseParallelOldGC的时候用它。
- 由于在“分代式GC框架”内,ParNew可以跟CMS搭配使用,而ParallelScavenge不能。当时ParNew GC被从Exact VM移植到HotSpot VM的最大原因就是为了跟CMS搭配使用。
- 在ParallelScavenge成为主要的throughput GC之后,它还实现了针对NUMA的优化;而ParNew一 直没有得到NUMA优化的实现。
上面说ParallelScavenge并行收集young gen,那old/perm gen呢?
ParallelScavenge因为和其他几个GC不在一个框架内,最初的ParallelScavenge体系对老年代的回收拿的是“VM分代式框里的“ Serial Old收集器,改了接口,负责full GC,
命名为: PSMarkSweep(=“ParallelScavenge的MarkSweep”),其实就是串行收。
这里的ParallelScavenge已经不是Parallel Scavenge,而是一套GC框架体系。
为了名称下好区别,在这套体系时,新生代收集器叫:PSScavenge,这时的老年代收集器叫:PSMarkSweep。(PS看成是ParallelScavenge缩写,作为前缀)。
后来,因为未知的原因,老年代GC的并行化,没有在”分代式GC框架“中完成,而选择了在ParallelScavenge框架中。其成果就是使用了LISP2算法的并行版的full GC收集器,名为PSCompact(=“ParallelScavenge-MarkCompact”),收集整个GC堆。
当启用-XX:+UseParallelOldGC时,用的就是PSScavenge+PSCompact的组合。
当启用-XX:+UseParallelGC时,用的就是PSScavenge+ PSMarkSweep的组合。
(在Jconsole查看时,PSCompact、PSMarkSweep都显示为PSMarkSweep)。
Guest Author有一幅关于GC收集器的示意图:
黄色部分的用于新生代的收集器,紫灰色部分的用于老年代的收集器。连接表示两个可以配合使用。
你会发现分代式GC框架有收集器(Serial (就是DefNew),ParNew,CMS,MSC)可以任意搭配。而ParallelScavenge体系里的PSScavenge(图示中的ParallelScavenge),只能和其同一体系的Parallel Old搭配。
至于ParallelScavenge和Serial Old的连线,就是因为上文提到的PSMarkSweep,他是从“VM分代式框里的“ 抽出来的Serial Old收集器,加了一层包装而已。
?号那个应该就是现在G1,他又是另一个体系框架内开发的,所以六亲不认。