JDK11中zgc垃圾回收器的探索

背景

垃圾回收器主要做的事情

  1. 自动跟踪和管理程序中创建的对象,确定哪些对象仍在使用,哪些对象已经不再使用。
  2. 回收那些不再使用的对象所占用的内存空间,使得这部分内存可以被重新使用。

1.1 传统垃圾回收器

垃圾回收器简述优缺点应用场景备注
Serial GC这是最基本、最古老的GC,它使用单线程进行垃圾回收,不能进行并行处理。因此,当它在进行垃圾回收时,用户线程必须暂停,等待垃圾回收完成。这种方式称为"Stop-The-World"。优点:简单高效,对于限制了CPU资源的环境,能提供很高的单线程垃圾回收效率。适用于单核处理器环境,或者小型应用。

Parallel GC

也称为吞吐量收集器,它是Serial GC的多线程版本。它在垃圾回收时也会暂停用户线程,但由于使用了多线程,所以垃圾回收的速度更快。

优点:多线程并行垃圾回收,提高了垃圾回收的效率。

缺点:在垃圾回收过程中,所有CPU资源都会被用于垃圾回收,可能导致应用程序的性能下降

适用于多核处理器环境,以及对吞吐量要求较高的大型应用。
CMS(Concurrent Mark Sweep) GC

这是一种以获取最短回收停顿时间为目标的收集器。它大部分工作都可以和用户线程并发执行,只有在初始标记和重新标记阶段需要"Stop-The-World"。

  • 优点:大部分工作都可以和用户线程并发执行,减少了"Stop-The-World"的时间,提高了应用的响应性。

  • 缺点:由于需要和用户线程并发执行,所以对CPU资源的消耗较大。另外,CMS GC无法处理浮动垃圾,可能会导致内存碎片

适用于对系统响应时间要求较高的应用。

G1 GC:

全称"Garbage-First",是一种面向服务器的垃圾收集器,主要用于多核处理器和大内存环境。G1 GC通过划分多个小块区域的方式,尽可能地减少"Stop-The-World"的时间。

  • 优点:可以预测停顿时间,实现了可控制的垃圾回收。通过划分多个小块区域的方式,尽可能地减少"Stop-The-World"的时间。

  • 缺点:在某些情况下,G1 GC的垃圾回收效率可能不如CMS GC(堆内存较小、对象存活率较高)。另外,G1 GC需要更多的CPU资源来保证高吞吐量。

使用场景:适用于多核处理器和大内存环境,以及对系统停顿时间有严格要求的大型应用。

1.2 传统垃圾回收器痛点

GC停顿

停顿指垃圾回收期间STW(Stop The World),当STW时,所有应用线程停止活动,等待GC停顿结束。

CMS以及G1的GC停顿

CMS新生代的Young GC、G1Young GC以及混合回收的和ZGC都基于标记-复制算法

Yong GC和G1标记复制算法的实现

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

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

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

停顿分析:

CMS以及G1的的yonug GC回收:

  1. 初始标记(Initial Mark):这是一个"Stop-The-World"(STW)阶段,即在这个阶段,所有的应用线程都会被暂停。垃圾回收器会标记出Eden区和一个Survivor区(假设是From)中所有存活的对象。
  2. 对存活对象进行复制:将所有存活的对象复制到另一个Survivor区(To)或者老年代(Old Generation)。同时,这些对象的年龄会加1。如果某个对象的年龄达到了一定的阈值(默认15),就会被晋升到老年代。
  3. 清空Eden区和From区:完成复制后,垃圾回收器会清空Eden区和From区。
  4. Survivor区交换:交换两个Survivor区的角色,即原来的To区变为From区,原来的From区变为To区。上述过程是STW的,触发频繁,耗时短;

