【JVM】聊聊垃圾回收之三色标记算法
在垃圾收集器 CMS中存在四个阶段,初始标记、并发标记、重新标记、并发清理。

那么在并发标记中由于没有STW,业务程序和GC程序是并发执行的,那么是如何实现对象的并发标记的。
并发垃圾回收
并发标记其实是一个宏观的过程,仍然需要STW,只不过减少STW的时间。
初始标记:标记GC Roots,并发标记是在应用程序不暂停的情况下,以GC Roots为起点,广度或者深度遍历所有可达对象。因为在这个过程中,应用程序和GC程序是并发执行的,所有很有可能对象的引用关系随时变化。导致并发标记过程出现标记错误。而重新标记的就是为了调整对标记错误的进行修正。并发清理是在应用程序不暂停的情况下,对垃圾对象进行清理
初始、并发、重新标记 三个阶段属于可达性分析,标记环节。
GC垃圾收集器的其实就是在吞吐量和STW之间权衡,为了减少STW对业务的影响,只能利用并发执行。
三色标记算法
三色标记是应用程序还在执行,对象引用关系随时会发生变化,所以引入三色标记算法。
白色对象: 没有被遍历过的对象,在开始遍历时都是白色对象,在经过可达性分析之后,还是白色,那么就是可被回收的对象。
灰色对象: 该对象已经被遍历,但是所引用的对象没有被遍历完。
黑色对象: 对象已经被遍历,所引用的对象已全部被遍历。
核心流程:
1.初始化将GC roots对象设置为灰色,其他对象为白色
2.以GC roots 开始遍历,将可达对象设置为灰色,自己设置为黑色
3.重复执行上述,最后是白色的对象就是不可达对象。
而1就是初始标记阶段,2、3是并发标记阶段。
说白了就是一个游戏,大家成群结对,找到不在队伍中的人员,开始都是一样的颜色,经过标记后,脱离队伍的一定是初始颜色的同学。

漏标和误标
在并发标记的过程,因为应用程序在同步执行,所以可能将存活对象标记为死亡对象称为漏标,将死亡对象标记为存活对象称为误标,后者是不可接受的,前者在下一次GC时会被清理。
误标
如果目前对象标记状态时如下图,那么当objA.fieldB = null ,因为本身A对象已经被标记为黑色,那么B以及B引用的对象都将标记为存活对象。

漏标
如果目前对象标记时上图所以,执行
objA.filedc = objA.fileB.filedC;
objA.fileB.fileC = null;
那么B到C之间的引用会断开,A和C之间建立连接。因为A是黑色对象,不会在遍历A的引用对象,从而出现漏标,将存活对象标记为死亡对象。

