深入理解G1回收器的工作原理与调优【料视出品】

33人 购买 好评度 -
用手机看

扫一扫继续用手机看

  • 微信扫码

  • QQ扫码

下载手机APP
收藏
  • 第一期
更多班级

第一期

支持随到随学,24年04月过期

¥97.22

本班因教学质量问题暂时不能报名。 查看详情

课程因违反平台规定暂时不能报名。

立即购买

课程概述

目录

往期学员作业()

评论

老师介绍

  • Bob老师

    Bob老师

    16年java开发经验,10的架构经验,曾就职于当当等大型互联网企业。熟练掌握分布式、高并发、高可用等技术。掌握支付平台、理财业务等业务架构。
  • 阿亮老师

    阿亮老师

    擅长JavaSE、Android、JDBC、JavaWeb以及Spring、JPA、Hibernate、MyBatis、SpringMVC、Struts2、Struts1、Lucene、WebService等开源技术,以及Oracle、MySQL等数据库技术。
简  介 虽然说Z回收器的效率要远远高于G1回收器,但当前来讲,应用最多的还是G1回收器,理解回收器的工作原理与调优是每个java工程师的基本功。理解回收器的原理,在工作中你可以快速定位错误,找到问题的根源。学习回收器的原理让你更进一步了解JVM的底层工作原理,理解其原理才能编写出高质量的代码。


相对于G1之前的回收器G1回收器要相对复杂些,也不太好理解,本课程能帮助深入的学习G1回收器的工作原理与调优。

为了便于大家理解,本课程分以下几个步骤循序渐进的方式为您讲解。

基本概念-->工作流程概览-->重要概念-->工作流程详解-->其它相关概念与调优


基本概念

可达性分析算法


写屏障

写前屏障与写后屏障


Marking bitmap
 
G1统计一个region的存活的对象,主要依赖于bitmap的标记。


G1之前的内存模型
 
Region
 

1.    Eden regions(年轻代-Eden区)
2.    Survivor regions(年轻代-Survivor区) 
3.    Old regions(老年代)
4.    Humongous regions(巨型对象区域)
5.    Free resgions(未分配区域,也会叫做可用分区)-上图中空白的区域

Region可以说是G1回收器一次回收的最小单元。即每一次回收都是回收N个Region。这个N是多少,主要受到G1回收的效率和用户设置的软实时目标有关。每一次的回收,G1会选择可能回收最多垃圾的Region进行回收。与此同时,G1回收器会维护一个空间Region的链表。每次回收之后的Region都会被加入到这个链表中。


G1HeapRegionSize参数进行设置

Collection Set

Collection Set(CSet)是指,在Evacuation阶段,由G1垃圾回收器选择的待回收的Region集合。G1垃圾回收器的软实时的特性就是通过CSet的选择来实现的。对应于G1算法的两种模式fully-young generational mode和partially-young mode。

Remember Set

RS(Remembered Set)是一种抽象概念,用于记录从非收集部分指向收集部分的指针的集合。

 

 G1回收器引入了一种新的结构,CT(Card Table)——卡表。

工作流程概览

回收分为两种模式:
Young-only phase
    fully-young generational mode:也被称为young GC;

Space-reclamation phase
     partially-young modes:也被称为Mixed GC,

两个工作阶段
Marking cycle phase:标记阶段,该阶段是不断循环进行的;
Evacuation phase:该阶段是负责把一部分region的活对象拷贝到空Region里面去,然后回收原本的Region空间,该阶段是STW(stop-the-world)的;

Marking cycle phase分为五个阶段

Initial marking phase
暂停所有应用线程(STW),标记从 GC Root开始直接可达的对象


Root region scanning phase
在G1 之前的不论是Parallel Collector还是CMS,都没有这么一个步骤,为什么要增加这么一个步骤呢?

这个过程称为根分区扫描(Root Region Scanning),这个时间,同时扫描到的 Suvivor 分区也被称为根分区(Root Region)。

