JVM 垃圾回收算法

pengjiang 9年前

来自: http://my.oschina.net/u/1041012/blog/616142


 


对象可触及性算法

可触及性算法是判断对象是否死亡的最基本的算法。所谓死亡对象就是内存堆中不再使用的对象。 
用于被GC回收的对象。

引用计数算法 Reference Counting

引用计数法是给对象中添加一个引用计数器,每当有一个地方引用它时,计数器加1, 
当引用失效时,计数器减1;任何时候计算器都为0的对象就是不可触及的对象。

Reference Counting 判定效率高,实现简单。 
微软的COM,ActionScript3,FlashPlayer3,Python 都采用Reference Counting来计算

Java 语言中没有采用这个算法的主要原因是,它难以解决循环引用的问题。

根搜索算法 GC Root Tracing

基本思路就是就是通过一系列名为 GC Root 的对象作为起始点,从这些节点开始向下搜索, 
搜索走过的路劲作为引用链,当一个对象到GC Root 没有任何引用链时,则证明此对象不可触及

GC Root 包括以下几种

  • 虚拟机栈中的引用的对象

  • 方法区中的类静态属性引用的对象

  • 方法区中的常量引用的对象

  • 本地方法栈中JNI的引用的对象。

垃圾回收算法

标记-清楚算法 Mark-Sweep

标记-清楚算法是最基本的算法,其算法分为两个阶段,标记阶段和清除阶段。 
首先标记出所需要回收的对象,标记完成后统一收掉所有的被标记的对象。

他的缺点主要有两个:

  • 效率问题,标记和清除的效率都不高

  • 空间问题,标记清除后会产生大量的碎片内存

复制算法 Copying

为了解决效率问题,复制算法将可用内存分为大小相等的两块,每次只用其中一块。 
当一块内存用完了,就将还存活着的对象复制到另外一块上面,然后把已用过的内存空间一次清理掉。

内存分配不用考虑碎片等复杂情况,只要移动堆顶指针,实现简单,运行高效。只是这种算法的代价是将内存缩小为原来的一半。

** 
在实际应用中,不会简单的把所有存活对象从一边拷贝到另外一边,经过优化,会把大对象放到老生代的内存区域中,这一动作其实就是内存分配担保的一个动作(Handle Promotion)。 
我们没有办法保证每次需要复制的内存对象 在另一块区域一定够,这时需要借一块区域通常来说是老年代存放,在清理之后在放回去,或者说就长期放在老年代了。 
**

标记-压缩算法 Mark-Compact

标记-压缩算法是 标记-清除算法的一种优化,标记过程仍然一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。

分代收集 General Collection

所谓分代收集其实是各种回收算法的综合应用。根据对象的存活周期的不同将内存划分为几块。一般是把Java堆分为新生代和老年代。 
新生代中,每次垃圾收集时都要发现大批对象死去,只有少量存活,那就选用复制算法,只需付出少量存活对象的复制。 
而老年代中因为对象的存活率高,没有额外的空间对它进行分配担保,就必须使用标记清理或者压缩算法。