标记阶段停顿分析

  • 初始标记阶段:初始标记阶段是指从根节点(GC Roots)出发标记全部直接子节点的过程,该阶段是STW的。由于GC Roots数量不多,通常该阶段耗时非常短。

  • 并发标记阶段:并发标记阶段是指从GC Roots开始对堆中对象进行可达性分析,找出存活对象。该阶段是并发的,即应用线程和GC线程可以同时活动。并发标记耗时相对长很多,但因为不是STW,所以我们不太关心该阶段耗时的长短。

  • 再标记阶段:重新标记那些在并发标记阶段发生变化的对象。该阶段是STW的。

清理阶段停顿分析

  • 清理阶段清点出有存活对象的分区和没有存活对象的分区,该阶段不会清理垃圾对象,也不会执行存活对象的复制。该阶段是STW的。

复制阶段停顿分析

  • 复制算法中的转移阶段需要分配新内存和复制对象的成员变量。转移阶段是STW的,其中内存分配通常耗时非常短,但对象成员变量的复制耗时有可能较长,这是因为复制耗时与存活对象数量与对象复杂度成正比。对象越复杂,复制耗时越长。

暂停时间过长

2.ZGC原理

优点总结:

支持TB级别的堆;

不会随着堆的大小增加而增加

2.1 ZGC为什么这么快

ZGC采用标记-复制算法(也有说法说是根据页面的大小选择采用标记复制以及标记整理算法),不过ZGC对该算法做了重大改进:ZGC在标记、复制和重定位阶段几乎都是并发的,这是ZGC实现停顿时间小于10ms目标的最关键原因。

回收流程:

  1. 标记阶段:ZGC首先会标记出所有的存活对象。

  2. 复制阶段:ZGC会将所有存活的对象复制到新的内存区域。

  3. 重映射阶段:ZGC会更新所有指向被复制对象的引用。

  4. 清理阶段:ZGC会回收被复制对象所在的内存区域。

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

在标记阶段用于处理并发标记中的漏标记

初始标记和初始转移分别都只需要扫描所有GC Roots,其处理时间和GC Roots的数量成正比,一般情况耗时非常短;再标记阶段STW时间很短,最多1ms,超过1ms则再次进入并发标记阶段。(G1的转移阶段是完全STW的)

2.2 堆空间的分页模型

ZGC 的 Region可以具有如下图所示的大中下三类容量:

【1】小型 Region(Small Region):容量固定为2MB,用于放置小于 256KB的小对象。

【2】中型 Region(Medium Region):容量固定为 32MB,用于放置大于 256KB但是小于 4MB的对象。

【3】大型 Region(Large Region):容量不固定,可以动态变化,但必须为 2MB的整数倍,用于放置 4MB或以上的大对象。每个大型 Region中会存放一个大对象,这也预示着虽然名字叫“大型 Region”,但它的实际容量完全有可能小于中型Region,最小容量可低至4MB.大型 Region在ZGC的实现中是不会被重分配的。

2.3 转移过程中访问对象问题的解决

2.3.1 色指针和读屏障技术;

大致工作原理:应用线程访问到对象时会触发读屏障,如果发现对象被移动了,读屏障”会把读出来的指针更新到对象的新地址上;而判断对象是否被移动过:利用对象引用的地址,即着色指针。

  1. 着色指针:在ZGC中,每个对象指针都被“着色”。每个指针都包含了关于该对象的一些信息(并发标记,转移,重定位等)。这样,ZGC就可以在并发的过程中,通过检查指针的颜色来知道该对象的状态,从而决定是否需要进行某些操作,如标记或复制该对象。

    ZGC中低42位(第0 ~ 41位)用于描述真正的虚拟地址(这就是上面提到的应用程序可以使用的堆空间),接着的4位(第42 ~ 45位)用于描述元数据,其实就是大家所说的Color Pointers,还有1位(第46位)目前暂时没有使用,最高17位(第47~63位)固定为0(限于java11中 4TB的内存空间)

  2. 读屏障:读屏障是一种在读取对象指针时插入的检查操作。当应用线程试图读取一个对象指针时,读屏障会先检查该指针的颜色。如果指针指示该对象已经被移动,那么读屏障就会先将应用线程重定向到对象的新地址,然后再返回该指针。这样,就可以确保应用线程总是能看到正确的对象状态。

