垃圾收集器总结

2018-10-31  本文已影响0人  tangyu_tyty

垃圾收集器总结

各种收集器组合使用的参数

Serial + CMS

注意不能使用-XX:+UseConcMarkSweepGC -XX:+UseSerialGC,否则会报Conflicting collector combinations in option list; please refer to the release notes for the combinations allowed

-XX:+UseConcMarkSweepGC -XX:-UseParNewGC

ParNew + CMS

-XX:+UseConcMarkSweepGC

-XX:+UseParNewGC -XX:+UseConcMarkSweepGC

Serial + Serial Old

-XX:+UseSerialGC

ParNew + Serial Old

不建议使用的组合。Using the ParNew young collector with the Serial old collector is deprecated and will likely be removed in a future release

注意不能使用-XX:+UseParNewGC -XX:+UseSerialGC,否则会报Conflicting collector combinations in option list; please refer to the release notes for the combinations allowed

-XX:+UseParNewGC

Parallel Scavenge + Serial Old

由于老年代的收集是单线程的,无法获得吞吐量最大化的效果,这种组合还不一定有 ParNew + CMS 给力

不知道怎么设置

Parallel Scavenge + Parallel Old

注重吞吐量以及CPU资源敏感的场景下使用

-XX:+UseParallelGC

-XX:+UseParallelOldGC

-XX:+UseParallelGC -XX:+UseParallelOldGC

或直接不写,默认使用的就是Parallel Scavenge + Parallel Old

G1

-XX:+UseG1GC
image

串行收集器

image

Serial

Serial Old

并行收集器

image

ParNew

Parallel Scavenge

Parallel Old

并发标记清除收集器

image

CMS (Concurrent Mark Sweep)

G1

image

G1具备如下特点:

在G1之前的其他收集器进行收集的范围都是整个新生代或者老年代,而G1不再是这样。使用G1收集器时,Java堆的内存布局就与其他收集器有很大差别,它将整个Java堆划分为多个大小相等的独立区域(Region),虽然还保留有新生代和老年代的概念,但新生代和老年代不再是物理隔离的了,他们都是一部分Region(不需要连续)的集合。

G1收集器之所以能建立可预测的停顿时间模型,是因为它可以有计划地避免在整个Java堆中进行全区域的垃圾收集。G1跟踪各个Region里面的垃圾堆积的价值大小(回收获得的空间大小以及回收所需时间的经验值),在后台维护一个优先级列表,每次根据允许的收集时间,优先回收价值最大的Region(这也是Garbage-First名称的由来)。这种使用Region划分内存空间以及有优先级的区域回收方式,保证了G1收集器在有限时间内可以获取尽可能高的回收效率。

G1实现细节远没有想象中那样简单:把Java堆分为多个Region后,垃圾收集是否就真的能以Region为单位进行了?听起来顺理成章,再仔细想想就很容易发现问题所在:Region不可能是孤立的。一个对象分配在某个Region中,它并非只能被本Region中的其他对象引用,而是可以与整个Java堆任意的对象发生引用关系。那在做可达性判定对象是否存活的时候,岂不是还得扫描整个Java堆才能保证准确性?这个问题其实并非在G1中才有,只是在G1中更加突出而已。在以前的分代收集中,新生代的规模一般都比老年代要小很多,新生代的收集也比老年代要频繁许多,那回收新生代中的对象时也面临相同的问题,如果回收新生代时也不得不同时扫描老年代的话,那么MinorGC的效率可能下降不少。

在G1收集器中,Region之间的对象引用以及其他收集器中的新生代与老年代之间的对象引用,虚拟机都是使用Remembered Set来避免全堆扫描的。G1中每个Region都有一个与之对应的Remembered Set,虚拟机发现程序在对Reference类型的数据进行写操作时,会产生一个Write Barrier暂时中断写操作,检查Reference引用的对象是否处于不同的Region之中(在分代的例子中就是检查是否老年代中的对象引用了新生代中的对象),如果是,便通过CardTable把相关引用信息记录到被引用对象所属的Region的Remembered Set之中。当进行内存回收时,在GC根节点的枚举范围中加入Remembered Set即可保证不对全堆扫描也不会有遗漏。

如果不计算维护Remembered Set的操作,G1收集器的运作大致可划分为以下几个步骤:

初始标记阶段仅仅是标记一下GC Roots能直接关联到的对象,并且修改TAMS(Nest Top at Mark Start)的值,让下一阶段用户程序并发运行时,能在正确可用的Region中创建新对象,这阶段需要停顿线程,但耗时很短。并发标记阶段是从GC Root开始对堆中对象进行可达性分析,找出存活的对象,这阶段耗时很长,但可与用户程序并发执行。而最终标记阶段则是为了修正在并发标记期间因用户程序继续运作而导致标记产生变动的那一部分标记记录,虚拟机将这段时间对象变化记录在线程Remembered Set Logs里面,最终标记阶段需要吧Remembered Set Logs的数据合并到Remembered Set中,这阶段需要停顿线程,但是可并行执行。最后在筛选回收阶段首先对各个Region的回收价值和成本进行排序,根据用户所期望的GC停顿时间来指定回收计划,从Sun公司透露出来的信息来看,这个阶段其实也可以做到与用户程序一起并发执行,但是因为指挥手一部分Region,时间是用户可控制的,而且停顿用户线程将大幅提高收集效率。

上一篇 下一篇

猜你喜欢

热点阅读