如何理解内存布局及GC原理

发布时间:2021-10-14 15:27:33 作者:iii
来源:亿速云 阅读:115

这篇文章主要介绍“如何理解内存布局及GC原理”,在日常操作中,相信很多人在如何理解内存布局及GC原理问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”如何理解内存布局及GC原理”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

ZGC (截止目前史上最好的 GC 收集器)

ZGC(The Z Garbage Collector) 是 JDK 11 中推出的一款低延迟垃圾回收器,它的设计目标包括: 在G1的基础上,做了很多改进(JDK 11 开始引入)


从设计目标来看,我们知道 ZGC 适用于大内存低延迟服务的内存管理和回收。本文主要介绍 ZGC 在低延时场景中的应用和卓越表现,文章内容主要分为四部分:



GC 之痛

很多低延迟高可用Java服务的系统可用性经常受 GC 停顿的困扰。GC 停顿指垃圾回收期间 STW(Stop The World),当 STW 时,所有应用线程停止活动,等待GC停顿结束。


以美团风控服务为例,部分上游业务要求风控服务 65ms 内返回结果,并且可用性要达到 99.99%。但因为 GC 停顿,我们未能达到上述可用性目标。当时使用的是 CMS 垃圾回收器,单次 Young GC 40ms,一分钟 10 次,接口平均响应时间 30ms。通过计算可知,有( 40ms + 30ms ) * 10 次 / 60000ms = 1.12%的请求的响应时间会增加 0 ~ 40ms 不等,其中 30ms * 10 次 / 60000ms = 0.5%的请求响应时间会增加 40ms。

可见,GC 停顿对响应时间的影响较大。为了降低 GC 停顿对系统可用性的影响,我们从降低单次 GC 时间和降低 GC 频率两个角度出发进行了调优,还测试过G1垃圾回收器,但这三项措施均未能降低 GC 对服务可用性的影响。

CMS 与 G1 停顿时间瓶颈


介绍ZGC之前,首先回顾一下 CMS 和 G1 的 GC 过程以及停顿时间的瓶颈。CMS 新生代的 Young GC、G1 和 ZGC 都基于标记-复制算法,但算法具体实现的不同就导致了巨大的性能差异。

标记-复制算法应用在 CMS 新生代(ParNew 是 CMS 默认的新生代垃圾回收器)和 G1 垃圾回收器中

标记-复制算法可以分为四个阶段:


  1. 标记阶段,即从 GC Roots 集合开始,标记活跃对象

  2. 清理阶段,即清理所有不活跃的对象

  3. 转移阶段,即把活跃对象复制到新的内存地址上

  4. 重定位阶段,因为转移导致对象的地址发生了变化,在重定位阶段,所有指向对象旧地址的指针都要调整到对象新的地址上


下面以 G1 为例,通过 G1 中标记-复制算法过程(G1 的 Young GC 和 Mixed GC 均采用该算法),分析 G1 停顿耗时的主要瓶颈。G1 垃圾回收周期如下图所示:

如何理解内存布局及GC原理

G1 的混合回收过程可以分为标记阶段、清理阶段和复制阶段。
标记阶段停顿分析
初始标记阶段 :
初始标记阶段是指从 GC Roots 出发标记全部直接子节点的过程,该阶段是 STW 的。由于 GC Roots 数量不多,通常该阶段耗时非常短。
并发标记阶段 :
并发标记阶段是指从 GC Roots 开始对堆中对象进行可达性分析,找出存活对象。该阶段是并发的,即应用线程和 GC 线程可以同时活动。
并发标记耗时相对长很多,但因为不是 STW,所以我们不太关心该阶段耗时的长短。
再标记阶段 :
重新标记那些在并发标记阶段发生变化的对象。该阶段是 STW 的。
清理阶段停顿分析
清理阶段清点出有存活对象的分区和没有存活对象的分区,该阶段不会清理垃圾对象,也不会执行存活对象的复制。该阶段是 STW 的,因此此阶段不会出现浮动对象。
复制阶段停顿分析
复制算法中的转移阶段需要分配新内存和复制对象的成员变量。转移阶段是 STW 的,其中内存分配通常耗时非常短,但对象成员变量的复制耗时有可能较长,这是因为复制耗时与存活对象数量与对象复杂度成正比。对象越复杂,复制耗时越长。(不会存在内存碎片且不适合大对象的赋值迁移)

四个 STW 过程中:

因此,G1 停顿时间的瓶颈主要是标记-复制中的转移阶段 STW。为什么转移阶段不能和标记阶段一样并发执行呢?主要是 G1 未能解决转移过程中准确定位对象地址的问题。

G1 的 Young GC 和 CMS 的 Young GC,其标记-复制全过程 STW,这里不再详细阐述

ZGC 原理

全并发的ZGC

