跨代引用、记忆集、卡表
一、什么是跨代引用?
新生代中的对象持有了老年代中的对象的引用 或 老年代中的对象持有了新生代中对象的引用。
如下图所示,新生代中的A对象持有了老年代中C对象的引用;老年代中的C对象持有了新生代中B对象的引用;老年代中的D对象持有了新生代中E对象的引用。
二、跨代引用所带来的问题
当进行一次只局限于新生代区域内的垃圾回收(Minor GC),但是新生代中的对象完全有可能被老年代中的对象所引用。为了找出这个区域中的存活对象,不得不在固定的GC Roots之外,在额外的遍历整个老年代中的对象,来确保可达性分析结果的准确性,反之也是一样,这样就会给内存回收带来很大的负担。
Minor GC/Young GC:针对新生代的垃圾收集
Major GC/Old GC:针对老年代的垃圾收集
MixedGC:针对于新生代与部分老年代的垃圾收集
Full GC:针对于整个Java堆和方法区(MetaSpace元空间) 的垃圾收集
三、如何解决这个问题
跨代引用假说:存在相互引用关系的两个对象应该是倾向于同生共死的。举个例子,如果新生代对象存在跨代引用,由于老年代对象难以消亡,该引用会使得新生代对象在垃圾收集时同样得以存活,进而在年龄到达阈值后进入老年代中,这时候跨代引用也随之被消除。
依据这条假说,只有少量对象才会存在跨代引用的问题。因此没有必要为了少量的跨代引用而去扫描整个老年代,也没有必要浪费空间去专门记录没一个对象是否存在及存在哪些跨代引用,只需要在新生代上建立一个全局的数据结构,这个数据结构将老年代划分为若干个小块,每次都只记录老年代中的哪一块内存存在跨代引用。此后每当发生Minor GC时,只有包含了跨代引用的那一小块内存才会被加入到GC Roots中进行扫描。
上述数据结构就被称之为记忆集,可以简单理解为一种用于记录从非收集区域指向收集区域的指针集合的抽象数据结构。记忆集的记录精度可以分为不同级别,下面展示三种记录精度:
- 字长精度:每个记录精确到机器字长,该字包含跨代指针
- 对象精度:每个记录精确到一个对象,该对象的字段中含有跨代指针
- 卡精度:每个记录精确到一块内存区域,该区域中含有跨代指针
其中,第三种卡精度所指的是一种被称之为卡表的实现方式。
四、卡表
卡表最简单的形式可以只是一个字节数组。
CARD_TABLE[this address >> 9] = 0;
字节组CARD_TABLE的每一个元素都对应着其标示的内存区域中一块特定大小的内存块,这个内存块被称之为"卡页"。一般来说,卡页大小都是2的N次幂的字节数,从上述代码中可以看出Hotspot中使用的卡页是2的9次幂,即512字节。
一个卡页内存中通常包含不止一个对象,只要卡页内某个对象的字段存在跨代指针,那就将对应卡表数组对应位置上的元素标示为1,称之为Dirty,没有则标示为0,称之为Clean。在垃圾收集时,只需要筛选出卡表中变脏的元素,就能够找到对应卡页内存块中包含的跨代指针,将其加入到GC Roots中一并扫描。
五、写屏障
5.1 卡表元素如何维护?
当有其他分代区域中的对象引用了本区域的对象时,其对应的卡表元素就应该变脏,变脏的时间点原则上应该发生在引用类型字段被赋值的那一刻。
5.2 如何在对象赋值的一刻去更新卡表?
假设是解释执行的字节码,虚拟机负责每条字节码指令的执行,有充分的时间介入;但是在编译执行的场景中,经过即时编译后得到的代码已经是纯粹的指令流了,这就必须要找到一个在机器码层面的手段,将维护卡表的动作放到没一个赋值操作中。
在HotSpot虚拟机中是通过写屏障技术来维护卡表的。写屏障可以看做是虚拟机层面上对于**“引用类型字段赋值”**这个动作的AOP切面,在引用对象赋值时会产生一个环绕式通知,供程序执行额外的动作,也就是说赋值的前后都在写屏障的覆盖范畴内。在赋值前的写屏障叫做写前屏障,在赋值后的写屏障叫做写后屏障。
void oop_field_store(oop* field, oop new_value) {// 引用字段赋值操作 *field = new_value; // 写后屏障,在这里完成卡表状态更新 post_write_barrier(field, new_value);
}
5.3 写屏障带来的性能问题
应用写屏障后,虚拟机就会为所有的赋值操作生成相应的指令,一旦收集器在写屏障中增加了更新卡表的操作,无论更新的是老年代对新生代对象的引用,每次只要对引用进行了赋值操作,就会判断是否需要更新卡表,从而产生额外的开销,不过这个开销与MinorGC时扫描整个老年代的代价要低的多。
跨代引用、记忆集、卡表相关推荐
- Hotspot细节实现安全区域、记忆集卡表
Hotspot细节实现 文章目录 Hotspot细节实现 3.安全区域 产生原因 安全区域概念 实现过程 4.记忆集与卡表 产生原因: 记忆集概念 实现过程: 卡精度(卡表) 3.安全区域 产生原因 ...
- JVM之记忆集|卡表|写屏障
背景 分代收集理论的时候,会存在为了解决对象跨代引用所带来的的问题.垃圾收集器在新生代中建立了名为记忆集的数据结构,用来避免把整个老年代加进GC roots扫描范围.事 实上并不只是新生代.老年代之间 ...
- G1技术细节之记忆集和卡表解决跨代引用问题
跨代引用面临的问题 首先,产生跨代引用场景是发生YongGC的过程.此时新生代的对象会开始寻找根,看自己是否属于根可达对象,从而判断自己是否是垃圾. 那很多同学就开始有疑惑了?不是判断对象是否存活,应 ...
- JVM之记忆集和卡表
当我们进行young gc时,我们的gc roots除了常见的栈引用.静态变量.常量.锁对象.class对象这些常见的之外,如果老年代有对象引用了我们的新生代对象,那么老年代的对象也应该加入gc ro ...
- JVM垃圾回收-记忆集和卡表
- jvm根节点枚举、安全点、安全区域、记忆集、卡表、写屏障、并发的可达性分析
讲具体的实现之前,先说说几个和这些垃圾回收器息息相关的一些知识点,可以有一个更好的理解 1.根节点枚举 也就是可达性分析算法从GC Roots集合中找引用链的过程,可作为GC Roots的节点主要在全 ...
- 记忆集、卡表、G1垃圾收集器简介
记忆集 在新生代做GCRoots可达性扫描过程中可能会碰到跨代引用的现象这种如果又去对老年代再去扫描效率太低了为此 在新生代引入记录集(Remember Set)的数据结构(记录从非收集区到收集区的指 ...
- 记忆集(Remember Set)和卡表(Card Table)
记忆集和卡表 为了解决对象跨代引用所带来的问题,垃圾收集器在新生代中建立了名为记忆集(Remembered Set)的数据结构,用以避免把整个老年代加进GC Roots扫描范围.事实上并不是只是新生代 ...
- 安全点、安全区域、记忆集、卡表与写屏障
一.安全点(safe point) 1.1.定义 用户程序执行时并非在代码指令流的任意位置都能够停顿下来开始垃圾收集,而是强制要求必须执行到达特殊位置后才能够暂停,这些位置被称为安全点 ( Safep ...
最新文章
- 【AI参赛经验】汉字书法识别比赛经验心得——by:microfat_htu
- CoRL 2020奖项公布,斯坦福获最佳论文奖,华为等摘得最佳系统论文奖
- ylbtech-Unitity-CS:Hello world
- mybatis association表关联与rowbounds共同使用时的异常及其解决方案
- pfSense DMZ配置
- BAPI_ACC_DOCUMENT_POST生成预制凭证增强
- 【springboot】启动时指定lib目录
- Oracle学习(十六)Oracle安装
- 生成pyd文件时提示“Unable to find vcvarsall.bat”的问题
- linux信号及其含义
- 10恢复出厂设置_笔记本电脑怎么恢复出厂设置
- 高品质平板电脑模型为您的作品演示加分
- DML DDL DCL区别
- Hive数据更新同时去重入门
- Web设计规范----控件、组件
- Gh0st3.6编译和源码免杀问题
- 利用Excel爬取网页数据
- wps中的word删除空白页
- (转)用4年多时间, 带领微软重登全球市值第一宝座, 纳德拉是如何做到的?
- 关闭Tomcat报错The web application [ROOT] appears to have started a thread named [Abandoned connectio
热门文章
- 我用python爬取了整个斗图网站
- [HTML CSS JS ES6 JS WebAPI JQuery]学习笔记目录
- 家装家居行业该如何进行网络推广?
- Credit Card
- yolov5 代码解读 --common.py
- 拟真机器人拯救者怎么来_俄罗斯“拯救者”机器人项目进入测试阶段(组图)
- Windows电脑申请iOS证书详细流程和工具
- Could not resolve view with name 'bok.html' in servlet with name 'spring'
- 利用Python实现QQ实时到账 免签支付 原创
- 培养孩子的时间管理能力是如此的重要!