ZGC简介
JDK14
2020年3月份,JDK14发布,可能我们大部分小伙伴的生产环境还停留在JDK8。目前LTS版本的JDLK主要是7,8,以及11。其他版本的支持周期都比较短,一般不建议在生产环境中使用的,不过这不影响我们了解最新的JDK的一些特性,这些可能是后面Java语言的发展方向。这次JDK14就带来了非常多的新特性,主要如下:
- Pattern Matching for instanceof (Preview)
- Packaging Tool (Incubator)
- NUMA-Aware Memory Allocation for G1 垃圾收集中分层,冷对象移出,需要硬件层面的支持。
- JFR Event Streaming
- Non-Volatile Mapped Byte Buffers
- Helpful NullPointerExceptions。这个非常有用,方便程序猿们定义空指针异常。
- Records (Preview)
- Switch Expressions (Standard)
- Deprecate the Solaris and SPARC Ports
- Remove the Concurrent Mark Sweep (CMS) Garbage Collector。 移除了CMS。
- ZGC on macOS。 ZGC支持Mac
- ZGC on Windows。 ZGC支持windows
- Deprecate the ParallelScavenge + SerialOld GC Combination
- Remove the Pack200 Tools and API
- Text Blocks (Second Preview)
- Foreign-Memory Access API (Incubator)
可以看到jdk14带来的新特性还是比较多的。其中有些是Preview和Incubator,暂时还不是很成熟,令我比较惊讶的是JDK14居然会移除CMS垃圾收集器,反而重点增强了ZGC。ZGC还是第一次见到,它到底是什么,有什么魔力能获得JDK如此大的支持。下面就和大家一起了解一下ZGC。
ZGC是什么
- ZGC是一个低延迟高并发的垃圾收集器。与传统的CMS以及G1不同,ZGC基本上在所有的地方并发执行。只在初始Marking的时候需要STW,而这部分实际是花费时间并不多。其设计目标是支持TB级内存容量,暂停时间小于10ms,对整个系统的吞吐率的影响小于15%。
基本原理
ZGC能够实现以上目标主要依靠两种技术,Colored Pointer以及Load Barrier。
Colored Pointer 着色指针
ZGC主要是利用指针的64位来表示Finalizable、Remapped、Marked1、Marked0(因此智能支持64位平台,无法在32位机器中运行)。其中的42位是用来存储对象的,因此能够支持最多4TB的内存管理,中间四位就是着色指针,剩下的位数留待以后扩展。
Load Barrier 读屏障
Load Barrier是内存屏障的一种,内存屏障硬件层的概念,不同的硬件平台实现内存屏障的方式是不一样的。JVM通常会屏蔽这些差异,提供统一的内存屏障指令。内存屏障主要有两个作用
- 阻止屏障两侧的指令重排序/
- 强制将缓冲区的脏数据写回主内存,让缓存中响应的数据失效。
在指令前插入Load Barrier,可以让高速缓存中的数据失效,强制从新从主内存加载数据;
通过在指针上坐标记,访问指针的时候加入Load Barrier,如果对象被GC移动,指针的颜色就会发生变化,Load Barrier会将指针地址更新为有效地址再返回。因此永远只会有单个对象读取时有概率被减速,不存在为保持应用与GC一致直接STW。
回收流程
大的流程上看分为两个阶段:标记以及重定位。
标记
标记也分为三个阶段:
- STW:GC roots被标记为Live对象,通过GC roots可以访问堆上其他对象。不能通过GC roots访问的对象为垃圾对象,由于一般情况下GC roots比较小,因此标记步骤非常快。标记完成之后应用程序开始执行。
- 第二阶段并发变了对象图,并标记所有可以访问的对象。读屏障针使用掩码测试所有已加载的引用,该掩码确定它们是否已标记或尚未标记,如果尚未标记引用,则将其添加到队列以进行标记。
- 最终时间很短的STW,处理一些边缘情况。
重定位
重定位涉及移动活动对象以释放堆内存。
-
root移动:ZGC也将内存分为多个页面,需要注意的是每个页面的大小是不同的(有点类似go的垃圾收集)。并发选择一组需要重定位活动对象的页面,出现一个STW,zgc定位改集合中的root对象,并将他们的引用映射到新位置。
-
并发重定位:GC线程遍历重定位集并重新定位其包含的页中所有对象。
- GC线程最终将对重定位中涉及到的所有对象重定位。
小结
zgc中碰到的几乎所有暂停都只依赖gc roots的大小。而不是实时堆的大小,这对目前或者以后的大内存的Java应用非常友好。这也是它性能很好的原因。与很多传统的收集器不同,ZGC也不区分新生代和老生带,这对性能调优也是一个好消息。另外附录2有一个非常好的图实例来介绍ZGC的收集过程,感兴趣的同学可以看一下。
使用
使用和调优是另外一个话题,目前工作中没有看到使用场景,想自己动手尝鲜的同学推荐一篇文章。