G1垃圾回收器

2020-04-10  本文已影响0人  wangjie_fourth

垃圾回收器的发展历程

file

背景

01、G1解决的问题

G1垃圾回收器是04年正式提出,12开始正式支持,在17年作为JDK9默认的垃圾处理器。

file

04年的时候,java程序堆的内存越来越大,从而导致程序中可存活的活对象越来越多,因此GCSTW时间越来越长。这是G1要解决的主要问题:STW带来的停顿时间太长了

CMS在此之前效率也很高,但活对象数量一多,STW时间也很长。而且CMS无法解决内存碎片化的问题。

G1还解决的问题是:CMSGC后,无法compact内存。

02、G1达成的目标

(1)减少由于STW而带来的程序延迟时间,做到伪实时、低延时、可设定目标;
可设定目标是指能够设置GC最大STW停顿的时间,G1会尽量达成目的,但不一定达成。

-XX:MaxGCPauseMillis=N

默认情况下是250毫秒

(2)解决CMSGC后,无法压缩程序内存的问题;

(3)在JDK9之后,默认的垃圾处理器就是G1;它适用于堆内存较大的情况下(>4~6G);

G1垃圾回收器

一、G1内存布局

G1不再遵循之前的堆中对象的分代排列,而是将堆分成若干个等大的区域。

file

而是变成:


file

默认是分成2048个区域,-XX:G1HeapRegionSize=N 2048

Humongous:当你分配的一个对象超过一半区域的大小时,这个对象就会被放入这个区域。这个区域属于老年代区域。

二、G1的介绍

G1垃圾回收器不再回收整个堆,而是选择一个Collection SetCS)。而且每次GC时,会估计每个Region中的垃圾比例,优先回收垃圾多的Region。这就为什么被叫做Garbage First算法。这也是为什么G1可以控制STW停顿时间的原因。
G1含有三种GC算法:

三、G1引来的问题

问题描述

G1将年轻代、老年代区域划分为许多个小区域,增加在GC判断对象是否为垃圾的难度。比如:

假设在Full young GC时,某个年轻代Region对象可能被老年代的某个对象引用,那么我在回收这个年轻代Region时,怎么知道这里面的对象是否被其他Region、老年代引用呢?

问题解决

Remembered SetCard Table

file

1、CardTable
每个Region中分为很多区域,每个区域我们成为CardTable,对应的就是上述蓝色区域;每个CardTable有多个entry组成。当对应的内存空间发生改变时,就会标记为dirty

2、RememberedSet
Region1CardTable引用Region2CardTable时,Region2RememberedSet就会记录对应CardTable中的entry,可以根据其找到对应的内存区域。

3、解析
当某个内存对应进行赋值是,就是对象的set方法,我们可以在这种方法上添加dirty的描述。
这其实就是典型的时间换空间的做法:用额外的空间维护引用信息,这就是占用5~10%的过多内存占用。

解决方法的实现

1、Write Barrier介绍
Write barrier是一种向JVM注入的一小段代码,用于记录指针变化。比如说object.field = <reference>

JVM开始更新指针时,就经过以下几步:

这里有一个问题:为什么要放在队列里,而不是直接去更新RememberedSet呢?
这是因为JVM运行可能会有多个线程并行的修改RememberedSet,这样就需要花费额外的时间来解决多线程同步问题。而这种更新引用是频繁的,所以这种额外时间是无法忍受的。

2、Dirty Card Queue
这个队列有白、绿、黄、红四个颜色,表示应用线程往这个队列放任务的状态。

四、GC算法的过程

1、Fully young GC

GC的过程

(1)STW
此时会暂停所有堆中的对象,将部分Region拷贝到指定区域。

file

(2)构建Collection Set
fully young GC就是选取所有的EdenSurvivor

(3)扫描GC Roots

(4)更新RememberedSet
排空Dirty Card Queue

(5)Process RS
根据RS找到要GC的对象被哪些对象引用了。

(6)对象拷贝
survivor区域对象的调整。

(7)Reference Processing

额外会做的事

G1记录每个阶段的时间,用于后期自动调优。比如说会记录EdenSurvivor的数量和GC时间,后期会根据我们之前设定的暂停目标来自动调整Region数量。
但是我们设置暂停目标越短,年轻代的Region数量就越少。但这可能会导致Fully young GC频繁发生。

2、Old GC

当堆用量达到一定程度时,就会触发old GC。可以通过以下参数进行设置:

-XX:InitatingHeapOccpancyPercent=45

old GC有一个很大特点就是并发进行的。但它是如何在堆中不断变化的情况下,确定哪些是要清理的垃圾对象呢?

三色标记算法

这种算法实现了在不暂停应用线程的情况下进行并发标记,标记过程过如下:
(1)将GC Root对象记录为黑色,其直接引用对象记录为灰色,并将这些灰色对象放入一个队列中

file
(2)从队列取出对象,将其标为黑色,将其引用对象记录为灰色,再放入队列中
file
(3)直到队列中无对象为止
file

三色标记算法的缺点:Lost Object Problem

三色标记算法并没有完全将所有的活对象都标记出来,这就是Lost Object Problem问题。比如说:
(1)刚开始时

file

(2)在即将描述将C标为灰色的一刹那

file

此时,C依然是活对象,但是已经无法将其标记了。

(3)结果

file

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阶段,主要是解决SATBReference processing
还会有一个Cleanup阶段,用于回收全为空的区

(5)恢复应用线程

3、Mixed GC

我们直到CMS最大的缺点就是无法进行压缩操作,而G1就通过Mixed GC解决了这个问题。

Mixed GC没有固定触发条件,他是根据Fully young GC收集的信息和我们配置的时间来决定,是否触发Mixed GC。它会根据暂停目标,来优先选择垃圾最多的Old Region来执行。

Mixed GC会选择若干个Region进行,默认是选择1/8Old RegionEden RegionSurvivor Region

Mixed GC的过程跟Fully young GC的过程相同,都是:STWParallelCopying

原博客地址

上一篇 下一篇

猜你喜欢

热点阅读