JVM:并发的可达性分析
当前主流编程语言的垃圾收集器基本上都是依靠可达性分析算法来判定对象是否存活的,可达性分析算法理论上要求全过程都基于一个能保障一致性的快照中才能够进行分析,这意味着必须全程冻结用户线程的运行。
- 在根节点枚举这个步骤中,由于 GC Roots 相比起整个 Java 堆中全部的对象毕竟还算是极少数,且在各种优化技巧(如 OopMap)的加持下,它带来的停顿已经是非常短暂且相对固定(不随堆容量而增长) 的了。
- 可从 GC Roots 再继续往下遍历对象图,这一步骤的停顿时间就必定会与 Java 堆容量直接成正比例关系了:堆越大,存储的对象越多,对象图结构越复杂,要标记更多对象而产生的停顿时间自然就更长。
要知道包含 “标记” 阶段是所有追踪式垃圾收集算法的共同特征,如果这个阶段会随着堆变大而等比例增加停顿时间,其影响就会波及几乎所有的垃圾收集器,同理可知,如果能够削减这部分停顿时间的话,那收益也将会是系统性的。
想解决或者降低用户线程的停顿,就要先搞清楚为什么必须在一个能保障一致性的快照上才能进行对象图的遍历?为了能解释清楚这个问题,我们引入三色标记(Tri-color Marking)作为工具来辅助推导,把遍历对象图过程中遇到的对象,按照 “是否访问过” 这个条件标记成以下三种颜色:
- 白色:表示对象尚未被垃圾收集器访问过。显然在可达性分析刚刚开始的阶段,所有的对象都是白色的,若在分析结束的阶段,仍然是白色的对象,即代表不可达。
- 黑色:表示对象已经被垃圾收集器访问过,且这个对象的所有引用都已经扫描过。黑色的对象代表已经扫描过,它是安全存活的,如果有其他对象引用指向了黑色对象,无须重新扫描一遍。黑色对象不可能直接(不经过灰色对象) 指向某个白色对象。
- 灰色:表示对象已经被垃圾收集器访问过,但这个对象上至少存在一个引用还没有被扫描过。
关于可达性分析的扫描过程,可以发挥一下想象力,把它看作对象图上一股以灰色为波峰的波纹从黑向白推进的过程。如果用户线程此时是冻结的,只有收集器线程在工作,那不会有任何问题。但如果用户线程与收集器是并发工作呢?收集器在对象图上标记颜色,同时用户线程在修改引用关系(即修改对象图的结构),这样可能出现两种后果。
- 一种是把原本消亡的对象错误标记为存活(即原本应该是白色的对象被误标为黑色),这不是好事,但其实这种情况是可以容忍的,只不过产生了一点逃过本次收集的浮动垃圾而已,下次收集清理掉就好。
- 另一种是把原本存活的对象错误标记为已消亡(即原本应该是黑色的对象被误标为白色),这就是非常致命的后果了,程序肯定会因此发生错误,下图演示了这样的致命错误具体是如何产生的。

