Garbage-First (G1)垃圾回收器适用于“CPU多核、大内存”的服务器。它尝试以高概率满足垃圾收集(GC)暂停时间目标,同时实现高吞吐量 。
G1回收器将heap分成一组大小相等的region(大约2000个),每个region的大小固定在1~32MB(必须为2的幂数)。每个region的内存是物理连续的,不同的region内存物理地址不一定连续,但是同generation的多个region是逻辑连续的。 这就是G1“区域化”。
一组region被逻辑划分成“young generation(含eden区、survivor区)”、“old generation(含old region,humongous region”。young generation和old generation的内存地址不是固定不变的,会应用的运行,由jvm在Xmx范围动态调整。
注意:G1中没有survivor0 和 survivor1之分,只有一个,大小是动态分配的。
每个Region初始化时,会初始化一个remembered set(已记忆集合),该集合用来记录并跟踪其它Region指向该Region中对象的引用。
如下:Region1和Region3中有对象引用了Region2的对象,则在Region2的Rset中记录了这些引用。
在垃圾回收时,根据Rsets即可知道一个region中的对象被哪些region引用,这样可以避免扫描整个堆来找到可以回收的垃圾。
垃圾回收器计划回收的region集合。
步骤1. 选择收集集合(Collection Set),G1会在遵循用户设置的GC暂停时间上限的基础上,选择一个最大年轻代region数,将这个数量的所有年轻代区域作为收集集合。
步骤2. 根处理(Root Scanning),接下来,需要从GC ROOTS遍历,查找从ROOTS直达到收集集合的对象,移动他们到Survivor区域的同时将他们的引用对象加入标记栈
步骤3. RSet扫描(Scan RS),将RSet作为ROOTS遍历,查找可直达到收集集合的对象,移动他们到Survivor区域的同时将他们的引用对象加入标记栈
步骤4. 移动(Evacuation/Object Copy),遍历上面的标记栈,将栈内的所有所有的对象移动至Survivor区域(其实说是移动,本质上还是复制)
注意:YoungGC过程中,存活对象copy到survivor区或者old区的过程是需要STW的。
eden区和survivor区的大小是动态计算的,可能会一直在变动过程中。
触发条件:当JVM无法将新对象分配到eden区域时,会触发年轻代的垃圾回收(年轻代垃圾回收是完全暂停的,虽然部分过程是并行,但暂停和并行并不冲突)。也会称为“evacuation pause”
mixedGC,即混合GC,针对年轻代和老年代都进行垃圾回收。mixedGC是G1垃圾回收器中特有的概念。
触发条件:一旦老年代占据堆内存的 45%(-XX:InitiatingHeapOccupancyPercent:设置触发标记周期的 Java 堆占用率阈值,默认值是 45%。),就要触发 Mixed GC的标记周期。
对整个堆进行回收,包括新生代,老年代、metaspace等。
触发条件:当mixedGC回收内存的速度无法跟上内存分配的速度,导致老年代也满了,就会进行Full GC对整个堆进行回收。G1中的Full GC也而是单线程串行的,而且是全暂停,使用的是标记-整理算法,代价非常高。
官方文档: