1. GC相关
1.1 什么是垃圾
没有任何引用指向的一个或者一堆对象
1.2 如何定位垃圾
-
引用计数(RefrenceCount): 计数变为0的时候就是垃圾,回收走
问题:循环引用 -
根可达算法(RootSearching):从根开始找,没有关联关系,回收走。
哪些内容算root,见下图
1.3 垃圾回收算法
- Mark-Sweep(标记清除)
算法相对简单,存活对象较多的情况下效率较高。
先标记,再清除,两次扫描,效率较低,容易产生碎片
- Copying(拷贝)
适用于存活对象较少的情况,只扫描一次,效率较高,且没有碎片。
空间浪费,异动复制对象,需要调整对象引用。
- Mark-Compack(标记压缩)
不会产生碎片,方便分配内存,不会产生内存减半。
扫描两次,需要移动对象,效率偏低
2. JVM内存分代模型(用于分代垃圾回收算法)
2.1 部分垃圾回收器使用的模型
- Epsiolon、ZGC、Shenandoah 之外的GC都使用逻辑分代模型
- G1 是逻辑分代,物理不分代
- 除此之外不仅逻辑分代,而且物理分代
2.2 新生代 + 老年代 + 永久代(1.7)Perm Generation / 元数据区(1.8)Metaspace
- 永久代存放元数据(class信息)
- 永久代必须制定大小限制;元数据区可设置也可以不设置,无上限,受限于物理内存
- 字符串常量1.7 在永久代,1.8在堆中
2.3 新生代 = Eden + 2个Suvivor区
- YGC回收之后,大多数的对象会被回收,活着的进入S0
- 再次YGC,活着的对象eden+s0 -> s1
- 再次YGC,eden + s1 -> s0
- 年龄足够 进入老年代
Parallel Scavenge:15
CMS:6
G1:15 - s区装不下,进入老年代
2.4 老年代
- 老年代满了,FGC (Full GC)
2.4 GC调优
- 尽量减少FGC
3. 常见的垃圾回收器
3.1 常见垃圾回收器
3.1 Stop-The-World(STW)
GC在后台自动发起和自动完成的,在用户不可见的情况下,在safe point情况下,将正常的工作线程全部停掉,即GC停顿,会带给用户不良的体验;
3.1.1 为什么要Stop-The-World
为了确保快照的一致性,不可以出现分析过程中对象引用关系还在不断变化的情况。
Stop-The-World是导致GC卡顿的重要原因之一。
3.2 常见垃圾回收器
3.2.1 Serial / SerialOld
Serial 年轻代
SerialOld 老年代,采用标记整理算法
串行回收,单线程、简单高效
垃圾回收时,必须暂停其他所有的工作线程,直到它结束(Stop The World)
年轻代 串行回收
3.2.2 Parallel Scavenge / ParallelOld
Parallel Scavenge(PS) 年轻代,采用复制算法
ParallelOld(PO)老年代,采用标记整理算法
并行回收,多线程收集器
默认垃圾回收器组合
3.2.3 ParNew
ParNew 年轻代
配合CMS的并行回收
同PS类似,PS的扩展版本,增加CMS扩展
3.2.4 CMS
老年代
标记清除算法,并发收集
垃圾回收与应用程序同时进行,降低STW的实现
3.2.5 G1
三色标记+SATB
逻辑分代,物理部分带
目标是用在多核、大内存的机器上
并发收集、并发标记、并发回收
大多数情况下可以实现制定的GC暂停时间,同时保持较高吞吐量
G1的内存区域不是固定的E或者O
- Region
大小是2的倍数,最大32 - 内存模型
Old 老年代区
Survivor 存活区
Eden 新生代区
Humoungous 大对象区,超过region 50%的对象 - Collection Set(CSet)
一组可被回收的分区(card)的集合。
再CSet中存活的数据会再GC过程中被移动到另一个可用分区,CSet中的分区可以来自Eden空间、survivor空间、或者老年代。
CSet会占用不到真个堆空间的1% - RememberedSet(RSet)
每一个region里面都有一个hash表,记录着其他region中的对象到本region的引用。
RSet的价值在于,使得垃圾收集器不需要扫描整个堆找到谁引用了当前分区中的对象,只需要扫描RSet即可。 - 新老年代比例
5%-60%
动态,一般不用手工指定,也不要手工指定,因为这是G1预测停顿时间的基准。 - GC何时触发
1.YGC
Eden空间不足
多线程并行执行
- FGC
Old空间不足
System.gc()
-
G1中的MixedGC
相当于CMS
XX:InitiatingHeapOccupacyPercent 默认值45%
当O超过这个值时,启动MixedGC -
MixedGC过程
初始标记 STW
并发标记
最终标记
筛选回收
-FullGC
JAVA 10以前是串行FullGC,之后是并行FullGC
3.2.6 ZGC
ColoredPointers + LoadBarrier
后续补充
3.3 一些概念
3.3.1 Card Table
由于做YGC时,需要扫描整个OLD区,效率非常低,所以JVM设计了CardTable, 如果一个OLD区CardTable中有对象指向Y区,就将它设为Dirty,下次扫描时,只需要扫描Dirty Card
在结构上,Card Table用BitMap来实现
3.3.2 三色标记算法
黑色:自身和成员变量均已标记完成
灰色:自身被标记,成员变量未被标记
白色:未被标记的对象
3.3.3 漏标
在并发标记过程中,黑色指向了白色,如果不对黑色重新扫描,则会漏标。
3.3.4 如何解决漏标
1、incremental update - 增量更新,关注引用的增加
把黑色重新标记为灰色,下次重新扫描属性(CMS使用)
2、SATB snapshot at the beginning - 关注引用的删除
当B->D消失时,要把这个引用推到GC的堆栈,保证D还能被GC扫描到(G1使用)
3.3.5 为什么G1使用SATB
灰色->白色 引用消失时,如果没有黑色指向白色,引用会被push到堆栈;下次扫描时,拿到这个引用,由于G1有RSet存在,不需要扫描整个堆去查找指向白色的引用,效率比较高,SATB配合RSet。