HotSpot虚拟机垃圾回收算法的实现细节

垃圾回收算法
前面两章讲了虚拟机的内存分布,对象的内存分配以及垃圾回收算法等概念,垃圾回收主要分为两类:计数式垃圾回收和可达性垃圾回收,但是由于引用计数法存在对象循环引用的缺陷,所以现在虚拟机大多采用可达性垃圾回收,即用可达性分析的方法判断对象是否已经死亡。可达性分析又主要分为两个过程:根节点枚举和可达性分析。具体概念读者可参考前两章内存分布和垃圾回收策略。下面主要介绍一下hotspot是如何实现根节点枚举与可达性分析的。
根节点枚举
可达性分析算法需要从根节点开始根据引用关系进行向下搜索,那我们首先就需要找出有哪些根节点,即根节点枚举。根节点主要是在一些全局性引用(方法区中的静态变量)和执行上下文中(栈帧中的局部变量)。
根节点枚举过程中,需要让虚拟机看似冻结在某一个时间节点上,因为如果分析过程中,根节点的引用关系还在不断变化的情况下,无法保证根节点枚举的正确性,所以根节点枚举需要在一个可以保障一致性的快照中进行。即根节点枚举也是需要停顿所有用户线程的,同样会面临stop the world问题。
OopMap
尽管根节点主要是分布在方法区和栈帧中,但是由于现在虚拟机动不动就几十上百兆,这里面的变量更是数不胜数,要是挨个遍历,那成本无疑是巨大的,所以虚拟机是有办法直接找到根节点信息的----OopMap。OopMap中记录了那些地方保存着对象引用。既然OopMap里面记录着对象引用位置,那么我们就需要在对象引用关系发生变化时维护OopMap的正确性。但是能导致引用关系变化的指令太多了,我们如果给每个指令都生成一个OopMap,那维护成本无疑是巨大的。所以要在什么时候生成OopMap呢?
安全点
虚拟机只会在安全点生成OopMap,这也就注定了垃圾收集过程并不是在任何时间都能进行的,必须在安全点才能进行。什么是安全点呢?如何选择安全点呢。其实安全点就是一些指定的指令,只不过在这些指令后会生成OopMap,生成OopMap的这些点会被称为安全点,安全点如何选择呢,既然要在安全点进行垃圾收集,那安全点就不能让虚拟机等太长时间,不能说要进行垃圾收集了,还要等好久到安全点,这样肯定是不行的。现在的虚拟机指令没有说是因为执行时间太长而且是时间太长的,所以安全点选择的这些指令必须具有指令序列复用这一特性,就可被虚拟机大量重复执行的,如方法调用,跳转等,通俗讲就是使用率比较高的指令。
安全点选出以后呢,中断用户线程的方式有两种,一种是主动式中断,另外一种是被动式中断。主动式中断即虚拟机会中断所有用户线程,然后检查线程有没有到达安全点。如果没有则继续执行直到到达安全点为止,但是这种方式一般不会采用,一般都是采用被动式中断,即设置一个标志位,用户线程会去不断轮询这个标志位,当标志位为true时,线程会中断,标志位的选择与安全点是重合的。
安全区域
通过安全点,我们能将运行的线程挂起,但是如果线程处于sleep或者wait状态呢?
安全区域是指一旦线程执行进入这一块区域以后,根节点引用关系就不会再发生变化,线程进入安全区域以后,会主动将自己标志为进入安全区域,这是虚拟机在进行垃圾收集时将不会在检查这些线程,线程出安全区域也会检查垃圾收集是否完成,如果没完成则等待,完成则继续执行
记忆集和卡表
前面垃圾收集算法提到过解决老年代对新生代对象的跨区引用,虚拟机会维护一个记忆集的数据结构,其实只要是涉及部分收集的垃圾收集器都会涉及跨区引用的问题,记忆集中记录了哪些区域存在跨代引用,下面说集中记忆集的实现方式
字长精度:每一条记录精确到一个机器字长(处理器的寻址位数)。
对象精度:每一条记录精确到一个对象(兑对象内部存在跨区引用)。
卡精度:每一条记录精确到一块内存。
记忆集是一个概念,卡表是是每一条记录精确到一块内存的实现方式,HotSpot会维护一个数组,每个数组元素成为一个卡页,每个卡页对应一块内存。每个卡页中有一个标志位,初始为0,当区域中的对象存在跨代引用时,标志位会被置为1,HotSpot虚拟机进行垃圾收集时,就会扫描所有存在跨区引用的区域。
写屏障
如何维护卡表数据的正确性呢? 首先我们得知道卡表数据啥时候要更新—当其他区域的对象引用本区域的对象时。HotSpot虚拟机通过写屏障维护数据的正确性,什么是写屏障呢?写屏障救灾时对象引用更新动作时,形成一个环形通知,提供给虚拟机执行一些额外操作,其实就是类似SpringAop原理,使用动态代理,给一些指令提供额外的操作。
可达性分析
前面降了HotSpot虚拟机如何进行根节点枚举,根节点枚举完成后,需要根据引用关系向下搜索。向下搜索的过程也要面临stop the world问题,即也要在使虚拟机看起来像冻结在某一个时间节点的快照中进行,为啥要这样呢?如果GC线程和用户线程并发会有什么问题呢?
如果我们在向下分析过程中,对象被一个还未扫描的对象节点引用改为了被一个已经被扫描过的对象引用,这时候我们再去扫描的时候就扫描不到这个对象了,这时候这个对象会被虚拟机标记为垃圾,这时候如果把让回收了,就会出现问题。
HotSpot虚拟机通过两个方法解决这个问题:“增量更新”和“原始快照”
增量更新:在引用关系由未扫描的对象变为已经被扫描过的对象时,虚拟机会将这个被扫描过得对象记录下来,等扫描结束以后,重新对这个对象进行扫描,HotSpot虚拟机通过写后屏障纪录引用关系。
原始快照:在引用关系由未扫描的对象变为已经被扫描过的对象时,虚拟机会将这个被断开的引用关系记录下来,等扫描结束后,重新扫描这个引用关系。HotSpot虚拟机通过写前屏障纪录引用关系。
通过增量更新和原始快照两个方法,现在扫描阶段基本上可以做到与用户线程并发进行

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值