JVM-三色标记

news2024/11/26 18:22:18

一、什么叫三色标记

三色也叫三色抽象,它是所有mutator和collector都必须遵守的定律。它把对象标记为三种颜色:

白色:对象还未被垃圾收集器访问,在回收的开始阶段所有的对象均为白色(当然了这只是指概念上的,一般在初始标记时都会顺便对根的直达对象染色)。而当这次回收结束之后,所有白色对象均为不可达对象(消亡的对象)

灰色:已经被垃圾收集器访问过了,但他还有其他的field需要垃圾收集器去访问。

黑色:已经被垃圾收集器访问过了,并且他的所有filed也被访问过了。黑色对象的任何一个filed都不可能是白色对象。并且黑色对象永远都不会被垃圾收集器再次访问,除非它的颜色发生变化。

二、三色标记的过程和缺点

2.1 标记过程

每次垃圾收集都从GC-Roots出发,然后遍历整个堆。如果把他们画成图如上图。

  • 第一次A被垃圾收集器扫描到了,但是field是B还没被扫描到,为灰色

  • 第二次扫描了B,这时候A的field全部被扫描了,就标记黑色,由于B的C、D还没被扫描到,标记灰色

  • 第三次C、D都被扫描了,所以B标为黑色,但是C的field是E,D的field是G,所以C、D为灰色

  • 第四次E、G都被扫描了,并没有其他field,所以直接标记为黑色

  • 由于F没有引用,属于不可达对象,随时会在垃圾收集被回收掉

2.2 缺点

假如正在运行的java程序和垃圾收集器没有互相干扰的话,那么垃圾收集器在标记过程中不会有任何问题。但是一但垃圾收集器在执行标记工作的时候,java程序也在同时运行,那么问题就来了,如下图:

当B被垃圾收集器染成灰色是,java程序删除了B-->C的引用,并在随后新增了A-->E的引用。因此B-->C的引用链断开了,根据三色标记算法的定义,垃圾收集器不能重新从黑色对象出发去扫描E,所以E对象被视为不可达对象,会被误标为已消亡,这就是“对象消失”问题,它造成的后果也是致命的。

经过研究发现:

  • 用户线程插入一条或多条从黑色到白色对象的新引用。

  • 用户线程删除全部从灰色对象到白色对象的直接或间接引用。

当以上两条同时出现时,就会出现“对象消失”问题。也就是说我们只要保证这两条不会同时出现,就可以解决这个问题。因而产生了两种解决的方案--增量更新(Incremental Update)和原始快照(Snapshot At The Beginning, SATB)

三、 增量更新和原始快照

增量更新就是当黑色对象插入新的指向白色对象的引用关系时, 就将这个新插入的引用记录下来, 等并发扫描结束之后, 再将这些记录过的引用关系中的黑色对象为根, 重新扫描一次。 这可以简化理解为, 黑色对象一旦新插入了指向白色对象的引用之后, 它就变回灰色对象了

原始快照就是当灰色对象要删除指向白色对象的引用关系时, 就将这个要删除的引用记录下来, 在并发扫描结束之后, 再将这些记录过的引用关系中的灰色对象为根, 重新扫描一次,这样就能扫描到白色的对象,将白色对象直接标记为黑色(目的就是让这种对象在本轮gc清理中能存活下来,待下一轮gc的时候重新扫描,这个对象也有可能是浮动垃圾)

以上无论是对引用关系记录的插入还是删除, 虚拟机的记录操作都是通过写屏障实现的。

1.1 写屏障

  • 给某个对象的成员变量赋值时,其底层代码大概长这样:

