现在来讲讲G1收集器。在前面的文章晒出了一张图片,G1收集器处于老年代和新生代之间。那么这个新晋升的收集器,到底怎么样。讲G1收集器之前,我们再来回顾一下CMS收集器和传统收集器的缺点。
CMS收集器和传统收集器的缺点
1.碎片化问题
2.标记清除算法会产生大量的碎片化的问题,如果存放一个大对象的时候,有可能会频繁的引发FullGC 使用串行老年代收集器单线程清理堆内存垃圾,触发stw问题,导致工作线程全部阻塞等待
3.存放大对象如果在新生代无法存放,则会晋升到老年代存放,但是如果大对象又不是被频繁引用,而且老年代GC并回事频繁触发,那么这个大对象就会占用很多空间
那为什么要来回顾这个问题呢?
其实我们的G1收集器解决了这些问题,那么他是采用什么方式解决这些问题的呢?我们都知道,碎片化问题,我们可以使用标记整理算法和标记赋值算法进行解决。那么第二个问题呢?我们可以发现,第二个和第三个问题都是针对于大对象来体现出问题的,那我们是不是可以使用一个专属的空间来管理大对象?是的,G1就是这样的。
那么G1的物理空间分布是怎么样的?其实他和我们的CMS不同,CMS是连续的,而G1是不连续的,G1采用的是将堆内存分为2048块,每块为1M-32M,但是每块的大小必须符合2的次幂。那么可以得出最大堆内存64G,最小堆内存2G。那他的模型图是怎样的?
我们可以看到里面一共有四个区,前三个我们已经知道也了解到了。但是最后有个H区。这个H区可以看到是连续的两个区间,其实并不然,这些区间都是随机分配的,堆内存中的每一块释放,之后他会作为什么区都是随机的。它们都是一部分Region (不需要连续)的集合。通过Region的动态分配方式实现逻辑上的连续。
那什么是大对象?
什么是大对象
在收集器中存在这么一个定义,当对象的大小大于区间设定值的一般的时候该对象为大对象。一旦发现没有引用指向巨型对象,则可直接在年轻代收集周期中被回收。
为何要设定H区
一般大对象不会被经常使用,有可能会非常浪费内存
G1收集器回收过程原理
G1收集器回收过程分为三个环节:
- 年轻代GC(Young GC )
- 新生代和并发标记过程( Concurrent Marking)
- 混合回收(Mixed GC )
young gc(新生代) 一> young gc + concurrent mark(新生代+并发标) 一> Mixed GC(混合回收)顺序,进行垃圾回收
Remembered Set(记忆集)
作用:我们可以回想一下,在CMS中,回收对象的时候,是进行引用链的形式记性对象标记的,那这么多的区块要怎么区遍历,或者说怎么知道哪些需要进行回收呢?其实每个区块都有一个Remembered Set(记忆集),并且使用CarTable
记录每个region区相互引用的关系。并且CarTable只存放不同区块相互关联的地址,也就是说,如果A与B对象在同一区块,那么将不会记录。反之,则记录。
G1收集器写屏障
写屏障(Store Barrier),所谓的写屏障,其实就是指在赋值操作前后,加入一些处理(可以参考AOP的概念):记录赋值的操作。
G1收集器CSet
收集集合(CSet)代表每次GC暂停时回收的一系列目标分区。在任意一次收集暂停中,CSet所有分区都会被释放,内部存活的对象都会被转移到分配的空闲分区中。因此无论是年轻代收集,还是混合收集,工作的机制都是一致的。年轻代收集CSet只容纳年轻代分区,而混合收集会通过启发式算法,在老年代候选回收分区中,筛选出回收收益最高的分区添加到CSet中。
候选老年代分区的CSet准入条件,可以通过活跃度阈值-XX:G1MixedGCLiveThresholdPercent
(默认85%)进行设置,从而拦截那些回收开销巨大的对象;
同时,每次混合收集可以包含候选老年代分区,可根据CSet对堆的总大小占比-XX:G1OldCSetRegionThresholdPercent
(默认10%)设置数量上限。
也就是说,G1的收集都是根据CSet进行操作的,年轻代收集与混合收集没有明显的不同,最大的区别在于两种收集的触发条件