新一代垃圾回收器 ZGC 设计与实现 阅读笔记 第5章 ZGC 垃圾回收算法实现

ZGC 垃圾回收分为10个步骤

5.1.1 初始标记

         从根集合出发,标记根集合所有引用的对象,以这些对象作为第2步并发标记的出发点。

         如果不STW,只有并发标记,应用程序线程发生在标记线程之前,也就是应用线程根本不知道需要标记老对象,导致漏标

      初始标记中,仅把根集合直接引用的对象,找出来作为并发标记的输入。这样造成STW足够短

      1.垃圾回收过程相关初始化 :判断是否回收软引用,根据系统负载设置是否启用加强回收模式

          1.1 软引用回收 : 内存不足;元数据分配失败,也会优先进行垃圾回收,此时要求进行软引用回收;FGC

          1.2 加强回收模式 :内存不足,系统有积压的阻塞内存分配请求;显式调用System.gc触发;FGC触发软引用回收;元数据分配失败。

     2.标记阶段初始化

         2.1 TLAB 初始化 : 直接把以前使用的TLAB丢弃,重新分配新的TLAB缓存

         2.2 对象缓存页面初始化 : 丢弃已经存在的对象缓存,在进行下一次对象分配时,发现缓存全部为NULL,会先分配页面,再分配对象

         2.3 地址映射视图初始化 : JVM启动的时候分配的空间,分配空间时关联了虚拟地址和物理地址,这时候需要重新设置关联。 已经使用的预分配内存无须额外处理,尚未使用的内存空间重新映射到当前视图空间中。归还内存空间

    3.开始标记根集合:遍历JVM 根集合的每一个元素,把集合元素引用的对象标记为活跃对象。把这些对象加入标记栈,这个标记栈供并发标记使用。标记栈底层实现是数组,所有的并发标记线程,都会访问这一个标记栈。并行线程和并发线程不是同一个线程。

    3.1 标记栈按照标记条带管理,最多存在16个标记条带,即多个线程可能同时访问一个标记条带。对象进入标记栈的时候,会先计算对象在哪个标记条带中,然后放入对应的标记条带中。标记条带目前最主要的作用是限制工作线程使用的内存量

   初始标记中产生的ZMarkStripSet就是并发标记中的输入。

5.1.2 并发标记

    1.标记 :从待标记的对象列表出发,根据对象引用的关系图遍历对象的成员变量,递归进行标记。

                   对应用程序线程进行标记发生在读对象时,为了触发标记动作可以设计一个读屏障,在字节码层面或者编译代码层面给读操作增加一个额外的处理即可。

    2.计数: 标记阶段需要统计活跃对象的个数、活跃对象所占的内存空间等。ZGC是按照页面进行回收的

    3.结束标记:当所有应用程序线程中的标记条带都没有对象时,说明并发标记阶段可以终止。但是正如我们所说的,应用程序线程可能是一直标记新的对象,多次主动刷新和被动刷新仍然不能满足终止条件 。因此要保证所有的待标记对象都在本阶段完成比较,就必须有一个STW阶段。实际上并发标记结束时,还有其他一些工作需要STW,这些工作都被合并到再标记阶段。

5.1.3 再标记和非强根并行标记

       需要STW来结束标记,但是在再标记阶段,如果在一定时间内不能结束标记,还会再执行并发标记。

JVM定义了4层类加载器

    启动类加载器,扩展类加载器,系统类加载器,自定义类加载器

5.1.4 非强引用并发标记和引用并发处理

       1.引用的四种状态 : Active 活跃 (垃圾回收中通过可达性分析找到对象),或者是软引用,符合软引用活跃的规则

                                         pending : 对象进入pending列表,即将被送入引用队列

                                         Enqueued : 引用线程ReferenceHandler把pending列表的对象加入引用队列

                                         Inactive : 对象不活跃,可以被回收了

       3.发现引用

           3.1 软引用: 软引用对象满足软引用回收策略时才会被发现。 总是回收,根据LRU策略回收

           3.2 虚引用 : 标记位图设置位

       4.引用处理 :把标记阶段发现的对象重新梳理一遍,如果对象还是活跃的,就重新标记对象。如果对象不活跃,就把对象放入pending列表

5.1.5 重置转移集

       经过对强引用和非强引用的标记后,得到堆空间哪些对象可以回收,哪些还不可以回收,所以可以进入对象的转移阶段。

优先转移垃圾比较多的页面

5.1.6 回收无效的页面

       并发释放无效的虚拟内存页面 (虚拟内存对应的物理内存已经释放,那么虚拟地址就是无效的),只有在对象重定位完成,页面中的对象地址转移信息表无用之后,才可以释放页面。

5.1.7 选择待回收的页面

      1.筛选所有可以被回收的页面 

           本次垃圾回收启动之后新分配的页面,那么无须回收

           大页面不回收,中小页面,页面垃圾所占空间超过页面空间25%,才可能被回收

     2.选择垃圾比较多的页面作为页面转移集

 5.1.8 初始化待转移集合的转移表

 5.1.9 初始转移

     1.调整地址视图 :将地址视图从M0 或者 M1 调整为Remapped,说明进入真正的转移,此后所有分配的对象视图都是Remapped

      2.重定位TLAB :重用TLAB

      3.开始转移,从根集合出发,遍历根对象的直接引用的对象,对这些对象进行转移。初始转移也需要STW,因为有多个根集合,所以这一步也是并行执行的。

5.1.10 并发转移

           并发转移是根据第7步 产生的转移集中的每一个页面进行转移

           应用程序线程在转移时访问待转移的对象,此时应用程序线程 会先完成转移的任务,然后再访问对象,涉及到读屏障

    再标记主要是为了处理因应用程序线程标记对象,导致仍然有待标记的对象,并发标记结束前会尝试多次刷新以避免这种状况。但是也不能完全避免,需要STW中进行。

        

     

发布了365 篇原创文章 · 获赞 2 · 访问量 7440

猜你喜欢

转载自blog.csdn.net/kuaipao19950507/article/details/104102871
ZGC