与CMS中的ParNew和G1类似,ZGC 也采用标记-复制算法,不过 ZGC对该算法做了重大改进:ZGC 在标记、转移和重定位阶段几乎都是并发的,这是 ZGC 实现停顿时间小于 10ms 目标的最关键原因

ZGC 垃圾回收周期如下图所示: 如何理解内存布局及GC原理

  ZGC只有三个 STW 阶段: 初始标记 , 再标记 , 初始转移 。

其中,初始标记和初始转移分别都只需要扫描所有 GC Roots,其处理时间和 GC Roots 的数量成正比,一般情况耗时非常短;再标记阶段 STW 时间很短,最多 1ms,超过 1ms 则再次进入并发标记阶段。即,ZGC几乎所有暂停都只依赖于 GC Roots 集合大小,停顿时间不会随着堆的大小或者活跃对象的大小而增加。与 ZGC 对比,G1的转移阶段完全STW的,且停顿时间随存活对象的大小增加而增加。


ZGC只有三个 STW 阶段:初始标记 , 再标记 , 初始转移 。其中,初始标记和初始转移分别都只需要扫描所有 GC Roots,其处理时间和 GC Roots 的数量成正比,一般情况耗时非常短;再标记阶段 STW 时间很短,最多1ms,超过1ms则再次进入并发标记阶段。即,ZGC 几乎所有暂停都只依赖于GC Roots集合大小,停顿时间不会随着堆的大小或者活跃对象的大小而增加。与 ZGC 对比,G1 的转移阶段完全 STW 的,且停顿时间随存活对象的大小增加而增加。

ZGC 关键技术

   ZGC 通过着色指针和读屏障技术,解决了转移过程中准确访问对象的问题,实现了并发转移。

大致原理描述如下:并发转移中“并发”意味着 GC 线程在转移对象的过程中,应用线程也在不停地访问对象。假设对象发生转移,但对象地址未及时更新,那么应用线程可能访问到旧地址,从而造成错误。ZGC中,应用线程访问对象将触发“读屏障”,如果发现对象被移动了,那么“读屏障”会把读出来的指针更新到对象的新地址上,这样应用线程始终访问的都是对象的新地址。那么,JVM 是如何判断对象被移动过呢?就是利用对象引用的地址,即着色指针。下面介绍着色指针和读屏障技术细节。

着色指针

 着色指针是一种将信息存储在指针中的技术。

ZGC 仅支持 64 位系统,它把 64 位虚拟地址空间划分为多个子空间,如下图所示: 如何理解内存布局及GC原理

其中,[0~4TB) 对应 Java 堆,[4TB ~ 8TB) 称为 M0 地址空间,[8TB ~ 12TB) 称为 M1 地址空间,[12TB ~ 16TB) 预留未使用,[16TB ~ 20TB) 称为 Remapped 空间。


当应用程序创建对象时,首先在堆空间申请一个虚拟地址,但该虚拟地址并不会映射到真正的物理地址。ZGC 同时会为该对象在 M0、M1 和 Remapped 地址空间分别申请一个虚拟地址,且这三个虚拟地址对应同一个物理地址,但这三个空间在同一时间有且只有一个空间有效。ZGC 之所以设置三个虚拟地址空间,是因为它使用“空间换时间”思想,去降低 GC 停顿时间。“空间换时间”中的空间是虚拟空间,而不是真正的物理空间。后续章节将详细介绍这三个空间的切换过程。


如何理解内存布局及GC原理

与上述地址空间划分相对应,ZGC 实际仅使用 64 位地址空间的第 0~41 位,而第 42~45 位存储元数据,第 47~63 位固定为 0。ZGC 将对象存活信息存储在 42~45 位中,这与传统的垃圾回收并将对象存活信息放在对象头中完全不同。

读屏障

读屏障是 JVM 向应用代码插入一小段代码的技术。当应用线程从堆中读取对象引用时,就会执行这段代码。需要注意的是,仅“从堆中读取对象引用”才会触发这段代码。

读屏障示例:
Object o = obj.FieldA   // 从堆中读取引用,需要加入屏障
<Load barrier>
Object p = o  // 无需加入屏障,因为不是从堆中读取引用
o.dosomething() // 无需加入屏障,因为不是从堆中读取引用
int i =  obj.FieldB  //无需加入屏障,因为不是对象引用

ZGC 中读屏障的代码作用 :在对象标记和转移过程中,用于确定对象的引用地址是否满足条件,并作出相应动作。

ZGC 并发处理演示

接下来详细介绍 ZGC 一次垃圾回收周期中地址视图的切换过程:

到此,关于“如何理解内存布局及GC原理”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注亿速云网站,小编会继续努力为大家带来更多实用的文章!

推荐阅读:
  1. 如何理解JAVA中的GC
  2. golang中的gc原理是什么

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

gc

上一篇:SCN、ORA-19706错误和_external_scn_rejection_threshold_hours参数是什么

下一篇:PHP如何实现自动转换字符集并支持数组转换

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》