天天看点

java虚拟机--低延迟垃圾收集器(ZGC收集器)

  • ZGC(Z Garbage Collector)是一款在JDK11中新加入的具有实验性质的低延迟垃圾收集器

ZGC特征

ZGC基于Region的内存布局,(暂时)不设分代,使用读屏障、染色指针和内存多重映射等技术来实现可并发的标记-整理算法,以低延迟为首要目标的一款垃圾收集器

读屏障理解:这与其他GC使用的写屏障形成对比,例如G1。读屏障的工作是检查引用的状态,并在将引用(或者甚至是不同的引用)返回给应用程序之前执行一些工作。 在ZGC中,它通过测试加载的引用来执行此任务,以查看是否设置了某些位。 如果通过了测试,则不执行任何其他工作,如果失败,则在将引用返回给应用程序之前执行某些特定于阶段的任务。

ZGC中Region特征

ZGC中的Region具有动态性–动态创建和销毁,以及动态的区域容量大小。Region可分为一下容量:

  • 小型Region: 容量固定为2MB ,用于放置 对象大小 < 256KB
  • 中型Region:容量固定为32MB,用于放置256<= 对象大小 <4MB
  • 大型Region:容量动态变化,但是2MB的整数倍,用于存放 对象大小 >= 4MB

    染色指针

    • 标记阶段,标记信息的记录形式

      Serial收集戚把对象的标记信息记录到对象头上,G1、Shenandoah把标记信息记录在与对象相互独立的数据结构上(BitMap),而ZGC使用染色指针,将标记信息记录到引用对象的指针上。

    • ** 染色指针的原理**

      染色指针是一种将少量额外信息存储到指针的技术。Linux64位指针的高18位不能寻址,剩余的46位可以寻址,将其高4位提取出来存储4个标志信息,故ZGC可以管理的内存为4TB(2的42次幂)通过这些标志位,虚拟机可以直接从指针中看到其引用的对象的(1)三色标记装填(2)对象是否进入重分配集(即被移动过)(3)是否只能通过finalize()方法才能被访问到。

    • 染色指针的优势
      • (1)能够逐个释放Region:染色指针可以使一旦某个Region的存活对象都被移走之,这个Region立即就能够被释放和重用,而不必向Shenandoah那样,等待整个堆中所有指向该Region的引用都被修正后才能清理。
      • (2)减少内存屏障使用数量:ZGC中未使用写屏障,只使用读屏障。写屏障为了维护更新记忆集,但ZGC中未使用分代,而是全局扫描,没有跨代引用,故不用写屏障。
      • (3)可作为可扩展存储结构:染色指针可以作为一种可扩展的存储结构来记录更多与对象标记、重定位过程相关的数据,以便以后进一步提高性能。
    • 染色指针的实现前提

      使用zgc,这样重新定义了内存中某些指针的其中几位,但是代码最终会转化为机器指令给cpu,cpu会把整个指针都视为一个内存地址来处理,不会区分zgc自己定义的4个标志位。

      解决方案:利用多重映射:linux/x86-64平台上的zgc使用多重映射将多个不同虚拟内存地址映射到同一个物理内存地址上,这是一种一对多的映射,这样ZGC在虚拟内部才能中看到的地址空间比实际堆内存容量更大。把染色指针中的标志位看作是地址分隔符,只要将这些不同地址段都映射到同一个物理内存空间,经过多重映射转换后,染色指针就能正常寻址了。

ZGC工作流程

(1)并发标记

  • 内容:与G1、Shenandoah一样,并发标记是遍历对象图做可达性分析阶段,前后要经过类似于G1、Shenandoah的初始标记、最终标记的短暂停顿。
  • 特点:ZGC的标记是在指针上而不是在对象上进行的,标记阶段会更新染色指针中的Marked0,Marked1标志位。

    (2)并发预备重分配:

  • 内容:根据特定的查询条件统计得出本次收集过程需要清理哪些Region,将这些Region组成重分配集。
  • ZGC划分Region的目的并非为了像G1那样做收益优先增量回收。相反,ZGC每次回收都会扫描所有的Region,用范围更大的扫描成本获取省去G1中记忆集的维护成本。因此,ZGC的重分配集只是决定里面的存活对象会被重新复制到其他的Region中,里面的Region会被释放,而并不能说回收行为就只是针对这个集合里面的Region进行,因为标记过程是针对全堆的。
  • JDK12的ZGC中开始支持的类卸载以及弱引用的处理,也是在这个阶段完成。

    (3)并发重分配:

  • 内容:将重分配集中的存活对象复制到新的Region上,并为重分配集中的每个Region维护一个转发表,该转发表记录了从旧对象到新对象的转向关系。
  • 特点:ZGC指针的治愈能力:由于染色指针,ZGC收集器从引用上就明确得知一个对象是否处于重分配集之中,如果用户线程此时(即对象复制到新Region后的第一次访问对像)并发访问位于重分配集中的对象,这次访问会被预置的内存屏障截获,并同时修改更新该引用的值,使其指向新的对象。这样做的优势:(1)第一次访问旧对象会陷入转发,而Shenandoah的转发指针每次对象访问都会进行转发。(2)由于染色指针的存在,一旦重分配集中某个Region的存活对象都复制完毕,这个Region就可以立刻释放(但是转发表害的的留着,哪怕堆中还有很对指向这个对象的未更新的指针也没关系,只要转发指针在,那么他么第一次访问旧对象的时候会被内存屏障截获,然后更新引用为新对象的地址

    (4)并发重映射

  • 内容:修正整个堆中指向重分配集中旧对象的所有引用。
  • 特点:由于刚才提到的指针治愈能力,当第一次访问重分配集中的对象时要经过转发表转发,同时更新该指针指向新的对象地址,所有修正整个堆中的指针不是一件迫切的事情。因此ZGC把并发重映射阶段的工作,合并到下次垃圾收集循环中的并发标记阶段完成。

ZGC与G1、Shenandoah比较

  • ZGC与Shenandoah一样可以做到几乎整个收集过程全程可并发,短暂停顿也只与GC Roots大小相关而与堆无关,因为同样可以实现。
  • ZGC优势:没有使用记忆集,连分代都没有,因此不需要写屏障,所有给用户线程代来的运行负担也要小得多。
  • ZGC劣势:ZGC 没有分代,这样限制了它承受的对象分配速率不会太高。对象分配的速率太高,而新对象很难进入当次垃圾回收范围,这样会产生大量的浮动垃圾,堆中剩余可以腾挪的空间就越来越小。解决这个问题,就需要对堆进行分代。

继续阅读