Java 垃圾回收超详细讲解记忆集和卡表


Posted in Java/Android onApril 08, 2022

在说记忆集和卡表之前,先给大家介绍一下跨代引用的问题。

Java 垃圾回收超详细讲解记忆集和卡表

假如要现在进行一次只局限于新生代区域内的收集(Minor GC),但新生代的实例对象1在老年代中被引用,为了找出该区域(新生代)中所有的存活对象,不得不在固定的GC Roots之外,再额外遍历整个老年代中所有对象来确保可达性分析结果的正确性,反过来也是一样。遍历整个老年代所有对象的方案虽然理论上可行,但无疑会为内存回收带来很大的性能负担。

事实上并不只是新生代、老年代之间才有跨代引用的问题,所有涉及部分区域收集(Partial GC)行为的垃圾收集器,典型的如G1、ZGC和Shenandoah收集器,都会面临相同的问题。

那么如何才能解决跨代引用呢?

首先,跨代引用相对于同代引用来说仅占极少数。原因是跨代引用的对象应该倾向于同时生存或者同时死亡的(举个:如果某个新生代对象存在跨代引用,由于老年代对象难以消亡,该引用会使得新生代对象在收集时同样得以存活,进而在年龄增长之后晋升到老年代中,这时跨代引用也随即被消除了)。

依据上面说所,就不应再为了少量的跨代引用去扫描整个老年代,也不必浪费空间专门记录每一个对象是否存在及存在哪些跨代引用,只需在新生代上建立一个全局的数据结构(该结构被称为“记忆集”,Remembered Set),这个结构把老年代划分成若干小块,标识出老年代的哪一块内存会存在跨代引用。此后当发生Minor GC时,只有包含了跨代引用的小块内存里的对象才会被加入到GCRoots进行扫描。虽然这种方法需要在对象改变引用关系(如将自己或者某个属性赋值)时维护记录数据的正确性,会增加一些运行时的开销,但比起收集时扫描整个老年代来说仍然是划算的。

下面就来介绍一下这个全局的数据结构记忆集。

记忆集

记忆集是一种用于记录从非收集区域指向收集区域的指针集合的抽象数据结构。如果我们不考虑效率和成本的话,最简单的实现可以用非收集区域中所有含跨代引用的对象数组来实现这个数据结构,如下面代码所示:

//以对象指针来实现记忆集的伪代码
Class RememberedSet {
	Object[] set[OBJECT_INTERGENERATIONAL_REFERENCE_SIZE]; 
}

这种记录全部含跨代引用对象的实现方案,无论是空间占用还是维护成本都相当高昂。而在垃圾收集的场景中,收集器只需要通过记忆集判断出某一块非收集区域是否存在有指向了收集区域的指针就可以了,并不需要了解这些跨代指针的全部细节。那设计者在实现记忆集的时候,便可以选择更为粗犷的记录粒度来节省记忆集的存储和维护成本。下面列举了一些可供选择(当然也可以选择这个范围以外的)的记录精度:

  • 字长精度:每个记录精确到一个机器字长(就是处理器的寻址位数,如常见的32位或64位,这个 精度决定了机器访问物理内存地址的指针长度),该字包含跨代指针。
  • 对象精度:每个记录精确到一个对象,该对象里有字段含有跨代指针。
  • 卡精度:每个记录精确到一块内存区域,该区域内有对象含有跨代指针。

上面的,第三种“卡精度”所指的是用一种称为“卡表”(Card Table)的方式去实现记忆集,这也是目前最常用的记忆集的实现形式。

卡表和记忆集又有什么关系呢?

前面介绍记忆集的时候提到 记忆集其实是一种"抽象”的数据结构,抽象的意思是只定义了记忆集的行为意图,并没有定义其行为的具体实现。卡表就是记忆集的一种具体实现,它定义了记忆集的记录精度、与堆内存的映射关系等。关于记忆集与卡表的关系,可以按照Java中Map与HashMap的关系来类比理解(即接口和实现类来的关系)。

下面来详细说一下记忆集的具体实现卡表

卡表

卡表是使用一个字节数组CARD_TABLE[] 实现,每个元素对应其标识的内存区域一块特定大小的内存块,每个内存块称为卡页,hotspot使用的卡页是2^9大小 即512字节。如下图所示

Java 垃圾回收超详细讲解记忆集和卡表

这样我们就可以把某个区域按照卡页进行划分,假如我们现在要对新生代区域进行垃圾回收,那么就可以把老年代区域看成是一个卡页一个卡页划分好的,如下图所示。

Java 垃圾回收超详细讲解记忆集和卡表

如图所示,因为cardpage1中存在指向新生代的跨代引用,所以对应卡表的第一个位置为1,表明该page区域存在跨代应用的对象。

  • 卡表角度:因为page1中存在跨代饮用的对象,所以卡表对应的第一个位置记为1,表明page1这个元素变脏。
  • 内存回收角度:因为卡表的第一个位置为1,表明该page区域存在跨代应用的对象,垃圾回收的时候需要扫描该区域。

