G1垃圾回收器

垃圾回收器的发展历程

file

背景

0一、G1解决的问题

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

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

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

G1还解决的问题是:CMSGC后,没法compact内存。多线程

0二、G1达成的目标

(1)减小因为STW而带来的程序延迟时间,作到伪实时、低延时、可设定目标;
可设定目标是指可以设置GC最大STW停顿的时间,G1会尽可能达成目的,但不必定达成。并发

-XX:MaxGCPauseMillis=N

默认状况下是250毫秒oracle

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

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

G1垃圾回收器

1、G1内存布局

G1再也不遵循以前的堆中对象的分代排列,而是将堆分红若干个等大的区域。
file线程

而是变成:
file3d

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

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

2、G1的介绍

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

  • Full young GC:年轻代GC算法:STWParallelCopying
  • 老年代GC算法:Mostly-concurrent markingIncremental compaction
  • Mixed GC:混合GC

3、G1引来的问题

问题描述

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

  • 老年代对象可能持有年代代的引用(跨代引用)
  • 不一样的Region间的互相引用
    跨代/跨Region引用

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

问题解决

Remembered SetCard Table
file

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

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

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

解决方法的实现

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

JVM开始更新指针时,就通过如下几步:

  • 标记CardDirty
  • Card存入Dirty Card Queue队列中

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

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

  • White
    表示没有应用线程往队列里听任务,什么事都不用干。

  • Green
    此时Refinement线程开始被激活,开始更新RS-XX:G1ConcRefinementGreenZone=N

  • Yellow
    此时所有的Refinement线程都被激活,来更新RS-XX:G1ConcRefinementYellowZone=N

  • Red
    这个时候,应用线程也开始参与排空队列的工做。-XX:G1ConcRefinementRedZone=N

4、GC算法的过程

一、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频繁发生。

二、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)恢复应用线程

三、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

原博客地址

相关文章
相关标签/搜索