引用:
G1是一款增量式的分代垃圾收集器
G1 物理上不分代,默认会将整个内存区域分为2048个region,region的大小为1MB-32MB,且为2的N次幂。适合内存大的jvm。 region可分为四种类型,且region并不是固定的,运行时会改变
-
Eden Space 新生代
-
Survivor Space 存活区
-
Old Generation 老年代
-
humongous 存储大对象,如果超过0.5个region,就放入humongous
对于堆中的大对象,默认直接会被分配到老年代,但是如果它是一个短期存在的大对象, 就会对垃圾收集器造成负面影响。为了解决这个问题,G1划分了一个Humongous区, 它用来专门存放大对象。如果一个H区装不下一个大对象,那么G1会寻找连续的H区来 存储。为了能找到连续的H区,有时候不得不启动Full GC。 G1的大多数行为都把H区 作为老年代的一部分来看待。
G1尽可能多的从有空闲的region中回收内存,同时到达预期的暂停时间指标。
card table (卡表)
老年代的每个region是有card组成的, 每个card 的大小是大约是512k。如果老年代的对象引用了新生代的对象,我们就把这个card标记为脏card,这样根据GC roots 找对象的时候就不用遍历整个老年代了,只在脏card中寻找就可以了。减少扫描范围,提升效率
RememberSet(记忆集)
新生代中每个region中都有个RememberSet(记忆集),简称Rset,用来记录哪个card指向这个region的引用。通过这种数据结构,G1就可以进行增量式回收内存,而不用扫描整个堆内存,因为只要扫描Rset,就可以知道哪些跨区的引用指向这个Region,从而对这些region进行回收。
G1的垃圾回收模式
-
young GC:只回收新生代
会收集eden区和上一次gc使用的存活区,采用复制的方式转移到新的Region中,如果达到分代年龄或者其他条件,会转移到老年代中
默认老年代占用到整个堆空间的大约45%时,它就会进行这个并发标记
-
mixedGC:回收一部分新生代和老年代
当老年代的内存超过一个阈值。并发标记完毕之后,会从 young GC模式切换到mixed GC,除了回收eden区和上一次gc使用的存活区,还会选择一部分的老年代进行回收。在进行老年代回收时依然采用复制算法,因为复制算法执行的快。经过多次混合模式的垃圾回收,很多老年代的region已经被处理过了,就会切换到young GC
tips:
如果你的回收速度是高于新的用户线程产生的垃圾的速度的时候,也就是我的回收速度比你新产生的垃圾快,来得及打扫,这个时候还不叫 Full GC,这个时候还是处于并发垃圾收集的阶段,虽然重新标记和数据拷贝的过程还会有暂停,但是这个暂停时间还是相对很短的,这还称不上 Full GC,那 G1在什么时候才会发生 Full GC呢,就是当你的垃圾回收的速度跟不上垃圾产生的速度了,新产生的垃圾比你的回收速度快了,这个时候并发收集就失败了,就跟以前的 CMS 类似,这时候它就会退化一个串行的收集,串行收集这时候就会叫Full GC了,特别的慢,当然也会更长时间的 STW 导致响应时间变长。
标记的各个阶段
-
初始标记。stw,扫描GC roots
-
并发标记。根据GC roots扫描整个堆,找到存活的对象
-
重新标记。stw,通过写屏障+SATB(原始对象快照)的方式标记对象
它是在pre-write barrier写屏障技术在对象引用改 变前把这个对象加入到了一个队列,并且表示它是未被处理的,这个队列的名称叫 satb_mark_queue,将来的 Remark 阶段就可以配合这个队列来对这些对象进行进一步的判断,
-
并发清楚。
G1垃圾收集器的运行过程:
①. G1 GC的垃圾回收过程主要包括如下三个环节:
-
年轻代GC (Young GC)
-
老年代并发标记过程 (Concurrent Marking)
-
混合回收(Mixed GC)
(如果需要,单线程、独占式、高强度的Full GC还是继续存在的。它 针对GC的评估失败提供了-种失败保护机制,即强力回收。)
顺时针young gc -> young gc + concurrent mark-> Mixed GC顺序,进行垃圾回收
②. 应用程序分配内存,当年轻代的Eden区用尽时开始年轻代回收过程;*G1的年轻代收集阶段是一个*并行(多个垃圾线程)的独占式收集器。在年轻代回收期,G1 GC暂停所有应用程序线程,启动多线程执行年轻代回收。然后从年轻代区间移动存活对象到Survivor区间或者老年区间,也有可能是两个区间都会涉及
③. 当堆内存使用达到一定值(默认45%)时,开始老年代并发标记过程
④. 标记完成马上开始混合回收过程。对于一个混合回收期,G1 GC从老年区间移动存活对象到空闲区间,这些空闲区间也就成为了老年代的一部分。和年轻代不同,老年代的G1回收器和其他GC不同,G1的老年代回收器不需要整个老年代被回收,一次只需要扫描/回收一小部分老年代的Region就可以了。同时,这个老年代Region是和年轻代一起被回收的。
⑤. 举个例子:一个Web服务器,Java进程最大堆内存为4G,每分钟响应1500个请求,每45秒钟会新分配大约2G的内存。G1会每45秒钟进行一次年轻代回收,每31个小时整个堆的使用率会达到45%,会开始老年代并发标记过程,标记完成后开始四到五次的混合回收
G1的可选过程:Full GC
-
G1的初衷就是要避免Fu1l GC的出现。但是如果上述方式不能正常工作,G1会停止应用程序的执行(Stop-The-World) ,使用单线程的内存回收算法进行垃圾回收,性能会非常差,应用程序停顿时间会很长。
-
要避免Full GC的发生,一旦发生需要进行调整。什么时候会发生Full GC呢? 比如堆内存太小,当G1在复制存活对象的时候没有空的内存分段可用,则会回退到full gc, 这种情况可以通过增大内存解决。
-
导致G1Full GC的原因可能有两个: .
1. 回收的时候没有足够的to-space来存放晋升的对象
2.并发处理过程没完成空间就耗尽了