JVM:垃圾回收器与内存分配策略简介

判断对象是否已经死亡的算法

1,引用计数算法

    引用计数算法给对象添加一个引用计数器,每当一个地方引用它是时,计数值就增加一;当引用失效时,计数值就建一,技术器为0的对象不可能在被使用。但是主流的JVM里没有使用它的,原因是他很难解决对象之间互相循环引用的问题。

    例如对象objA和objB都有字段instance,objA.instance = objB,objB.instance = objA,除此之外这两个对象再无任何引用,实际上这两个对象已经不可能在被访问,但是因为相互引用,所以计数值不为0,无法收集他们。

2,可达性分析算法

    这个算法的基本思路就是通过一系列的称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链,当一个对象到GC Roots没有任何的引用链相连时,证明此对象是不可用的,如图:

虽然对象object5,object6,object7是相互关联的,但是他们到GC Roots不可达,所以是可回收的。

常见的垃圾回收算法

1,标记-清除(Mark-Sweep)算法

    如同其名,它的回收有两个阶段,首先标记出所有的需要回收的对象,在标记完成后统一回收所有被标记的对象。它是一种最基本的回收方法,但他也有不足。一是效率不高,标记和清除的效率都不高,二是容易产生内存碎片(如图),碎片太多导致无法分配较大的对象时,就会提前触发另一次垃圾收集动作。后续的方法都是在它的基础上进行改进。

2,复制算法

    将内存的区域划分为相同大小的两块,每次只是用一块,在一块使用完成后,将其中的存活对象存储到另一块中,再把已使用的内存空间全部清理掉,这样就不用考虑内存碎片化的问题了,只需要移动堆顶的指针就行。但是这样的缺点就是只有一般半的内存可用。

    复制算法也是新生代使用的回收算法,IBM的一项研究表明,新生代中的对象98%是“朝生夕死”的,也就是存活对象只占了大概2%。所以没有必要按照1:1分配空间,而是将内存划分为一块较大的Eden空间和两块小的Survivor空间,每次使用Eden和一块Survivor。回收时,将Eden和Survivor中还存活的对象一次性的复制到另一块Survivor中,最后清理掉Eden和使用的Survivor。

    HotSpot虚拟机默认Eden和Survivot的内存大小比例为8:1,所以整个新生代中可用的空间容量为新生代总容量的90%。当然无法保证每次存活的对象只占不到10%,这时就可以通过分配担保机制使用老年代来继续存储。

三:标记-整理(Mark-Compact)算法

    进入老年代的对象一般都是存活率较高的,假如在一次对象回收的过程中全部的对象都存活,这时如果再用复制算法,那么需要的空间将是50%,浪费极高。所以老年代使用和标记-清除类似的方式,标记过程一样,但是后续操作不是直接清理,而是将可回收对象移动到内存的一端,然后清理掉另一端的其他剩余内存。如图:

四:分代收集算法

    这算是对GC垃圾收集算法的一个总结,当前虚拟机大都采用分代收集算法(),当前堆中根据对象的存活周期将内存划分为了几块,一般分为新生代和老年代。新生代中大量的对象都会死去,所以使用复制算法复制少量的对象;而老年代中对象存活效率高,没有额外的空间进行分配担保而且为了效率,就必须使用标记-整理算法。

参考:《深入理解Java虚拟机》

猜你喜欢

转载自blog.csdn.net/fanxing1964/article/details/79432971