2.3.2 转移过程详解

  1. 准备阶段,所有对象都在小页面A中,指针为蓝色

  2. 初始标记,所有对象都在A中,GCroots可达的对象指针变为绿色

  3. 并发标记,业务线程和回收现场并发,标记其他对象,将其他使用对象的指针变为绿色

  4. 再标记;

    标记在并发标记中漏标的对象

  5. 并发转移准备

    选择清理哪些页面

  6. 初始转移,转移A对象(GC可达的),修改地址,改变指针颜色

  7. 并发转移

    转移BC,并在转发表中记录转移地址,

    此时业务指针从堆中取对象时,读屏障发现指针颜色为绿色,读屏障则会去读取转发表,返回新地址;

    并在业务线程中更新指针颜色以及实际地址;

  8. 此时第一次垃圾回收结束,这个时候GC root直达对象指针颜色为蓝色,其余所有存活对象指针颜色为绿色;

  9. 此时开始第二次垃圾回收

  10. 初始标记阶段

    将GCroots 可达对象标记为红色

  11. 并发标记阶段

    如果扫描到指针颜色为绿色的对象,则根据转发表找到新地址,将该对象变为红色,并在业务线程中的对象实地址,清除转发表数据,蓝色的对象同第一次回收阶段

总结:利用空间(虚拟空间)去换取时间

3. JDK 11中的ZGC的坑

Loading...

https://segmentfault.com/a/1190000023192220

简单梳理下就是:每次调用StackWalker遍历栈帧的时候,每个栈帧都会生成一个ResolvedMethodName对象放到jvm中的ResolvedMethodTable中,但jdk11的zgc不能有效清理其中不用的对象。因为ResolvedMethodTable是个定容的hashtable,随着其中的数据越来越多,每个bucket的单链表越来越长,查询效率会越来越慢。 所以最终导致CPU的使用率越来越高。

总结:由于没有讲logger写为static,所以logger频繁初始化调用StackWalker方法

jdk11+zgc+log4j+编码不规范

注:在jdk13中已修复

  1. 在zgc增加调用SymbolTable::unlink()方法

  2. ResolvedMethodTable的实现,支持了动态扩缩容,可以避免单链表过长的问题

    1. do_concurrent_work(JavaThread* jt)函数:这是处理并发工作的主要函数。首先,计算负载因子(load factor),这是一个衡量表的填充程度的指标。然后,根据负载因子的大小,决定是扩展方法表还是清理无用的条目。如果负载因子大于预设值(这里是2),并且方法表还没有达到最大大小,那么就调用grow(jt)函数来扩展方法表。否则,就调用clean_dead_entries(jt)函数来清理无用的条目。

    2. grow(JavaThread* jt)函数:这是用于扩展方法表的函数。首先,创建了一个GrowTask对象,这是一个用于扩展表的任务。然后,在一个循环中执行这个任务,直到任务完成。在执行任务的过程中,如果需要,它会暂停任务并将线程切换到VM状态,然后再继续执行任务。任务完成后,它会更新当前的表大小,并打印出新的大小。

void ResolvedMethodTable::do_concurrent_work(JavaThread* jt) {
  _has_work = false;
  double load_factor = get_load_factor();
  log_debug(membername, table)("Concurrent work, live factor: %g", load_factor);
  // 人工load_factor大于2,并且没有达到最大限制,就执行bucket扩容,并且移除无用的entry
  if (load_factor > PREF_AVG_LIST_LEN && !_local_table->is_max_size_reached()) {
    grow(jt);
  } else {
    clean_dead_entries(jt);
  }
}

