【JVM篇】【垃圾回收常用算法】

【JAVA】【JVM篇】【垃圾回收常用算法】

来自二线的码农笔记,用自己的理解总结知识点,互相学习

简单概述

垃圾回收常用算法可以简单的分为两类,第一大类是如何找到垃圾对象,代表的有引用计数法和可达性分析算法可以帮助我们确定一个对象是否可以被回收。第二类是帮助我们清理垃圾腾出内存,代表的有标记清楚法、复制算法、标记整理法和分代收集算法,针对不通JVM内存不通的区采用不同的算法。接下自开始聊聊这些算法:

1.引用计数算法:判断对象的引用数量

引用计数算法是通过判断对象的引用数量来决定对象是否可以被回收。引用计数算法是垃圾收集器中的早期策略。在这种方法中,堆中的每个对象实例都有一个引用计数。当一个对象被创建时,且将该对象实例分配给一个引用变量,该对象实例的引用计数设置为 1。当任何其它变量被赋值为这个对象的引用时,对象实例的引用计数加 1(a = b,则b引用的对象实例的计数器加 1),但当一个对象实例的某个引用超过了生命周期或者被设置为一个新值时,对象实例的引用计数减 1。特别地,当一个对象实例被垃圾收集时,它引用的任何对象实例的引用计数器均减 1。任何引用计数为0的对象实例可以被当作垃圾收集。
优点: 速度很快,效率高
缺点:存在循环引用问题
主流的商用JVM实现都没有采用这种方式。

问题: 如果两个对象相互引用,那么这两个对象永远都不会回收,计数永远都不可能为0

image.png

2.可达性分析算法:判断对象的引用链是否可达

可达性分析算法是通过判断对象的引用链是否可达来决定对象是否可以被回收。

可达性分析算法是从离散数学中的图论引入的,程序把所有的引用关系看作一张图,通过一系列的名为 “GC Roots” 的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain)。当一个对象到 GC Roots 没有任何引用链相连(用图论的话来说就是从 GC Roots 到这个对象不可达)时,则证明此对象是不可用的,如下图所示。在Java中,可作为 GC Root 的对象包括以下几种:
1)虚拟机栈(栈帧中的本地变量表)中引用的对象。(可以理解为:引用栈帧中的本地变量表的所有对象)
2)方法区中静态属性引用的对象(可以理解为:引用方法区该静态属性的所有对象)
3)方法区中常量引用的对象(可以理解为:引用方法区中常量的所有对象)
4)本地方法栈中(Native方法)引用的对象(可以理解为:引用Native方法的所有对象)


image.png

即使在可达性分析算法中不可达的对象,也并非是“非死不可”的,这时候它们暂时处于“缓刑”阶段,要真正宣告一个对象死亡,至少要经历再次标记过程。 标记的前提是对象在进行可达性分析后发现没有与GC Roots相连接的引用链。
第一次标记并进行一次筛选。第二次标记这个对象还有最后一次机会,如果仍不可达到就要被回收。

前面两种算法都是如何确定给一个对象是垃圾,接下来看怎么处理?

3.标记-清除(sweep)

标记清除的算法最简单,主要是标记出来需要回收的对象,然后然后把这些对象在内存的信息清除。如何标记需要回收的对象,在上上面文章里面已经有说明。


image.png

4.标记-清除-压缩

这个算法是在标记-清除的算法之上进行一下压缩空间,重新移动对象的过程。因为标记清除算法会导致很多的留下来的内存空间碎片,随着碎片的增多,严重影响内存读写的性能,所以在标记-清除之后,会对内存的碎片进行整理。最简单的整理就是把对象压缩到一边,留出另一边的空间。由于压缩空间需要一定的时间,会影响垃圾收集的时间。


image.png

5.标记-清除-复制

这个算法是吧内存分配为两个空间,一个空间(A)用来负责装载正常的对象信息,,另外一个内存空间(B)是垃圾回收用的。每次把空间A中存活的对象全部复制到空间B里面,在一次性的把空间A删除。这个算法在效率上比标记-清除-压缩高,但是需要两块空间,对内存要求比较大,内存的利用率比较低。适用于短生存期的对象,持续复制长生存期的对象则导致效率降低

6.三种算法对比

分代式GC里,老年代常用mark-sweep(标记-清除);或者是mark-sweep/mark-compact(标记-清除-压缩)的混合方式,一般情况下用mark-sweep,统计估算碎片量达到一定程度时用mark-compact。这是因为传统上大家认为年老代的对象可能会长时间存活且存活率高,或者是比较大,这样拷贝起来不划算,还不如采用就地收集的方式。Mark-sweep、mark-compact、copying这三种基本算法里,只有mark-sweep是不移动对象(也就是不用拷贝)的,所以选用mark-sweep(cms)。
在分代式假设中,年轻代中的对象在minor GC时的存活率应该很低,这样用copying算法就是最合算的,因为其时间开销与活对象的大小成正比,如果没多少活对象,它就非常快;

简要对比三种基本算法:


image.png

你可能感兴趣的:(【JVM篇】【垃圾回收常用算法】)