Wilson 于 1994 年在理论上证明了,当且仅当以下两个条件同时满足时,会产生 “对象消失” 的问题,即原本应该是黑色的对象被误标为白色:
- 赋值器插入了一条或多条从黑色对象到白色对象的新引用;
- 赋值器删除了全部从灰色对象到该白色对象的直接或间接引用。
因此,我们要解决并发扫描时的对象消失问题,只需破坏这两个条件的任意一个即可。由此分别产生了两种解决方案:增量更新(Incremental Update)和原始快照(Snapshot At The Beginning,SATB)。
- 增量更新要破坏的是第一个条件,当要插入黑色对象指向白色对象的引用关系时,就将这个新插入的引用记录下来, 等并发扫描结束之后,再将这些记录过的引用关系中的黑色对象为根,重新扫描一次。这可以简化理解为,黑色对象一旦新插入了指向白色对象的引用之后,它就变回灰色对象了。
- 原始快照要破坏的是第二个条件,当要删除灰色对象指向白色对象的引用关系时,就将这个要删除的引用记录下来, 等并发扫描结束之后,再将这些记录过的引用关系中的灰色对象为根,重新扫描一次。这可以简化理解为,无论引用关系删除与否,都会按照刚刚开始扫描那一刻的对象图快照来进行搜索。
以上无论是对引用关系记录的插入还是删除,虚拟机的记录操作都是通过写屏障实现的。
在 HotSpot 虚拟机中,增量更新和原始快照这两种解决方案都有实际应用,譬如,CMS 是基于增量更新来做并发标记的,G1、Shenandoah 则是用原始快照来实现。
参考资料
《深入理解Java虚拟机》第 3 章:垃圾收集器与内存分配策略 3.4.6 并发的可达性分析
JVM:并发的可达性分析的更多相关文章
- 面试官:你说你熟悉jvm?那你讲一下并发的可达性分析
这是why技术的第35篇原创文章 上面这张图是我还是北漂的时候,在鼓楼附近的胡同里面拍的. 那天刚刚下完雨,路过这个地方的时候,一瞬间就被这五颜六色的门板和自行车给吸引了,于是拍下了这张图片.看到这张 ...
- 【JVM】GC 可达性分析中哪些算是GC ROOT?
至今为止,我基本上发现网上没有几个博客说的很明白的,今天我在这里斗胆总结一下,各位大佬,如有错误,还望指责 ^ _ ^ 首先那肯定还得是看看概念了,在JVM中,什么可以作为GC Root呢? 虚拟机栈 ...
- JVM 基础:回收哪些内存/对象 引用计数算法 可达性分析算法 finalize()方法 HotSpot实现分析
转自:https://blog.csdn.net/tjiyu/article/details/53982412 1-1.为什么需要了解垃圾回收 目前内存的动态分配与内存回收技术已经相当成熟,但为什么还 ...
- JVM中垃圾回收机制如何判断是否死亡?详解引用计数法和可达性分析 !
因为热爱,所以坚持. 文章下方有本文参考电子书和视频的下载地址哦~ 这节我们主要讲垃圾收集的一些基本概念,先了解垃圾收集是什么.然后触发条件是什么.最后虚拟机如何判断对象是否死亡. 一.前言 我们 ...
- JAVA垃圾回收-可达性分析算法
在java中是通过引用来和对象进行关联的,也就是说如果要操作对象,必须通过引用来进行.那么很显然一个简单的办法就是通过引用计数来判断一个对象是否可以被回收.不失一般性,如果一个对象没有任何引用与之关联 ...
- JVM探秘:MAT分析内存溢出
本系列笔记主要基于<深入理解Java虚拟机:JVM高级特性与最佳实践 第2版>,是这本书的读书笔记. MAT是分析Java堆内存的一个工具,全称是 The Eclipse Memory A ...
- 转: 关于Linux与JVM的内存关系分析
转自: http://tech.meituan.com/linux-jvm-memory.html Linux与JVM的内存关系分析 葛吒2014-08-29 10:00 引言 在一些物理内存为8g的 ...
- JVM并发机制的探讨——内存模型、内存可见性和指令重排序
并发本来就是个有意思的问题,尤其是现在又流行这么一句话:“高帅富加机器,穷矮搓搞优化”. 从这句话可以看到,无论是高帅富还是穷矮搓都需要深入理解并发编程,高帅富加多了机器,需要协调多台机器或者多个CP ...
- Python 多线程并发程序设计与分析
多线程并发程序设计与分析 by:授客 QQ:1033553122 1.技术难点分析与总结 难点1:线程运行时,运行顺序不固定 难点2:同一段代码,再不加锁的情况下,可能被多个线程同时执行,这会造成很多 ...
- jvm的GC日志分析 [转]
jvm的GC日志分析 标签: jvm内存javagc 2015-06-22 16:37 1566人阅读 评论(1) 收藏 举报 分类: Java(4) JVM的GC日志的主要参数包括如下几个: ...
随机推荐
- 接口文档API、剖析http协议, 原生http请求函数
http协议:超广本传输协议 特点: 短连接 请求完成后就断开 无状态 对于事务处理无记忆能力 媒体独立 客户端要指定适合的传输内容类型,如json http 是建立在tcp/ip协议之上的应用层 ...
- 如何简单使用Git
Git基本功能 Git基本功能 在具体介绍Git能做什么之前,先来了解下Git里的四个角色: workspace: 本地的工作空间. index:缓存区域,临时保存本地改动. local reposi ...
- 十大经典排序之快速排序(C++实现)
快速排序 通过一趟排序将待排序列分割成两部分,其中一部分记录的关键字均比另一部分记录的关键字小.之后分别对这两部分记录继续进行排序,以达到整个序列有序的目的. 思路: (1)选择基准:从数列中挑出一个 ...
- python——tips
一:python分号使用 每一条语句最后个加个分号:这是c,oc,java,php等语言中不可缺少的部分,但是对于python,分号是可加,可不加的 如:不加分号代码 class Person: na ...
- 【node打包缺包】Error: Can't walk dependency graph: Cannot find module 'jquery' from
问题: 在使用node打包时,终端报错提示Error: Can't walk dependency graph: Cannot find module 'jquery' from/.../ 这个提示缺 ...
- ARMV8 short notes
offset addressing pre-index addressing define like: load the compass content of the operand 2 to ope ...
- python学习记录(三)-数据类型
字符串格式化 var = 'abcde' # 切片 print(var[2],var[-1]) # c e print(var[1:3:1],var[-2:-5:-1],var[::-1]) # bc ...
- bash中的basename与dirname以及${}
var=/dir1/dir2/file.tar.gz basename $var #获取文件名 file.tar.gz dirname $var #获取目录名称 / ...
- c++中文编码格式
c++程序中涉及到中文字符的输入输出以及其他操作经常会出现乱码.乱码主要是由于程序的源文件编码.可执行文件编码以及程序运行环境的编码不匹配导致.比如,c++源程序文件编码为GB18030, 在源程序中 ...
- CSS伪类三角形
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...