垃圾回收器与内存分配策略之-回收前的准备

开门见山,这里要说的是关于Jvm垃圾回收机制中,还未设计到垃圾收集器时我们所需要具备的基本知识。

其实垃圾回收无非就是在一群对象集合中找到无用的可回收的对象,根据相关的算法进行回收。虽然短短的一句话描述完了垃圾回收,但是细剖的话,里面大有文章。这边文章我们会介绍怎么找对象(枚举根节点),在找到对象中如何判断哪些对象时是可以回收掉的(可达性分析算法),在回收对象的时候如何保证回收的过程中当前对象引用关系处于稳定状态(安全点,安全区域),除此之外对象跨代引用问题如何解决(卡表,写屏障,伪共享),具备这些条件后就坐等回收掉这些垃圾了(三种基本回收算法)

一.枚举根节点

我们都知道Jvm是根据一系列Gc Root的跟对象作为起始节点,从这些节点开始根据引用关系向下搜索,如果某个对象到Gc Root没有任何链路的话,则说明这个对象是不可用,那么问题来了,如何找到这些Gc Root呢?HotPot虚拟机使用的是OopMap的数据结构来存储哪些地址上是对象的类型,这样虚拟机直接从这个结构中就能很快的找到根节点。这个结构中的数据是在什么时候存储的呢?a)在类加载完成的时候,HotSpot虚拟机就把对象内存什么偏移量上是什么类型的数据计算出来。b)即使编译过程中,也会在特定的代码位置记录下栈和寄存器的哪些地方是引用,对于JNI来说,直接扫描器对象句柄就可以,没有OopMap结构来维护他们。这样的话,通过OopMap就能获取到对应的根节点了。但是记住,枚举根节点的这个过程是STW(stop the word)即暂停所有的用户线程。因为我们枚举出来的必须是准确的结果,所以不能让引用关系发生变化。

注意枚举根节点是STW以后从OopMap里面获取根节点的操作。

二.安全点和安全区域

安全点就是说程序执行到这里以后,当前的引用关系是处于一个稳定状态的标记点。只有用户线程执行到了安全点以后并且停顿下来才能执行垃圾回收,这样就保证了,在垃圾回收的时候当前系统的引用关系是稳定的,这样的回收才是更加可靠的。所以安全点的选取一般是”是否具有让程序长时间执行的的特征”,因为垃圾回收一般也很短暂,可能简单的几个for循环的功夫垃圾回收就搞定了。所以这里长时间的执行比如方法调用,循环跳转,异常跳转等都属于指令序列复用,所z以在这些时刻设置为安全点,引用关系指定是老稳定了!

在发生垃圾回收的时候,需要中断线程,那么就会设置一个标志位,各个线程执行的过程中会不断的去轮询这个标志位,一旦发现中断标志位为真时就在自己临近的安全点上主动挂起。这就是主动中断的思想。怎么实现轮询的呢,一般来说在要发生垃圾回收需要暂停用户线程时,虚拟机就会生成精简的轮询指令,并设置标记位。从而实现主动中断。

安全点对于运行中的线程没毛病,那么对于处于sleep或者block状态的线程他们没办法去执行轮询操作,那么怎么搞?虚拟机显然也不可能等他们重新运行以后轮询到标记位然后走到安全点在挂起来以后去垃圾回收吧。所以有了安全区域,即在这一段范围内,引用关系都是稳定的,所以线程执行到安全区域的代码时,首先标记自己进入安全区域了,那么垃圾回收时,不用搭理在安全区域挂起的线程,可放心执行垃圾回收。当线程唤醒以后,要检查一下虚拟机是否完成了枚举根节点或者垃圾收集过程中的其他的需要STW的过程,如果完成了,那就继续执行,没完成就一直等待,直到收到可以离开安全区域的信号为止。

那么挂起不在安全区域的线程虚拟机怎么处理?

答:个人理解,在安全区域的挂起就是不安全的,因为它的引用关系并不是稳定状态的,只不过是线程睡眠阻塞了而已,但凡线程唤醒后,如果垃圾收集器收集了不该回收的对象,那么就会出问题。而在安全区域内的,即说明引用关系是稳定的,OopMap中记录的也是这个稳定关系下的根节点,即已经确认好哪些要回收,那些不用回收了,所以在安全区内的回收才是安全的。因此我觉得不在安全区域内阻塞或者睡眠的线程,虚拟机会唤起他们,让其进入安全区域,在执行垃圾回收。

枚举跟节点完事了那么就要开始分析哪些节点是可回收哪些不应该回收!

枚举根节点出来了接下来就是分析哪些对象可以回收哪些对象不回收的时候了。即可达性分析算法。

三.可达性分析算法

垃圾收集器开始工作了,首先从根节点开始遍历,完事了发现一批节点没有引用链了,那么首先会对这批节点进行第一次标记,并且会对他们进行一次筛选。(筛选条件就是如果当前对象没有覆盖finalize方法或者当前的这个方法已经被执行了),这是一类,其次覆盖了没执行的是一类。前面的这类就可以认定是这次垃圾回收肯定要回收的了。剩余的这类会被放在FQuenue队列里面,然后由一个优先级较低的线程Finalier去执行这些线程的finalize方法,这里就是这个对象自救的机会。然后稍后垃圾回收器会对F-Quenue队列中的对象进行第二次标记(即检查一下有没有自救成功的),如果没有则全部被列为可以回收的。如果有自救成功的,则不回收。