void ResolvedMethodTable::grow(JavaThread* jt) {
  ResolvedMethodTableHash::GrowTask gt(_local_table);
  if (!gt.prepare(jt)) {
    return;
  }
  log_trace(membername, table)("Started to grow");
  {
    TraceTime timer("Grow", TRACETIME_LOG(Debug, membername, table, perf));
    while (gt.do_task(jt)) {
      gt.pause(jt);
      {
        ThreadBlockInVM tbivm(jt);
      }
      gt.cont(jt);
    }
  }
  gt.done(jt);
  _current_size = table_size();
  log_info(membername, table)("Grown to size:" SIZE_FORMAT, _current_size);
}

4 ZGC在之后JDK版本迭代中的改进

4.0 ZGC在JDK11下的缺陷

  1. ZGC时Java进程占用三倍内存问题:由于ZGC着色指针把内存空间映射了3个虚拟地址,使得TOP/PS等命令查看占用内存时看到Java进程占用内存过大。此问题不影响操作系统,但是会影响到监控运维工具。

    Tencent Kona JDK11无暂停内存管理ZGC生产实践

  2. 吞吐量低于G1 GC。一般来说,可能会下降5%-15%。对于堆越小,这个效应越明显,堆非常大的时候,比如100G,其他GC可能一次Major或Full GC要几十秒以上,但是对于ZGC不需要那么大暂停。这种细粒度的优化带来的副作用就是,把很多环节其他GC里的STW整体处理,拆碎了,放到了更大时间范围内里去跟业务线程并发执行,甚至会直接让业务线程帮忙做一些GC的操作,从而降低了业务线程的处理能力。

  3. 对象分配卡顿,除了ZGC的暂停阶段之外,还受到下面的一些因素的影响:Page Cache Flush问题影响分配速度:ZGC把堆分为不同大小的page(对应G1的Region)——small/medium/large page(不同大小的object分配到不同类型的page中),如果各种大小对象分配速度不稳定(比如medium大小的object突然变多,那么就需要把large/small page转换成medium page,比较耗时),JDK15 production-ready之后有所缓解;

  4. 由于ZGC采用colored pointer技术,因此不支持压缩指针,一定程度上影响小堆(32GB以下)的性能(JDK15后可以支持UseCompressedOops关闭时依然开启UseCompressedClassPointers)

4.1 JDK17中LTS版本的改进

  1. 并发类卸载:在JDK 15中,ZGC增加了并发类卸载的功能,这使得ZGC可以在垃圾收集过程中并发地卸载不再使用的类。

  2. 并发预处理:在JDK 14中,ZGC增加了并发预处理的功能,这可以减少垃圾收集的暂停时间。

  3. JFR事件:在JDK 14中,ZGC增加了对Java Flight Recorder (JFR)事件的支持,这使得开发者可以更好地监控和诊断ZGC的行为。

  4. 精简的锁:在JDK 15中,ZGC的内部锁机制被精简,这可以提高ZGC的性能。

  5. 支持更多的平台:在JDK 11中,ZGC只支持Linux/x64平台。在后续的版本中,ZGC增加了对更多平台的支持,包括macOS、Windows和Linux/Aarch64。

4.2 JDK21分代ZGC的不同

它在ZGC的基础上引入了分代概念。在分代ZGC中,堆被划分为多个代,通常包括新生代和老年代。新创建的对象首先放在新生代,当它们存活足够长的时间后,会被移动到老年代。

分代ZGC的主要优点是可以更有效地处理短生命周期的对象。由于大多数对象的生命周期都很短,所以通过在新生代中更频繁地进行垃圾收集,可以更快地回收这些对象,从而提高垃圾收集的效率。同时,由于老年代中的对象相对稳定,所以可以减少在老年代中的垃圾收集频率,从而减少了垃圾收集对应用的影响。

总的来说,分代ZGC和ZGC的主要区别在于是否使用了分代概念,以及如何处理不同生命周期的对象。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/790422.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

