JVM是如何解决跨代引用的?

JVM是如何解决跨代引用的?

跨代引用问题

假如要现在进行一次只局限于新生代区域内的收集(Minor gc),但新生代的对象1在老年代中被引用,为了找出该区域(新生代)中所有的存活对象,不得不在固定的gc roots之外,在额外遍历整个老年代中所有对象来确保可达性分析结果的正确性。

遍历整个老年代所有对象的方案虽然可行,但是会给内存回收带来很大的性能负担。

实时上并不是只有新生代、老年代才有跨代引用的问题,所有涉及部分区域手机行为的垃圾收集器,典型的如G1、ZGC收集器,都会面临同样的问题。

如何解决跨代引用

先说结论,JVM是使用 写屏障 + 卡表 解决的。

  • 首先,跨代引用是少数的,例如:某个新生代对象存在跨代引用,由于老年代对象难以消亡,就会导致新生代对象在收集时同样得已存活,进而在年龄增长之后晋升到老年代中了。
  • 所以就不用为了少量的跨代引用扫描整个老年代,只需要在新生代建立一个全局的数据结构(该结构被称为 记忆集,Rember Set),标识出老年代的哪一块内存存在跨代引用,此后发生minor gc时,只有包含了跨代引用的小块内存里的对象才会被加入gc root进行扫描。
  • 个人理解,card table是用来划分老年代的,即把老年代划分多个大小相等的连续区域,而Rember set中记录了卡表的位置。

记忆集(Remembered Set)

一种记录 从非收集区域(如老年代) 指向 收集区域(如年轻代)的指针集合的抽象数据结构,在对象层面来说就是非收集区域对收集区域对象的引用记录。

它存放在收集区域,比如在新生代里面存放着老年代对新生代的每一个引用,这样在收集新生代的时候,我们就可以根据记忆集知道哪些对应被老年代对象引用了,不能回收,这就解决了跨代引用过的问题。

记忆集根据记录的精度可以分三类实现方式:

  • 字长精度:记录的是老年代指向新生代地址。
  • 对象精度:记录的是老年代引用的新生代对象。
  • 卡精度:记录的是新生代一段地址是否存在被老年代引用的记录

卡表

是以第三种卡精度的方式实现的记忆集,也是目前最常用的方式。记忆集是抽象的概念,而卡表就是记忆集的一种具体实现。

卡表最简单的形式可以是一个字节数组,HotSpot就是这样实现的。

CARD_TABLE [this address >> 9] = 0;

把地址的值右移9位相当于除于512就是卡表索引,每字节512为一组对应卡表同一个元素,一组就是一个卡页,如果这个卡页中只要有一个对象被其他区域对象所引用,对应卡表元素的值就变成1,也就是所谓的元素变脏。

在垃圾回收时,只要筛选出卡表中变脏的元素,就能轻易得出哪些卡页对应的内存包含跨代指针,把他们加入GC Rootsz中一并扫描。

写屏障

可以看成是虚拟机层面在"引用类型字段赋值"这个动作的AOP切面,引用对象赋值的时候产生一个环形通知,进行一些额外的处理,这样就是引用对象赋值这个操作都在写屏障的覆盖范围内,赋值前的写屏障叫写前屏障,赋值后的写屏障叫写后屏障。

卡表的更新,就是通过写屏障写入的。

相关推荐
爱棋笑谦3 小时前
JVM基础
jvm
懒洋洋大魔王13 小时前
7.Java高级编程 多线程
java·开发语言·jvm
只吹45°风13 小时前
JVM-类加载器的双亲委派模型详解
jvm·类加载器·双亲委派
五味香16 小时前
C++学习,动态内存
java·c语言·开发语言·jvm·c++·学习·算法
longlongqin18 小时前
JVM 虚拟机的编译器、类加载过程、类加载器有哪些?
jvm
niceffking18 小时前
JVM HotSpot 虚拟机: 对象的创建, 内存布局和访问定位
java·jvm
刘大猫.1 天前
Arthas dashboard(当前系统的实时数据面板)
jvm·arthas·dashboard·当前系统的实时数据面板·dashboard命令·arthas命令
longlongqin1 天前
JVM 内存结构?
jvm
Joeysoda1 天前
Java数据结构 时间复杂度和空间复杂度
java·开发语言·jvm·数据结构·学习·算法
18你磊哥1 天前
java重点学习-JVM组成
java·开发语言·jvm