增量更新和原始快照
对于误标其实问题不大,大不了下一次在执行GC的时候处理,但是对于漏标就问题很大,会将存活的对象标记为死亡对象。
产生漏标的主要是两点
1.产生了一个新引用,黑色对象到白色对象的引用
2.删除引用:删除灰色对象到白色对象的所有引用
两种解决方案:增量更新和原始快照,增量更新记录新增的引用关系,原始快照记录的是删除的引用关系。CMS使用增量更新,G1使用原始快照。
增量更新
在并发标记中,如果应用程序新增了一个黑色对象对一个白色对象的引用,那么就将这个白色对象记录下来,在并发标记完成之后,重新标记以白色标记的对象,进行可达性分析。这样漏标的对象就会重新标记为黑色对象。
原始快照
增量更新是对新增的,而原始快照针对的是当删除一个白色对象对于灰色对象的引用时,虚拟机会记录这个白色对象,在完成并发标记阶段后,在可达性分析这些白色对象的引用关系。
对于上述不管时什么其实都需要在代码的新增和删除引用的过程进行拦截处理,也叫读写屏障。
虚拟机的记录操作都是通过写屏障实现的。
写屏障
给某个对象的成员变量赋值时,其底层代码大概长这样:
/*** @param field 某对象的成员变量,如 a.b.d* @param new_value 新值,如 null*/void oop_field_store(oop* field, oop new_value) {*field = new_value; // 赋值操作
}
所谓的写屏障,其实就是指在赋值操作前后,加入一些处理(可以参考AOP的概念)
void oop_field_store(oop* field, oop new_value) {pre_write_barrier(field); // 写屏障‐写前操作*field = new_value;post_write_barrier(field, value); // 写屏障‐写后操作
}
写屏障实现SATB
当对象B的成员变量的引用发生变化时,比如引用消失(a.b.d = null),我们可以利用写屏障,将B原来成员变量的引用对象D记录下来:
void pre_write_barrier(oop* field) {oop old_value = *field; // 获取旧值remark_set.add(old_value); // 记录原来的引用对象
}
写屏障实现增量更新
当对象A的成员变量的引用发生变化时,比如新增引用(a.d = d),我们可以利用写屏障,将A新的成员变量引用对象D 记录下来:
void post_write_barrier(oop* field, oop new_value) { remark_set.add(new_value); // 记录新引用的对象
}
读屏障
读屏障是直接针对第一步:D d = a.b.d,当读取成员变量时,一律记录下来:
void pre_load_barrier(oop* field) {oop old_value = *field;remark_set.add(old_value); // 记录读取到的对象
}
CMS:写屏障 + 增量更新
G1,Shenandoah:写屏障 + SATB
ZGC:读屏障
并发清理和新建对象
在最后的阶段,并发清理阶段,其实存活对象可能变成死亡对象,那么只需要在下次GC时进行回收就可以了,那么死亡对象可能变成存活对象嘛,从而导致被清理掉嘛?
因为死亡对象不在有变量可以直接被引用。
** 为什么CMS和G1都使用标记-清除而非标记-整理算法来回收老年代的垃圾对象?**
垃圾回收时间快,减少STW时间。在多次垃圾回收之后进行一次碎片整理有效避免了分配效率问题和空间利用率问题。
小总
本篇主要介绍了垃圾回收算法中CMS中 并发标记阶段中的主要问题,从而引出漏标和误标,而解决漏标的问题主要增量更新和原始引用,而都是通过底层的读写屏障解决的。
相关文章:
【JVM】聊聊垃圾回收之三色标记算法
在垃圾收集器 CMS中存在四个阶段,初始标记、并发标记、重新标记、并发清理。 那么在并发标记中由于没有STW,业务程序和GC程序是并发执行的,那么是如何实现对象的并发标记的。 并发垃圾回收 并发标记其实是一个宏观的过程,仍然需…...
鸿蒙Harmony应用开发—ArkTS声明式开发(基础手势:Image)
Image为图片组件,常用于在应用中显示图片。Image支持加载PixelMap、ResourceStr和DrawableDescriptor类型的数据源,支持png、jpg、jpeg、bmp、svg、webp和gif类型的图片格式。 说明: 该组件从API Version 7开始支持。后续版本如有新增内容&am…...
华为OD面试分享8(2024年)
个人情况: 23毕业,24一战跨考ustc,觉G。 211本,目标院校。 知识储备: 仅限408 用leetcode刷过408排序算法 0项目经验 时间线: 23.12.27 投的简历。当时啥也不会啥也不知道,听入职的同学说…...
Java的堆如何分代的?
前言: java对象一般都是朝生夕死,也有部分对象是一直存在的。两种对象如何都放在一起,进行垃圾回收的时候效率较低。所以通过将不同时期的对象放在不同的内存池中,节省垃圾回收的时间,提高性能。 组成: 新生…...
C# CallerMemberName、CallerFilePath、CallerLineNumber
CallerMemberName:调用某个方法的主方法名称 CallerFilePath:调用某个方法的主方法所在的类文件地址 CallerLineNumber:调用这个方法所在的行号 用这三个附加属性,需要设置默认值。...
Claude3系统解读与使用测评
大家好,我是herosunly。985院校硕士毕业,现担任算法研究员一职,热衷于机器学习算法研究与应用。曾获得阿里云天池比赛第一名,CCF比赛第二名,科大讯飞比赛第三名。拥有多项发明专利。对机器学习和深度学习拥有自己独到的见解。曾经辅导过若干个非计算机专业的学生进入到算法…...
vue3注册全局组件
注册单个全局组件 一.在main.ts中引入注册为全局组件 在main.ts 引入我们的组件跟随在createApp(App) 后面 切记不能放到mount 后面这是一个链式调用用其次调用 component 第一个参数组件名称 第二个参数组件实例 import { createApp } from vue import App from ./App.vue …...
vue 在什么情况下在数据发生改变的时候不会触发视图更新
在 Vue 中,通常数据发生变化时,视图会自动更新。但是,有几种情况可能导致数据变化不会触发视图更新: 1.对象属性的添加或删除: Vue 无法检测到对象属性的添加或删除。因为 Vue 在初始化实例时对属性执行了 getter/se…...
HTML世界之标签Ⅲ
一、dfn 标签 <dfn> 标签是一个短语标签,用来定义一个定义项目。 写法: <dfn></dfn> 二、dialog 标签 <dialog> 标签定义一个对话框、确认框或窗口。 属性 值 描述 open open 规定 dialog 元素是有效的,用户…...
【UE5】游戏框架GamePlay
项目资源文末百度网盘自取 游戏框架 游戏 由 游戏模式(GameMode) 和 游戏状态(GameState) 所组成 加入游戏的 人类玩家 与 玩家控制器(PlayerController) 相关联 玩家控制器允许玩家在游戏中拥有 HUD,这样他们就能在关卡中拥有物理代表 玩家控制器还向玩家提供 …...
用户管理【MySQL】
文章目录 查看用户信息创建用户修改密码删除用户授予权限收回权限 查看用户信息 在名为mysql的数据库中有一个表user维护着 MySQL 的用户信息。 其中: user: 表示该用户的用户名。host: 表示该用户可以从哪个主机登录,localhost…...
Rust教程:How to Rust-变量
本文为第1篇 专栏简介 本专栏是优质Rust技术专栏,推荐精通一门技术栈的蟹友,不建议完全无计算机基础的同学 感谢Rust圣经开源社区的同学,为后来者提供了非常优秀的Rust学习资源 本文使用: 操作系统macOS Sonoma 14 / Apple M…...
TCP/IP超全笔记 - TCP篇
TCP/IP超全笔记 - TCP篇 什么是 TCP TCP(Transmission Control Protocol,传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议。 面向连接:一对一,先连接,再传输数据可靠交付:…...
MIT 6.858 计算机系统安全讲义 2014 秋季(一)
译者:飞龙 协议:CC BY-NC-SA 4.0 MIT 6.858 计算机系统安全笔记 2014 秋季 2014 年由Nickolai Zeldovich 教授和James Mickens 教授教授授课的 6.858 讲座笔记。这些讲座笔记略有修改,与 6.858 课程网站上发布的内容略有不同。 第1讲&#x…...
mybatis-plus整合spring boot极速入门
使用mybatis-plus整合spring boot,接下来我来操作一番。 一,创建spring boot工程 勾选下面的选项 紧接着,还有springboot和依赖我们需要选。 这样我们就创建好了我们的spring boot,项目。 简化目录结构: 我们发现&a…...
Kafka|处理 Kafka 消息重复的有效措施
文章目录 消息重复场景生产者端Kafka Broker消费者端 如何防止消息重复 消息重复是 Kafka 系统中另一个常见的问题,可能发生在生产者、Broker 或消费者三个方面。下面我们来讨论一些可能导致消息重复的场景以及如何处理。 消息重复场景 生产者端 重试机制导致消息…...
【C++】函数模板和类模板
目录 1.泛型编程 2.函数模板 2.1函数模板的定义格式 2.2函数模板的实例化 2.3函数模板参数的匹配原则 3.类模板 3.1类模板的定义格式 3.2类模板的实例化 3.3模板的分离编译 1.泛型编程 泛型编程:编写与类型无关的通用代码,是代码复用的一种手段…...
Echarts 配置项 series 中的 data 是多维度
文章目录 需求分析 需求 如下图数据格式所示,现要求按照该格式进行绘制折线图 分析 在绘制折线图时,通常我们的 series 中的 data 数据是这样的格式 option {title: {text: Stacked Area Chart},tooltip: {trigger: axis,axisPointer: {type: cross…...
快速了解Redis
Redis是什么? Redis是一个数据库,是一个跨平台的非关系型数据库,Redis完全开源,遵守BSD协议。它通过键值对(Key-Value)的形式存储数据。 与传统数据库不同的是 Redis 的数据是存在内存中的 ,也就是它是内存数据库&am…...
1.2_2 OSI参考模型
文章目录 1.2_2 OSI参考模型一、概述(一)ISO/OSI参考模型是怎么来的?(二)ISO/OSI参考模型(三)ISO/OSI参考模型解释通信过程 二、各层功能及协议(一)应用层(第…...
Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以?
Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以? 在 Golang 的面试中,map 类型的使用是一个常见的考点,其中对 key 类型的合法性 是一道常被提及的基础却很容易被忽视的问题。本文将带你深入理解 Golang 中…...
Debian系统简介
目录 Debian系统介绍 Debian版本介绍 Debian软件源介绍 软件包管理工具dpkg dpkg核心指令详解 安装软件包 卸载软件包 查询软件包状态 验证软件包完整性 手动处理依赖关系 dpkg vs apt Debian系统介绍 Debian 和 Ubuntu 都是基于 Debian内核 的 Linux 发行版ÿ…...
iPhone密码忘记了办?iPhoneUnlocker,iPhone解锁工具Aiseesoft iPhone Unlocker 高级注册版分享
平时用 iPhone 的时候,难免会碰到解锁的麻烦事。比如密码忘了、人脸识别 / 指纹识别突然不灵,或者买了二手 iPhone 却被原来的 iCloud 账号锁住,这时候就需要靠谱的解锁工具来帮忙了。Aiseesoft iPhone Unlocker 就是专门解决这些问题的软件&…...
Opencv中的addweighted函数
一.addweighted函数作用 addweighted()是OpenCV库中用于图像处理的函数,主要功能是将两个输入图像(尺寸和类型相同)按照指定的权重进行加权叠加(图像融合),并添加一个标量值&#x…...
在web-view 加载的本地及远程HTML中调用uniapp的API及网页和vue页面是如何通讯的?
uni-app 中 Web-view 与 Vue 页面的通讯机制详解 一、Web-view 简介 Web-view 是 uni-app 提供的一个重要组件,用于在原生应用中加载 HTML 页面: 支持加载本地 HTML 文件支持加载远程 HTML 页面实现 Web 与原生的双向通讯可用于嵌入第三方网页或 H5 应…...
Java线上CPU飙高问题排查全指南
一、引言 在Java应用的线上运行环境中,CPU飙高是一个常见且棘手的性能问题。当系统出现CPU飙高时,通常会导致应用响应缓慢,甚至服务不可用,严重影响用户体验和业务运行。因此,掌握一套科学有效的CPU飙高问题排查方法&…...
以光量子为例,详解量子获取方式
光量子技术获取量子比特可在室温下进行。该方式有望通过与名为硅光子学(silicon photonics)的光波导(optical waveguide)芯片制造技术和光纤等光通信技术相结合来实现量子计算机。量子力学中,光既是波又是粒子。光子本…...
【电力电子】基于STM32F103C8T6单片机双极性SPWM逆变(硬件篇)
本项目是基于 STM32F103C8T6 微控制器的 SPWM(正弦脉宽调制)电源模块,能够生成可调频率和幅值的正弦波交流电源输出。该项目适用于逆变器、UPS电源、变频器等应用场景。 供电电源 输入电压采集 上图为本设计的电源电路,图中 D1 为二极管, 其目的是防止正负极电源反接, …...
C/C++ 中附加包含目录、附加库目录与附加依赖项详解
在 C/C 编程的编译和链接过程中,附加包含目录、附加库目录和附加依赖项是三个至关重要的设置,它们相互配合,确保程序能够正确引用外部资源并顺利构建。虽然在学习过程中,这些概念容易让人混淆,但深入理解它们的作用和联…...
【C++进阶篇】智能指针
C内存管理终极指南:智能指针从入门到源码剖析 一. 智能指针1.1 auto_ptr1.2 unique_ptr1.3 shared_ptr1.4 make_shared 二. 原理三. shared_ptr循环引用问题三. 线程安全问题四. 内存泄漏4.1 什么是内存泄漏4.2 危害4.3 避免内存泄漏 五. 最后 一. 智能指针 智能指…...