typora 两边太宽,设置宽度

步骤: 查看目前使用主题类型 文件 —> 偏好设置 —> 外观 —> 打开主题文件夹 修改对应的主题:max-width

在Linux下使用Docker部署chirpstack

目录 一、前言 二、chirpstack 1、chirpstack是什么 2、chirpstack组件 3、为什么选择Docker部署 三、Linux下部署过程 四、web界面部署过程 一、前言 本篇文章我是在Linux下使用 Docker 进行部署chirpstack,chirpstack采用的是v4 版本,v4 版本 与…

实时数仓搭建

项目概述 本项目针对实时数仓中的dim层,使用flik获取维度数据以及维度表结构把处理过的数据和维度表同步到habse中,同步采用的是雪花模型,遵循三范式,对维度数据进行实时的增删改查。 对维度表进行动态拆分功能。 动态拆分功能…

centos安装数据库同步工具sqoop并导入数据,导出数据,添加定时任务

目录 1.安装jdk 1.1上传jdk安装包到/opt目录下并解压 1.2解压 1.3配置环境变量 2.安装hadoop 2.1.下载hadoop 2.2.解压hadoop 2.3配置环境变量 3.安装sqoop 3.1下载 3.2解压 3.3下载依赖包并复制到指定位置 3.3.1下载commons-lang-2.6-bin.tar.gz 3.3.2将mysql-c…

【postgresql初级使用】用户与角色的关系,搭建数据库安全体系中的分权管理

用户角色管理 ​专栏内容: postgresql使用入门基础手写数据库toadb并发编程 个人主页:我的主页 管理社区:开源数据库 座右铭:天行健,君子以自强不息;地势坤,君子以厚德载物. 文章目录 用户角色管…

Nature Renderer 2022(植被渲染工具插件)

渲染大量详细的植被。 自然渲染器通过替换Unity的默认地形细节和树系统来提高植被渲染的质量。一切都适用于现有数据:使用相同的草地、植被和树木,并保留现有地形。我们只是升级您的渲染器。 Unity验证的解决方案 Nature Renderer受到25000多名开发人员的信任,是Unity验证的…

基于Make的c工程No compilation commands found报错

由于安装gcc时只安装了build-essential,没有将其添加到环境变量中,因此打开Make工程时,CLion会产生如下错误: 要解决这个问题,一个方法是将GCC添加到环境变量中,但是这个方法需要修改至少两个配置文件&…

请编写函数,删除字符串中指定位置下的字符,删除成功函数返回被删字符,否则返回空值