这一个过程是并发进行的。但是该过程要在下一个young GC开始之前结束。如果第二次的young GC启动了,那么这个过程中,survivor region就可能发生变化。这个时候执行root region phase就会产生错误的结果。

Concurrent marking phase
在并发标记阶段,因为应用还在运行,所以可能会有引用变更,包括现有引用指向别的对象,或者删除了一个引用,或者创建了一个新的对象等。G1采用的是使用SATB的并发标记算法。

Remark phase
也叫final marking phase。该阶段只需要扫描SATB(Snapshot At The Beginning)的buffer,处理在并发阶段产生的新的存活对象的引用。


要结束标记的过程,要满足三个条件:

concurrent marking已经追踪了所有的存活对象;
marking stack是空的;
所有的log都被处理了;



Cleanup phase
清理阶段。该阶段会计算每一个region里面存活的对象,并把完全没有存活对象的Region直接放到空闲列表中。在该阶段还会重置Remember Set。该阶段在计算Region中存活对象的时候,是STW(Stop-the-world)的,而在重置Remember Set的时候,却是可以并行的;

Evacuation

Evacuation阶段STW的,大概可以分成两个步骤:第一个步骤是从Region中选出若干个Region进行回收,这些被选中的Region称为Collect Set(简称CSet);而第二个步骤则是把这些Region中存活的对象复制到空闲的Region中去,同时把这些已经被回收的Region放到空闲Region列表中。


重要概念解读

Remember Set和Card Table

RS(Remember Set)是一种抽象概念,用于记录从非收集部分指向收集部分的指针的集合。


装入队列后赋值动作完成,然后由ConcurrentG1RefineThread去计算card所在的Region,如果这个Region是新生代,就不用去理会了,如果是老年代的就将其放入Rest.

Card table是Rset的一种实现。


Remembered Set并发问题

使用hash表来实现Rset去除重复。

三种粒度

由于RSet的记录要占用分区的空间,如果一个分区非常”受欢迎”,那么RSet占用的空间会上升,从而降低分区的可用空间。G1应对这个问题采用了改变RSet的密度的方式,在PRT中将会以三种模式记录引用:

稀少:直接使用hash表,key为region的index,value为card数组

细粒度:记录引用的card的set,其中每一位代表一个card

粗粒度:只记录引用的region


漏标问题

首先我们看下三色标记法
 

并发标记中的漏标


出现漏标的必要条件是以下两个情况同时发生:
    mutator使黑色对象直接引用了白色对象;
    mutator删除了从灰色对象到白色对象之间的所有引用路径。



三色不变式与增量更新写屏障

为了解决漏标问题,需要破坏上文所述的两个情况,亦即强制回收器满足如下两个条件之一。

强三色不变式:保证永远不会存在黑色对象到白色对象的引用(破坏情况1)。
弱三色不变式:所有被黑色对象引用的白色对象都处于灰色保护状态(破坏情况2)。


SATB(snapshot-at-the-beginning)

SATB(snapshot-at-the-beginning),是最开始用于实时垃圾回收器的一种技术。G1垃圾回收器使用该技术在标记阶段记录一个存活对象的快照。然而在并发标记阶段,应用可能修改了原本的引用,比如删除了一个原本的引用。这就会导致并发标记结束之后的存活对象的快照和SATB不一致。



G1的垃圾回收器的写屏障使用一种两级的log buffer结构:


Marking bitmap和TAMS

Marking bitmap
是一种数据结构,其中的每一个bit代表的是一个可用于分配给对象的起始地址。
bitmap 


TAMS(top at mark start)
变量,是一对用于区分在标记阶段新分配对象的变量,分别被称为previous TAMS和next TAMS。在previous TAMS和next TAMS之间的对象则是本次标记阶段时候新分配的对象。如图:
 



回收过程详解


G1触发Evacuation的原则大概是:



