垃圾收集算法详解
说到垃圾收集器必须要提的就是垃圾收集算法,因为所有的垃圾收集器都是基于垃圾收集算法实现的,垃圾收集算法是垃圾收集器的方法论,了解了这些方法论,对垃圾收集器的工作原理也就清楚了。说到垃圾收集算法,那么必须得提分代收集理论,因为有了分代收集理论才有了垃圾收集算法。
点击查看分代收集理论的建立下面介绍下jvm中我们用到的垃圾收集算法
1.标记-清除算法
什么是标记-清除算法?
最早出现也是最基础的垃圾收集算法便是“标记-清除算法”该算法分为标记、清除两部分,回收对象时先标记待清除对象,标记完成后清除这些被标记的对象(也可以标记存活对象,清除未被标记的对象),标记的过程就是判定对象是否是垃圾的过程(使用可达性分析算法)而且后续的垃圾收集算法大多都是以“标记-清除算法”为基础进行改进的。
优点:
最基础简单的垃圾收集算法,为后续垃圾收集算法奠定了基础。
缺点:
①.执行效率不稳定,随着对象的不断增多,该算法的标记、清除的效率就会不断下降,造成执行效率不稳定,比如新生代中绝大部分对象都是朝生夕灭的,就不适合这种算法。②.内存空间碎片化,标记清除过后会产生大量不连续的内存碎片,空间碎片太多会导致再需要分配大对象时找不到合适的空间,从而提前或频繁出发GC。
应用:
“标记-清除算法”的实现最常用的就是CMS了,该垃圾收集器的收集区域是老年代(下一篇详细介绍这两种垃圾收集器)(下一篇分详细介绍这一种垃圾收集器)。
2.标记-复制算法
什么是标记-复制算法?
将内存划分为大小相等的两块,每次只使用其中一块,当第一块内存使用完了,就把这块内存上的对象复制到另一块未使用的内存上,然后清空第一块内存,这种回收对象的算法被称为“标记-复制算法”,该算法是基于“标记-清除算法”演变而来。
优点:
①解决了“标记-清除算法”的执行效率不稳定问题,该算法主要应用于新生代,新生代对象大部分都是朝生夕灭的,新生代被划分为Eden、From Survivor、To Survivor,每次使用Eden和一个Survivor用于新生对象的存储。 垃圾回收时就把Eden、From Survivor中存活的对象复制到另一个Survivor上,这样便不会有执行效率不稳定的情况,因为大部分对象都是朝生夕灭的(该优点是相对于新生代来说)。②解决了“标记-清除算法”的空间碎片化问题,因为是清空Eden From Survivor所以不会再有不连续的空间碎片。
缺点:
①“标记-复制算法”因为必须有一部分空间时刻空闲着,所以会有一定的空间浪费,②在极端情况下To Survivor区域不一定能存储的了新生代存活下来的对象,所以需要分配担保策略(对象进入老年代)。
应用:
“标记-复制算法”实现常用的有:Serial、ParNew、Parallel Scavenge。这三款垃圾收集器收集目标都是新生代(下一篇详细介绍这三种垃圾收集器)。
3.标记-整理算法
什么是标记-整理算法?
“标记-复制算法”有他的自身缺陷,这种场景很适合新生代,但对于大部分对象都是存活下来的老年代就不适用了,老年代中对象存活过多,复制动作的内存开销就会很大,所以针对老年代的特性,提出了“标记-整理算法”,标记动作与“标记-清除算法”、“标记-复制算法”中的标记一样都是使用的可达性分析算法对对象进行标记,整理部分则是将存活的对象向内存空间一端进行移动,然后直接清理掉边界以外的内存区域。
优点:
①解决了“标记-清除算法”的空间碎片化问题。②解决了“标记-复制算法”需要分配担保的问题。
缺点:
根据强分代假说“熬过越多次垃圾收集的对象,越难以被回收”,老年代中的大部分对象都是年龄达到了16的对象,都是很难被回收的,所以采用“标记-整理算法”去移动对象,对应用程序的吞吐量其实影响很大,但是不得不使用“标记-整理算法”,因为“标记-清除算法”会浪费一定空间,“标记-复制算法”又必须有分配担保策略也需要浪费空间,且“标记-复制算法”也无法满足老年代中所有对象都存活的极端情况。
应用:
“标记-整理算法”实现常用的有:Serial Old、Parallell Old。这两款垃圾收集器的收集目标都是老年代(下一篇详细介绍这两种垃圾收集器)。
4.对比标记-清除算法与标记-整理算法
为什么单独比较这两种算法呢,因为老年代中既有使用“标记-清除算法”实现的CMS垃圾收集器,也有使用“标记-整理算法”实现的Serial Old、Parallel Old等垃圾收集器,而“标记-复制算法”在典型情况下只用于新生代(G1是例外,这是一种区别于典型垃圾收集器的收集器),所以这里之比较这两种算法的优劣,“标记-清除算法”与“标记-整理算法”的根本区别在于在回收对象时,对象是否发生了移动,换种说法就是,“标记-清除算法”是一种非移动式的垃圾收集算法,而“标记-整理算法”是一种移动式的垃圾收集算法。在“标记-清除算法”中因为对象不移动,只是清除被标记的对象,这样就产生了很多的空间碎片,致使在新对象进来时会导致可能没有足够的空间进行存储新对象,从而提前或频繁触发GC,而且因为空间是碎片化的,对象的分配必须依赖“空闲列表”,每次对象进来要先通过空闲列表来查找堆中哪些区域是空闲且足够该对象的分配,无形中就增加了程序的时间成本,降低应用的吞吐量,所以CMS作为“标记-清除算法”实现的垃圾收集器更为关注的是STW状态的延时,而不是应用程序的吞吐量,总结一句话“标记-清除算法”在分配对象阶段更为复杂,“标记-整理算法”是移动式回收算法,在老年代中大部分对象都是存活的,因此在回收对象时,会伴随大量的对象移动,从而会导致对象回收阶段会占用相对多的时间,因此采用“标记-整理算法”实现的Parallel Old是更为关注吞吐量的一款垃圾收集器,总结一句话,“标记-整理算法”在回收对象阶段更为复杂。