/**
* @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);  // 记录新引用的对象
}

1.2 读屏障

oop oop_field_load(oop* field) {
    pre_load_barrier(field); // 读屏障-读取前操作
    return *field;
}

读屏障是直接针对第一步:D d = a.b.d,当读取成员变量时,一律记录下来:

void pre_load_barrier(oop* field) {  
    oop old_value = *field;
    remark_set.add(old_value); // 记录读取到的对象
}

四、三色标记的应用

现代追踪式(可达性分析)的垃圾回收器几乎都借鉴了三色标记的算法思想,尽管实现的方式不尽相同:比如白色/黑色集合一般都不会出现(但是有其他体现颜色的地方)、灰色集合可以通过栈/队列/缓存日志等方式进行实现、遍历方式可以是广度/深度遍历等等。

对于读写屏障,以Java HotSpot VM为例,其并发标记时对漏标的处理方案如下:

  • CMS:写屏障 + 增量更新

  • G1,Shenandoah:写屏障 + SATB

  • ZGC:读屏障

工程实现中,读写屏障还有其他功能,比如写屏障可以用于记录跨代/区引用的变化,读屏障可以用于支持移动对象的并发执行等。功能之外,还有性能的考虑,所以对于选择哪种,每款垃圾回收器都有自己的想法。

为什么G1用SATB?CMS用增量更新?

SATB相对增量更新效率会高(当然SATB可能造成更多的浮动垃圾),因为不需要在重新标记阶段再次深度扫描被删除引用对象,而CMS对增量引用的根对象会做深度扫描,G1因为很多对象都位于不同的region,CMS就一块老年代区域,重新深度扫描对象的话G1的代价会比CMS高,所以G1选择SATB不深度扫描对象,只是简单标记,等到下一轮GC再深度扫描。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

PaddleNLP开源UTC通用文本分类技术,斩获ZeroCLUE、FewCLUE双榜第一

飞桨PaddlePaddle 2023-01-12 20:02 发表于湖北 针对产业级分类场景中任务多样、数据稀缺、标签迁移难度大等挑战,百度提出了一个大一统的通用文本分类技术UTC(Universal Text Classfication)。 UTC在ZeroCLUE和FewCLUE两个榜单上均位居榜首…

css加载会造成阻塞吗?

目录 A.设置网络加载速度 B.css加载会阻塞DOM树的解析渲染吗? 1.css会阻塞DOM树解析? 2.css加载会阻塞DOM树渲染? 3.个人对这种机制的评价 3.css加载会阻塞js运行吗? 4.结论 可以使用以下几种方法解决CSS加载速度的问题: …

曲线曲率介绍和python求法

目录曲率1.1 弧长参数 s参考资料曲率 比如我们想知道曲线 AB\boldsymbol{AB}AB 上任一点处的弯曲程度怎么办呢?这时就需要一个十分重要的概念——曲率。 维基百科: 在数学中,曲率(curvature)是描述几何体弯曲程度的量…

Octave安装与使用

1. 介绍 Octave是一种编程语言,旨在解决线性和非线性的数值计算问题。Octave为GNU项目下的开源软件,早期版本为命令行交互方式,4.0.0版本发布基于QT编写的GUI交互界面。Octave语法与Matlab语法非常接近,可以很容易的将matlab程序…

机器学习/人工智能 实验二:图像特征自动学习方法实践与分析

写在前面 参考的是https://zh.d2l.ai/index.html 一、实验目的与要求 (1)利用基于深度学习的特征自动学习方法完成图像特征提取的实验方案的设计。 (2)编程并利用相关软件完成实验测试,得到实验结果。 (3)通过对实验数据的分析、整理,得出实验结论&am…

Unity | 序列化(Serialized)和反序列化(NonSerialized)是什么意思

一、什么是序列化 官方叙述:序列化是将对象的状态信息转换为可以存储或传输的形式的过程。 人话叙述:我们平时输入的代码,实际上是不能储存或者传输的,所以我们需要翻译一下,翻译成能储存或者翻译的文字,这…

【SpringBoot 学习】52、SpringBoot 使用 grpc 实现远程服务调用

文章目录一、SpringBoot 使用 grpc 实现远程服务调用1、服务端编写2、客户端编写一、SpringBoot 使用 grpc 实现远程服务调用 gRPC 是一个现代的、开源的、高性能的远程过程调用(RPC)框架,可以在任何地方运行。gRPC 使客户端和服务器应用程序…

Java-Thread多线程的使用

Java-Thread多线程的使用一、线程,进程,并发,并行的概念1.进程2.线程3.并发和并行二、线程的创建和使用1.通过继承Thread类,重写run方法2.实现Runnable接口,重写run方法3.使用案例三、线程的常用方法四、线程的退出和中…

关于机器人状态估计(11)-VIO单目与双目/雷达Lidar SLAM/未来的机器人

写这篇文章的时候刚发生行业大事件: Google收购ROS 其实一开始还是水,绝对大量文不对题,但是必有干货,毕竟用的是这个关键的系列标题。 最近有几件行业内发生的大小事,让我觉得有必要更一下。 首先是Livox的mid-360…

VueUse(中文)——核心函数:State相关函数

VueUse官方地址 一、createGlobalState 将状态保持在全局范围内,以便跨Vue实例重用 1、没有持久性(存储在内存中) 例如: 或者 2、持久性存储 使用useStorage()存储在localStorage:例如: 组件使用: 二、createInjectionSt…

【图灵商城】前、后端项目搭建与运行

【图灵商城】前、后端项目搭建与运行 项目介绍 图灵商城-基础班架构图,如下所示: 本节是项目代码的初始化,今天是2023-01-15,先预祝大家新年快乐!!! 图灵商城这个项目是一个前后端分离的项目…

我这是这样知道 React TS 中的 Event Handler 类型的

开头 现在 TypeScript 的发展也越来越成熟,已逐渐应用到我们开发的前端项目之中,它能够带来类型提示,提前规避类型上的错误,来提高项目代码的健壮性,以及更高效的编码效率,前提就是我们需要定义好相应的类…

创客匠人赋能线上瑜伽健身实现流量增长

近年来,我国的儿童、成年人肥胖率逐年攀升。身边发胖的人越来越多,尤其是步入中年的人群,很多都有“发福”的现象。 超重肥胖已经成为影响我国居民健康的重要公共卫生问题。在身材焦虑和疫情肆虐下,很多人开始重视自己的健康问题…

python学习笔记---Python基础【廖雪峰】

Python基础 数据类型和变量 整数 对于很大的数,例如10000000000,很难数清楚0的个数。Python允许在数字中间以_分隔,因此,写成10_000_000_000和10000000000是完全一样的。十六进制数也可以写成0xa1b2_c3d4。 >>> prin…

Qt opencv编译详细教程(windows版)

在Qt中使用opencv这个算法视觉库,前期环境步骤搭建如下: 1、下载cmake。 2、下载opencv库,我这里下载的是opencv-3.3.1版本。 3、没有安装Qt的下载Qt软件,Qt的环境安装部署,这里不做介绍。 4、安装cmake后,…

python学习笔记---IO编程【廖雪峰】

IO编程 ​ IO在计算机中指Input/Output,也就是输入和输出。由于程序和运行时数据是在内存中驻留,由CPU这个超快的计算核心来执行,涉及到数据交换的地方,通常是磁盘、网络等,就需要IO接口。 ​ IO编程中,S…

冒泡排序算法的实现和优化~

冒泡排序算法: 算法思想: 反复扫描待排序记录序列,在扫描的过程中,顺次比较相邻的两个元素的大小,若逆序就交换位置 文字描述该算法: 以升序为例: 依次比较数组中相邻两个元素大小&#xf…

win10+ubuntu23.04双系统安装

win10win10先安装好(确保主板上各个螺丝稳定,至少4对螺丝铜柱,否则会各种蓝屏)如果双系统安装失败了,连win10都进不去了,用原版ISO刻录的U盘或者光驱来修复引导。Easybcd安装Ubuntu23.04使用的ubuntu镜像文…

【项目实战】使用Java Keytool工具生成的CSR给第三方云平台签名

一、背景 客户要求我们提供一个CSR文件,给他们签名,他们的服务器是部署在Amazon上的。 二、Keytool是什么? Keytool 是一种 Java中的数字证书管理工具,用于管理密钥和证书。 它可以用来生成/申请数字证书、导入证书、导出证书、…

优秀的程序员是如何做好时间管理的

程序员是一项既消耗脑力,又消耗体力的职业,想成为一名成功的程序员,不仅要靠坚持不懈的努力,异于常人的天赋,更需要一套行之有效的时间管理方法,才能让自己在有限的时间内写出更好的代码,获得更…