char arr_del(char* p, int pos) {if (pos> strlen(p) || pos<0){printf("这是一个无效下标\n");exit(1);}//到这里就是有效下标char ch p[pos];//把要删除的下标存储for (int i pos; p[i] ! \0; i){p[i] p[i 1];}return ch; } int main() {char arr[100];…

PFC电路中MOS管的选取3

MOS管的驱动波形 一个 MOS管在开通或者关断的时候&#xff0c;必定会经历一个线性区。这个线性区域在 Vgs波形上表现出一个平台&#xff0c;在这个平台的时候电流和电压的变化率是很大的&#xff0c;有很大的 dv/dt&#xff0c;di/dt &#xff0c;由于 di/dt变化非常大&#xf…

Transformer模型解析:走进自然语言处理的新时代

UPDATED&#xff1a;2023 年 1 月 27 日&#xff0c;本文登上 ATA 头条。&#xff08;注&#xff1a;ATA 全称 Alibaba Technology Associate&#xff0c;是阿里集团最大的技术社区&#xff09;UPDATED&#xff1a;2023 年 2 月 2 日&#xff0c;本文在 ATA 获得鲁肃点赞。&…

使用lv虚拟卷扩展磁盘

使用centos演示。 首先创建centos虚拟机。链接&#xff1a;VMWARE安装Centos8,并且使用ssh连接虚拟机-CSDN博客 1. 增加磁盘。 选中要扩容的虚拟机&#xff0c;右键选择设置&#xff0c;然后点击磁盘&#xff0c;选择添加。 这里选择NVM的磁盘。选择这种磁盘是为了保持与之前…

【Java】零散知识--感觉每条都有知识在进入脑子唤起回忆

1&#xff0c;什么是双亲委派 AppClassLoader在加载类时&#xff0c;会向上委派&#xff0c;取查找缓存。 AppClassLoader >>ExtClassLoader >>BootStrapClassLoader 情况一 向上委派时查找到了&#xff0c;直接返回。 情况二 当委派到顶层之后&#xff0c;缓…

python网络爬虫之Urllib

概述 urllib的request模块提供了最基本的构造HTTP请求的方法&#xff0c;使用它可以方便地实现请求的发送并得到响应&#xff0c;同时它还带有处理授权验证&#xff08;authentication&#xff09;、重定向&#xff08;redirection&#xff09;、浏览器Cookies以及其他内容。 …

java算法day11

二叉树的递归遍历二叉树的非递归遍历写法层序遍历 递归怎么写&#xff1f; 按照三要素可以保证写出正确的递归算法&#xff1a; 1.确定递归函数的参数和返回值&#xff1a; 确定哪些参数是递归的过程中需要处理的&#xff0c;那么就在递归函数里加上这个参数&#xff0c; 并且…

LabVIEW机器视觉技术在产品质量检测中有哪些应用实例

LabVIEW的机器视觉技术在产品质量检测中有广泛的应用&#xff0c;通过图像采集、处理和分析&#xff0c;实现对产品缺陷的自动检测、尺寸测量和定位校准&#xff0c;提高生产效率和产品质量。 1. 电子元器件质量检测 在电子制造业中&#xff0c;电子元器件的质量检测是确保产品…

AI绘画杀死了设计师!?恰恰相反……

与大多数人想象的不同&#xff0c;ChatGPT等各种AI工具爆火之后&#xff0c;受到冲击最大的居然是设计师、作家、翻译等具有创造性的工作&#xff0c;以体力劳动为主的蓝领反而最不易被替代。 以城市数据团做过的一项研究为例&#xff0c;他们对中国1639种职业进行了GPT替代风险…

蚁剑编码器编写——php木马免杀

蚁剑编码器编写——php木马免杀 我的想法是 木马要先免杀&#xff0c;能够落地&#xff0c;再去考虑流量层面的问题 举几个例子演示一下 命令执行与代码执行是有比较大的区别&#xff0c;蚁剑执行的是php代码&#xff0c;而system&#xff0c;proc_open,passthru,exec,shell_…

【C++深度学习】多态(概念虚函数抽象类)

✨ 疏影横斜水清浅&#xff0c;暗香浮动月黄昏 &#x1f30f; &#x1f4c3;个人主页&#xff1a;island1314 &#x1f525;个人专栏&#xff1a;C学习 &#x1f680; 欢迎关注&#xff1a;&#x1f44d;点赞 &…

比curl更直观的网站性能测试工具httpstat——筑梦之路

GitHub - davecheney/httpstat: Its like curl -v, with colours. wget https://raw.githubusercontent.com/reorx/httpstat/master/httpstat.pymv httpstat.py /usr/bin/httpstat #移动到环境变量路径chmod x /usr/bin/httpstat #添加可执行权限 exec bash #重置当前bash进…

Python爬虫教程第3篇-解决使用reqeusts遇到的ProxyError异常

起因 问题出现在windows电脑上&#xff0c;我用mac执行程序的时候并不会报错&#xff0c;但是如果在windows上的时候&#xff0c;大部分windows电脑会报错&#xff0c;而有些版本低的windows电脑又不会报错。 异常栈信息 HTTPSConnectionPool, Cannot connect to proxy, no …