FullGC
G1在对象复制/转移失败或者没法分配足够内存(比如巨型对象没有足够的连续分区分配)时,会触发FullGC。FullGC使用的是stop the world的单线程的Serial Old模式,所以一旦触发FullGC则会STW应用线程,并且执行效率很慢。JDK 8版本的G1是不提供Full gc的处理的。对于G1 GC的优化,很大的目标就是没有FullGC。


TLAB(Thread Local Allocation Buffer)

为什么会有TLAB?
    TLABWasteTargetPercent=1
    TLABSize=0
指针碰撞法分配
    TLABRefillWasteFraction=64
如何判断TLAB存满

如何调整RefillWaste大小
    TLABRefillWasteFraction=64,
    TLABWasteIncrement=4(默认值为4)

超过RefillWaste对象与巨型对象的缓慢分配

TLAB清理TLAB与申请缓慢分配




TLAB相关设置
-XX:+UseTLAB
-XX:TLABWasteTargetPercent=1 设置占用比例 
-XX:+PrintTLAB来查看TLAB空间的使用情况
-XX:-ResizeTLAB
-XX:TLABRefillWasteFraction=64
-XX:TLABWasteIncrement=4
调优实践

G1参数设置与说明

"-XX:+UseG1GC"(jdk 9以后是G1是默认回收器)
 

"-XX:MaxGCPauseMillis
 

-XX:G1HeapRegionSize

-XX:InitiatingHeapOccupancyPercent  
 
-XX:G1NewSizePercent -XX:G1MaxNewSizePercent
 
-XX:NewRatio

-XX:SurvivorRatio

-XX:ConcGCThreads

-XX:ParallelGCThreads
 
-XX:G1MixedGCLiveThresholdPercent

-XX:GCTimeRatio

-XX:G1HeapWastePercent

-XX:G1OldCSetRegionThresholdPercent

-XX:G1MixedGCCountTarget

-XX:G1ReservePercent


-XX:SoftRefLRUPolicyMSPerMB

-XX:MaxTenuringThreshold

-XX:TargetSurvivorRatio

-XX:+ReduceInitialCardMarks

-XX:+ParallelRefProcEnabled

-XX:G1RSetUpdatingPauseTimePercent

-XX: G1SummarizeRSetStatsPeriod

TLAB相关:
23、-XX:+UseTLAB
24、-XX:+ResizeTLAB 
25、-XX:MinTLABSize  
26、-XX:TLABSize  
27、    -XX:TLABWasteTargetPercent,
28、    -XX:TLABRefillWasteFraction,
29、    -XX:TLABWasteIncrement,
30、    -XX:GCLockerRetryAllocationCount

日志相关:
GC日志相关设置

通过这些参数可以对JVM的GC日志输出进行配置,方便分析

jkd8之前:

-Xloggc  

UseGCLogFileRotation
滚动GC日志文件,须配置Xloggc


NumberOfGCLogFiles

GCLogFileSize

PrintGCDetails

PrintGCDateStamps

PrintTenuringDistribution

PrintGCApplicationStoppedTime

PrintHeapAtGC


Jdk9以后默认配置
-Xlog:all=warning:stdout:uptime,level,tags



分为四个部分用“:”隔开


G1GC日志解读

Jdk8之前的日志
Jdk9之后的日志
年青代回收
混合回收
标记过程
To-space exhausted
Evacuation Failure
FullGC


针对不同问题进行调优

如何减少FullGC

巨型对象问题 

增加堆大小

增加并发标记线程

老年代预留空间浪费

强制回收器尽早开始标记

System.gc()


排查对耗时过长的原因

处理系统用时过长

处理Finalizable对象处理时间过长

解决年轻代回收用时过长
    
解决混合回收用时过长

Rset扫描或更新用时过长如何处理

如何提高吞吐量

资料下载报名后支持下载

* 课程提供者:料视教育