一个卡页的内存中通常包含不止一个对象,只要卡页内有一个(或更多)对象的字段存在着跨代指针,那就将对应卡表的数组元素的值标识为1,称为这个元素变脏(Dirty),没有则标识为0。在垃圾收集发生时,只要筛选出卡表中变脏的元素,就能轻易得出哪些卡页内存块中包含跨代指针,把它们加入GC Roots中一并扫描。这样就不需要扫描整个老年代大大减少GC Roots的扫描范围。 

到此这篇关于Java 万字超详细讲解记忆集和卡表的文章就介绍到这了,更多相关Java 记忆集和卡表内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Java/Android 相关文章推荐
浅谈@Value和@Bean的执行顺序问题
Jun 16 Java/Android
解决SpringCloud Feign传对象参数调用失败的问题
Jun 23 Java/Android
Java elasticsearch安装以及部署教程
Jun 28 Java/Android
Java常用函数式接口总结
Jun 29 Java/Android
详解Spring事件发布与监听机制
Jun 30 Java/Android
Mybatis-plus在项目中的简单应用
Jul 01 Java/Android
Java数据开发辅助工具Docker与普通程序使用方法
Sep 15 Java/Android
Java spring定时任务详解
Oct 05 Java/Android
Java 实战项目之家居购物商城系统详解流程
Nov 11 Java/Android
Java基础——Map集合
Apr 01 Java/Android
SpringCloud项目如何解决log4j2漏洞
Apr 10 Java/Android
Spring中的@Transactional的工作原理
Jun 05 Java/Android
Java 常见的限流算法详细分析并实现
Java 超详细讲解ThreadLocal类的使用
Java 通过手写分布式雪花SnowFlake生成ID方法详解
Java详细解析==和equals的区别
Apr 07 #Java/Android
Java 超详细讲解hashCode方法
Apr 07 #Java/Android
Java 关于String字符串原理上的问题
Apr 07 #Java/Android
Java虚拟机内存结构及编码实战分享
You might like
PHP使用memcache缓存技术提高响应速度的方法
2014/12/26 PHP
JS window.opener返回父页面的应用
2009/10/24 Javascript
JavaScript 利用Cookie记录用户登录信息
2009/12/08 Javascript
浅析onsubmit校验表单时利用ajax的return false无效问题
2013/07/10 Javascript
JavaScript获取客户端计算机硬件及系统等信息的方法
2014/01/02 Javascript
js获取select默认选中的Option并不是当前选中值
2014/05/07 Javascript
一个不错的字符串转码解码函数(自写)
2014/07/31 Javascript
js与C#进行时间戳转换
2014/11/14 Javascript
JS+CSS实现带小三角指引的滑动门效果
2015/09/22 Javascript
jQuery mobile 移动web(6)
2015/12/20 Javascript
JavaScript位置与大小(1)之正确理解和运用与尺寸大小相关的DOM属性
2015/12/26 Javascript
jQuery height()、innerHeight()、outerHeight()函数的区别详解
2016/05/23 Javascript
jQuery学习笔记——jqGrid的使用记录(实现分页、搜索功能)
2016/11/09 Javascript
React学习笔记之事件处理(二)
2017/07/02 Javascript
JavaScript常用事件介绍
2019/01/21 Javascript
angular 实现同步验证器跨字段验证的方法
2019/04/11 Javascript
jQuery 判断元素是否存在然后按需加载内容的实现代码
2020/01/16 jQuery
Vue中watch、computed、updated三者的区别及用法
2020/07/27 Javascript
[01:53]DOTA2超级联赛专访Zhou 五年职业青春成长
2013/05/29 DOTA
[07:54]DOTA2-DPC中国联赛 正赛 iG vs VG 选手采访
2021/03/11 DOTA
python3 图片referer防盗链的实现方法
2018/03/12 Python
pandas数据清洗,排序,索引设置,数据选取方法
2018/05/18 Python
python 爬虫 批量获取代理ip的实例代码
2018/05/22 Python
Python + selenium + requests实现12306全自动抢票及验证码破解加自动点击功能
2018/11/23 Python
python学习——内置函数、数据结构、标准库的技巧(推荐)
2019/04/18 Python
Python如何调用外部系统命令
2019/08/07 Python
计算pytorch标准化(Normalize)所需要数据集的均值和方差实例
2020/01/15 Python
Python接口测试数据库封装实现原理
2020/05/09 Python
定义一结构体数组表示分数,并求两个分数相加之和
2013/06/11 面试题
学雷锋月活动总结
2014/04/25 职场文书
电子商务系毕业生自荐信
2014/05/29 职场文书
农村环境卫生倡议书
2015/04/29 职场文书
2015中学学校工作总结
2015/07/20 职场文书
2015年物业管理员工工作总结
2015/10/15 职场文书
laravel ajax curd 搜索登录判断功能的实现
2021/04/17 PHP
如何利用python实现Simhash算法
2022/06/28 Python