JVM 垃圾回收器 ZGC

JAVA内存模型

在这里插入图片描述
在这里插入图片描述

JVM线程
  • OSThread
    JVM 对不同系统的线程抽象
  • Thread
    Thread 是 C++ 定义的线程基类,除了 OSThread 类,作为其他线程的基类,它包含了 OSThread 对象的指针
  • JavaThread:
    Java 层创建的线程
    CompilerThread :C1 CompilerThread 和C2 CompilerThread
  • VMThread:
    单例原生线程VMThread
    维护一个vm操作队列(VMOperationQueue):GC,heapdump。
    VM_CMS_Operation
    VM_CGC_Operation
    VM_GC_Operation
  • WorkerThread
    子类GCTaskThread、GangWorker是用来执行gc任务的

在这里插入图片描述

Parallel:VMThread和GCTaskThread

在这里插入图片描述

CMS:VMThread和GangThread

在这里插入图片描述

ZGC:ZWorker和RuntimeWorker

在这里插入图片描述

JVM线程-Stop The Word

Stop the World 是JVM等待所有的用户线程进入safepoint并且阻塞,做一些全局性操作的行为。

  1. JIT相关
  2. Class redefinition
  3. 取消偏向锁 ;
  4. Various debug operation
    -XX:+PrintGCApplicationStoppedTime系统停顿时间
    -XX:+PrintSafepointStatistics
    -XX: PrintSafepointStatisticsCount=1
什么是ZGC

A Scalable Low Latency Garbage Collector-可伸缩的低延迟垃圾收集器
目标:
1,TB级别内存(最多4TB)
2,最大10ms的暂停时间(STW)
3,最大15%的应用吞吐量减低
4,支持NUMA
特点
1,不支持分代,仅支持Linux 64位系统
2,设计了分页管理支持TB级内存,
3,快速的进行并发标记和并发移动(Color Pointers)
4,设计了读屏障,实现了增量标记
5,设计了物理内存和虚拟内存两级内存管理

GC屏障 (GC Barriers)
Write Barrier:往堆写入引用时,GC需要执行一些额外操作,对于在Java中:obj.field=xxx。常见于分代GC,比如老年代引用新生代,会维护card table。G1在维护Rset的时候使用了write barrier
Load barrier:在从堆读取引用时,GC需要执行一些额外操作。对于在Java中,也就是像执行这样的代码Object xxx=obj.field时才需要额外操作。
ZGC的Load Barrier的作用:
运用在从堆里面加载对象,而不是在后续访问对象时。
判断指针的当前颜色。如果是坏颜色,就修复它。

Heap Regions
堆的分区又被称作ZPages
1,动态创建和销毁,
2,动态大小,都是2M的整数倍(目前对x86_64)。
3,ZGC有3种不同的页面类型:
小型 (2MB大小)
中型 (32MB大小)
大型 (2MB的倍数):大于4MB的一个对象。
4,区分物理内存和虚拟内存。
足够的虚拟内存(ZGC总是4TB)
物理内存可以扩展到最大堆大小(使用-Xmx设置)

在这里插入图片描述

着色指针Colored Pointers
ZGC不支持compressed oops和32位系统平台。因为部分元数据存放在64指针之中
前42位为定位对象地址的,紧接着4位分别为finalizable , remapped , marked1和marked0 标志位。更高的位目前都是0。
通过marked1和marked0判断是否被标记
通过remapped知道没有指向重定位后的集合中
通过finalizable判断是否只能通过Finalizer才能访问到。
读屏障可以实现对象的在这里插入图片描述##### ZGC过程-标记阶段

  • 标记阶段
    Live Map是一个位图(bitmap) ,用于存储给定索引处的对象是否可达和/或最终可达。
    ZGC把内存分为多个Striped,Striped在多个线程上进行分配,每个线程负责各自的Striped,当一个线程完成时,也会处理其他现在未完成的Striped
    在这里插入图片描述

  • 标记阶段-初始标记
    第一阶段是STW,其中GC roots被标记为活对象。
    如果一个对象不能通过遍历从roots开始的对象图来访问,那么应用程序也就无法访问它,则该对象被认为是垃圾。从roots访问的对象集合称为Live集(live-set)。GC roots标记步骤非常短,因为roots的总数通常比较小。
    在这里插入图片描述

  • 标记阶段-并发标记
    初始标记阶段完成后,应用程序恢复执行,ZGC开始下一阶段.
    该阶段同时遍历对象图并标记所有可访问的对象。
    应用程序线程中的load-barrier读屏障针使用掩码测试所有已加载的引用,该掩码确定它们是否已标记或尚未标记,将未标记的引用推送到线程局部标记缓冲区。只要此缓冲区已满,GC线程就可以获得此缓冲区的所有权,并以递归方式遍历此缓冲区中的所有可到达对象。在应用程序线程中标记只是将引用推送到缓冲区,GC线程负责遍历对象图并更新Live map.

  • 标记阶段-标记结束
    在遍历完成之后,有一个最终的,时间很短的的Stop The World阶段,清空缓存区,解决并发阶段引用变化的情况。

ZGC过程-重定位阶段

重定位阶段-选择重定位pages
重定位前,此时需要解决的问题是要重定位哪些pages,称为Relocation Set。
生成了forwarding tables记录指针变化。
在这里插入图片描述 - 定位阶段-重定位roots
选择重定位集后,会出现一个Stop The World暂停,其中ZGC重定位该集合中root对象,并将他们的引用映射到新位置。
遍历所有的roots,并进行迁移,指针记录到forwarding tables

在这里插入图片描述

(1)重定位阶段-重定位roots指向的对象
移动root后,下一阶段是并发重定位。
在此阶段,GC线程遍历重定位集并重新定位其包含的页中所有对象。
如果应用程序线程试图在GC重新定位对象之前加载它们,那么应用程序线程也可以重定位该对象,这可以通过读屏障(在从堆加载引用时触发)实现。
一旦一个page已经没有元素了,此时它就可以马上被重新使用。不用等到GC所有阶段结束。
在这里插入图片描述

(2)重定位阶段-重定位root指向的对象
同时注意,此时其他指向5的对象指针仍然没有变,当这些对象需要访问5时如果不做特殊处理就会出错,这就是load barrier的作用,它会让当前对象对5的引用重新指向新的地址。

在这里插入图片描述
重定位阶段结束,同时也意味着到此时,GC周期结束。但是就会发现上面还有部分指针没有remap到正确的位置。如果需要进行修复,那也要进行一次全部遍历。如下图:进行一次并发重映射。
但是就会发现,第一个并发标记和最后的并发重映射都是一个对象遍历,所有这两个阶段可以合并。合并就设计到一个问题,怎么判断当前的引用是上次GC循环没有remap的呢?这就需要一个标志,这也就是引用标志中有两位特殊的标志位marked1和marked2。
在这里插入图片描述

JDK 11中ZGC的不足

1,实验性质
2,ZGC仅实现了单代内存管理,也就是说没有考虑热点数据与冷数据,这个在商业的C4已经支持。
3,C2的支持还不够完善;
4,不支持Graal, HDSB等功能
5,ZGC并不是下一代GC唯一的发展方向:Shenandoah

http://ju.outofmemory.cn/entry/367911
https://blog.csdn.net/qq_42882671/article/details/82622328
https://blog.longyb.com/2018/10/04/Zgc_Introduction/
https://dinfuehr.github.io/blog/a-first-look-into-zgc/
过程概览地址:http://hg.openjdk.java.net/zgc/zgc/file/59c07aef65ac/src/hotspot/share/gc/z/zDriver.cpp#l316

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章