1.ZGC简介
1.JDK11推出的低延迟垃圾回收器
2.支持4TB级别的堆
3.停顿不超过10ms,不随着堆增加而增加
2.垃圾回收器组合
1.Serial + SerialOld
2.PS:Parallel Scavenge +ParallelOld
3.CMS:ParNew + CMS
4.G1
5.ZGC
3.GCRoot
1.局部变量对象引用
2.静态变量对象引用
3.常量对象引用
4.JNI指针(native)
4.CMS
1.初始标记(STW)
1.只标记GCRoot对象
2.不包括GCRoot指向的对象链路(查询链路上所有对象效率比较低)
3.因此这步停顿时间较短
2.并发标记
1.垃圾回收线程和应用线程一起运行
2.垃圾回收线程标记GCRoot链路上的所有对象
3.并发标记过程中有可能会产生新的垃圾或不是垃圾的对象(对垃圾重新引用)
3.重新标记(STW)
1.CMS会内置记录在并发标记期间那些被新建的对象或者有变动的对象,因此重新标记阶段不需要再重新标记所有对象,只对并发标记阶段改动过的对象做标记即可。
4.并发清理(CMS的问题所在)
1.CMS存在的问题:
1.使用mark-sweep(标记清除)算法会出现内存碎片(大对象无法分配)
2.但是JVM分配对象需要连续的空间
3.当大对象因为内存碎片无法分配下去就会将CMS转化成SerialOld算法进行标记整理
4.导致一旦触发SerialOld会变成单线程,本次STW会很久
5.ZGC
1.传统垃圾回收器和ZGC
1.传统GC需要遍历堆空间里面的对象头markword进行可达性分析
2.ZGC直接对指向对象的指针进行着色,无需访问对象头
2.指针着色(仅支持64位操作系统)
1.32位操作系统只支持4g内存空间的原理
1.只支持32位的指针
2.因此指针寻址访问只有2^32 = 4g
1.指针存储对象的内存地址
3.ZGC使用指针高位的空间作为着色
3.指针结构(64位)
1.一个指针对象是64位
2.0-41位代表指针的实际内存地址(最多可以右4TB)
3.42-45位代表指针的着色(哪一个有颜色哪一个就为1)
1.M0
2.M1
3.预留
4.Remapped
4.46-63是预留指针位置
4.流程
1.new出对象,Remapped置为1
2.初始对象(STW)
1.标记GCroot指针
2.将GCrootM1设置为1,Remapped置为0
3.表示该对象是可达的,不是垃圾
3.并发标记
1.标记GCroot可达的对象
2.将GCroot的可达对象的指针M1设置为1,Remapped置为0,表示不是垃圾
3.除了标记可达还要对上一轮并发转移的转移表内对象转移进行重定向,重定向成功并且不是垃圾的对象将其指针M0置为0,M1置为1,表示已转移。
4.再标记(STW)
1.修正并发标记出现的改变
5.并发转移准备
1.分区(对G1的分区进行优化)
1.小页面 2m
1.对象大小 < 256k
2.中页面 32m
1.对像大小 256k > 4m
3.大页面 >32m
2.筛选
1.标记完后知道哪些区域有垃圾
2.优先回收小页面
6.初始转移(STW)
1.转移GCroot
2.对内存空间进行整理
7.并发转移(为了解决CMS内存碎片的问题)
1.将GCroot可达对象的内存转移位置记录到转发表
2.问题:如何防止并发时对象移动造成对象赋值的空指针
1.读屏障
/**
* 读屏障
*/
public class ZTest {
ZTest instance = null;
public static void main(String[] args) {
ZTest instanceA = new ZTest();
instanceA.instance = new ZTest();
//<load barrier> JIT插入读屏障
Object c = instanceA.instance;
}
}
3.转发表
1.并发转移有一张转发表,记录对象的目前内存位置和需要转移的位置
2.只有触发读屏障就会使用到转发表进行对象转移,否则等下一轮GC并发标记阶段才会进行对象的重定向
3.下一次GC并发标记将对象转发表处理完且还存活指针中M0置为0,M1置为1