这就是可达性算法分析,个人理解它这一顿操作(我们不管它内部怎么操作),虚拟机就知道哪些垃圾可以回收了。就会对其进行回收。

有个问题需要考虑,那就是在可达性分析时,对象如果存在跨代引用怎么办,即,新生代的对象被老年代所引用。但是单纯的扫描新生代就会错误认为这个对象要被回收,当然让虚拟机每次都去扫描整个老年代不现实,所以就有了卡表来解决这个问题。卡表是通过写屏障来实行更新卡表的操作的,那么关于cpu缓存来说可能会存在伪共享的问题。接下来一起介绍了。

四:卡表,写屏障,伪共享

补充:常见避免伪共享的问题方式是多余字段填充满缓冲行,比如线程A和线程B读取a,b变量,并且a,b变量是紧挨着的,那么在读取a的时候,给a填充数数据使其占满一行缓冲行,那么b变量就会被读取到第二个缓冲行,这样就能解决伪共享的问题。不然的话,a,b都被读取到一个缓冲行,那么如果对于a进行操作后,将缓冲行中的数据写入内存,那么线程B就得在从内存中读取一遍b了。

缓冲行,伪代码,详细参考
:https://www.jianshu.com/p/c4b67ed125f0

五.并发的可达性分析

上面阐述了可达性分析的过程,但是因为可达性分析算法是要求STW的,虽然在枚举根节点这个步骤中,GC Root相对于Java堆中对象来说,还是很少了,但是可达性分析需要分析整个堆,那么STW的时间和对象的大小是成正比例关系的,这里就不能忽视STW的时间了,所以实际中的可达性分析是用户线程和可达性分析算法一起执行,所以在这里就会出现一个问题,比如一趟分析完事后,可能把需要清除的对象没有清除,不需清除的对象清除了,因为用户线程也在运行,所以说引用关系可能会发生变化,第一个该删除的没删除,问题不是很大,但是第二个可能造成虚拟机宕机。这个问题就是对象消失问题。

说一下什么是对象消失,为了方便说明问题,用经典的三色标记来说:

白色:没有被扫描过,在分析结束后还是白色,就意味着需要被回收。

黑色:被垃圾收集器访问过,是存活的对象,并且这个对象上面的所有引用都扫描过了,无需扫描,并且不可能直接指向白色。

灰色:被垃圾收集器访问过,但是它身上的引用没有全部扫描完。

对象消失问题要同时满足下面两个条件才会发生:

a)复制器给黑色节点插入了一条或者多个白色节点 && 赋值器删除了灰色节点到这些白色节点的引用。

这样一来的话,就不会在扫描这些白色节点了,他们就会被回收,但是实际上,他们不能被回收! 这就是对象消失。

那么为了解决这个问题,破坏其中一个条件即可,有两个解决方案:

1.增量更新,这个是破坏第一个条件,即在并发可达性分析过程中,如果给一个已经判定为存活的对象插入了一个判断为要回收的对象,那么则记录下来这个判定为存活的对象,等并发可达性分析结束以后,在以这个对象为根节点重新扫描一次。这样即使条件b满足,也不会发生对象消失,这样就避免将不用删除的对象删除了。

2.原始快照,这个是破坏第二个条件,即在并发可达性分析过程中,如果删除了灰色对象引用的白色对象的关系时,则记录这个引用关系,在并发结束以后,以当前灰色节点为跟节点,再次扫描。这样即使全部插入黑色了,也会在被扫描到,也不会发生对象丢失问题。

上述两种解决方案都是对发生变化的白色对象再次进行扫描。在实际的应用两个方案都有用到,比如CMS就是增量更新来做并发标记,G1和Shenandoah是原始快照来实现的。

到这里如何筛选出要回收的对象已经出来了,面对这些对象,垃圾回收器怎么回收的呢?那么就看看基本的三种回收算法

五.三种基本回收算法

a)标记-清除

解释:顾名思义,就是标记出来需要回收的对象,然后统一回收所有被标记的对象,当然也可以反过来,标记存活对象,统一回收未被标记的对象。其中标记的过程就是截至前面说的找到要回收的对象的过程。

优点:简单

缺点:a)执行效率不稳定,如果对象很多,并且大部分需要回收,那么回收的过程就会很慢。b)会造成内存碎片、

b)标记-复制算法

解释:将新生代分为8:1:1,即Eden:Survivor:Survivor.每次分配内存只使用Eden和其中一块Survivor。发生垃圾回收后,将其存活的对象复制到另外一个Survivor,然后清除当前内存。

优点:减少内存碎片

缺点: 内存担保机制。即剩余存活对象大于剩余Survivor空间,通过分配担保机制让多余对象直接进入老年代。

c)标记-整理算法

因为复制收集算法在对象存活率比较低的时候时候使用,而对象存活率比较高的时候,就不适用了。所以对于老年代有人提出了标记整理算法。

实现过程:和标记清除-算法一样,但是这个标记以后并不是立马清除,而是让存活的对象都向一端移动,然后直接清除掉边界以外的内存。

以上就是了解垃圾回收器前需要具备的知识点,如果你具备了,相信你在读垃圾收集器时,会轻松很多!

声明:本站部分文章内容及图片转载于互联 、内容不代表本站观点,如有内容涉及侵权,请您立即联系本站处理,非常感谢!

(0)
上一篇 2020年5月24日
下一篇 2020年5月24日

相关推荐