< 返回新闻公共列表
云南大王-G1垃圾回收器
发布时间:2020-04-13 00:00:00
垃圾回收器的发展历程
背景
01、G1解决的问题
G1垃圾回收器是04年正式提出,12开始正式支持,在17年作为JDK9默认的垃圾处理器。
在04年的时候,java程序堆的内存越来越大,从而导致程序中可存活的活对象越来越多,因此GC的STW时间越来越长。这是G1要解决的主要问题:STW带来的停顿时间太长了。
CMS在此之前效率也很高,但活对象数量一多,STW时间也很长。而且CMS无法解决内存碎片化的问题。
G1还解决的问题是:CMS在GC后,无法compact内存。
02、G1达成的目标
(1)减少由于STW而带来的程序延迟时间,做到伪实时、低延时、可设定目标;
可设定目标是指能够设置GC最大STW停顿的时间,G1会尽量达成目的,但不一定达成。
-XX:MaxGCPauseMillis=N
默认情况下是250毫秒
(2)解决CMS在GC后,无法压缩程序内存的问题;
(3)在JDK9之后,默认的垃圾处理器就是G1;它适用于堆内存较大的情况下(>4~6G);
G1垃圾回收器
一、G1内存布局
G1不再遵循之前的堆中对象的分代排列,而是将堆分成若干个等大的区域。
而是变成:
默认是分成2048个区域,-XX:G1HeapRegionSize=N 2048
Humongous:当你分配的一个对象超过一半区域的大小时,这个对象就会被放入这个区域。这个区域属于老年代区域。
二、G1的介绍
G1垃圾回收器不再回收整个堆,而是选择一个Collection Set(CS)。而且每次GC时,会估计每个Region中的垃圾比例,优先回收垃圾多的Region。这就为什么被叫做Garbage First算法。这也是为什么G1可以控制STW停顿时间的原因。
G1含有三种GC算法:
Full young GC:年轻代GC算法:STW、Parallel、Copying
老年代GC算法:Mostly-concurrent marking、Incremental compaction
Mixed GC:混合GC
三、G1引来的问题
问题描述
G1将年轻代、老年代区域划分为许多个小区域,增加在GC判断对象是否为垃圾的难度。比如:
老年代对象可能持有年代代的引用(跨代引用)
不同的Region间的互相引用
假设在Full young GC时,某个年轻代Region对象可能被老年代的某个对象引用,那么我在回收这个年轻代Region时,怎么知道这里面的对象是否被其他Region、老年代引用呢?
问题解决
Remembered Set、Card Table
1、CardTable
每个Region中分为很多区域,每个区域我们成为CardTable,对应的就是上述蓝色区域;每个CardTable有多个entry组成。当对应的内存空间发生改变时,就会标记为dirty。
2、RememberedSet
当Region1的CardTable引用Region2的CardTable时,Region2的RememberedSet就会记录对应CardTable中的entry,可以根据其找到对应的内存区域。
3、解析
当某个内存对应进行赋值是,就是对象的set方法,我们可以在这种方法上添加dirty的描述。
这其实就是典型的时间换空间的做法:用额外的空间维护引用信息,这就是占用5~10%的过多内存占用。
解决方法的实现
1、Write Barrier介绍
Write barrier是一种向JVM注入的一小段代码,用于记录指针变化。比如说object.field = 。
在JVM开始更新指针时,就经过以下几步:
标记Card为Dirty
将Card存入Dirty Card Queue队列中
这里有一个问题:为什么要放在队列里,而不是直接去更新RememberedSet呢?
这是因为JVM运行可能会有多个线程并行的修改RememberedSet,这样就需要花费额外的时间来解决多线程同步问题。而这种更新引用是频繁的,所以这种额外时间是无法忍受的。
2、Dirty Card Queue
这个队列有白、绿、黄、红四个颜色,表示应用线程往这个队列放任务的状态。
White
表示没有应用线程往队列里放任务,什么事都不用干。
Green
此时Refinement线程开始被激活,开始更新RS。-XX:G1ConcRefinementGreenZone=N
Yellow
此时全部的Refinement线程都被激活,来更新RS。-XX:G1ConcRefinementYellowZone=N
Red
这个时候,应用线程也开始参与排空队列的工作。-XX:G1ConcRefinementRedZone=N
四、GC算法的过程
1、Fully young GC
GC的过程
(1)STW
此时会暂停所有堆中的对象,将部分Region拷贝到指定区域。
(2)构建Collection Set
fully young GC就是选取所有的Eden和Survivor。
(3)扫描GC Roots
(4)更新RememberedSet
排空Dirty Card Queue
(5)Process RS
根据RS找到要GC的对象被哪些对象引用了。
(6)对象拷贝
survivor区域对象的调整。
(7)Reference Processing
额外会做的事
G1记录每个阶段的时间,用于后期自动调优。比如说会记录Eden、Survivor的数量和GC时间,后期会根据我们之前设定的暂停目标来自动调整Region数量。
但是我们设置暂停目标越短,年轻代的Region数量就越少。但这可能会导致Fully young GC频繁发生。
2、Old GC
当堆用量达到一定程度时,就会触发old GC。可以通过以下参数进行设置:
-XX:InitatingHeapOccpancyPercent=45
old GC有一个很大特点就是并发进行的。但它是如何在堆中不断变化的情况下,确定哪些是要清理的垃圾对象呢?
三色标记算法
这种算法实现了在不暂停应用线程的情况下进行并发标记,标记过程过如下:
(1)将GC Root对象记录为黑色,其直接引用对象记录为灰色,并将这些灰色对象放入一个队列中
(2)从队列取出对象,将其标为黑色,将其引用对象记录为灰色,再放入队列中
(3)直到队列中无对象为止
三色标记算法的缺点:Lost Object Problem
三色标记算法并没有完全将所有的活对象都标记出来,这就是Lost Object Problem问题。比如说:
(1)刚开始时
(2)在即将描述将C标为灰色的一刹那
此时,C依然是活对象,但是已经无法将其标记了。
(3)结果
Lost Object Problem的解决
这种解决办法还是通过Write barrier技术来解决。当B.c=null,也就是C指针被删除时,G1还是被认为活对象。
那如果C是新生对象呢?这是老年代GC
Old GC过程
(1)STW
老年代GC会在这个时候,进行一次Fully young GC
(2)恢复应用线程
(3)使用三色标记算法并发标记(init marking)
(4)STW
这时候会有一个Remark阶段,主要是解决SATB、Reference processing
还会有一个Cleanup阶段,用于回收全为空的区
(5)恢复应用线程
3、Mixed GC
我们直到CMS最大的缺点就是无法进行压缩操作,而G1就通过Mixed GC解决了这个问题。
Mixed GC没有固定触发条件,他是根据Fully young GC收集的信息和我们配置的时间来决定,是否触发Mixed GC。它会根据暂停目标,来优先选择垃圾最多的Old Region来执行。
Mixed GC会选择若干个Region进行,默认是选择1/8的Old Region、Eden Region、Survivor Region。
Mixed GC的过程跟Fully young GC的过程相同,都是:STW、Parallel、Copying。
原博客地址