JVM垃圾收集(二)—— 垃圾收集算法

上一节我们主要讲了如何判断一个对象是否是垃圾,当我们识别出需要清理的垃圾之后,下一步该做什么呢?就是如何对垃圾进行回收,这就是我们这一节主要讲的内容——垃圾收集算法。

与其说我们这一节讲四种算法,我更喜欢说成是三种算法(复制算法、标记-清除算法、整理算法)和一种思想(分代算法),因为分代算法是可以结合其他算法去应用的。

 

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

标记-清除算法时最基础的算法,从名字我们就可以大致猜测出它的基本流程,算法分为“标记”和“清除”两个阶段:

  • 标记阶段。首先标记出所有需要回收的对象,需要回收的对象判定就是利用上一节提到的算法来判断。
  • 回收阶段。等全部标记完成后对所标记对象进行内存回收。

整个流程很容易理解和记忆,但是他的不足也非常明显,主要体现在两个方面:

  • 效率问题。标记和清除两个过程的效率都不算高。
  • 空间问题。标记清楚之后会产生大量不连续的内存碎片,内存碎片过多可能会导致接下来程序运行的过程中需要分配较大对象的时候,没有办法找到足够的连续内存,从而不得不提前触发另一次的垃圾回收。
JVM垃圾收集(二)—— 垃圾收集算法_第1张图片 “标记-清除”算法示意图

这个算法虽然简单,但是为后面的算法提供了思想基础,后面的算法针对他的不足进行了相应的改进。

 

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

针对“标记-清除”算法的空间不连续问题,有人提出了“标记-整理”算法。算法第一阶段仍与“标记-清除”算法一样,对回收对象进行标记,但是第二阶段不直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后清理掉端边界以外的内存。如图所示:

JVM垃圾收集(二)—— 垃圾收集算法_第2张图片 “标记-整理”算法示意图

 

复制算法(Copying)

为了解决“标记-清除”算法的效率问题,一种叫做“复制”算法的收集算法出现了。算法将可用的内存分为大小相等的两块,每次只使用其中的一块。当需要内存回收的时候,就将还存活的对象复制到另一块上面,然后把已经使用过的这一块内存一次清理掉。这样每次只使用一半的内存,就不用再担心内存碎片的问题,因为复制过去的时候是按顺序分配内存的,实现简单,运行的效率也很高。但是同时付出的代价也显而易见:将内存的容量缩小为原来的一半。复制算法的执行过程如图所示:

JVM垃圾收集(二)—— 垃圾收集算法_第3张图片 复制算法示意图

 

对比与总结:

复制算法的缺点有两个,一个是浪费了一半的空间,另外一个就是当对象的存活率很高的时候,就需要进行较多的复制操作,这时的算法效率就会降低;此时对比“标记-整理”算法,不但没有浪费一半的空间,并且在对象存活率高的时候效率和复制算法接近。所以在选用算法时,如果对象存活率低,则复制算法更加优秀;如果对象存活率高,那么“标记-整理”算法更为合适。(这也是后边所说的新生代和老年代使用算法不同的依据)

 

分代算法

前边已经讲完了三种算法,现在要讲的分代算法就是我前边提到的一种“思想”。算法也是要将内存划分为几块,但是注意这里和复制算法的思想是不一样的;不同的存活周期的对象被划分在不同的内存中。一般是把Java堆分为新生代和老年代,这样就可以根据各个年代的特别采取最合适的收集算法。新生代一般用来存储刚刚分配的对象,这种对象有一个特点就是存活期很短,每次垃圾收集的时候都会有大批的对象死去,根据“对比与总结”中所说,这种情况使用复制算法比较合适。而老年代一般是存放长期未被回收的对象,这个区域的对象的特点是存活率高,所以使用“标记-整理”或者“标记-清除”算法更好一些。

 

结合HotSpot虚拟机的实例分析

根据IBM公司的专门研究表明,新生代中的对象98%是“朝生夕死”的,所以对于使用复制算法的情况并不需要按照1:1的比例来对内存空间进行划分,而是将内存分为一块较大的Eden空间和两块较小的Survivor空间,每次只需要使用Eden和一块Survivor。当内存回收发生时,将Eden和一块Survivor中还存活的对象一次性复制到另一块Survivor空间上,最后清理掉Eden和使用过的Survivor空间。

HotSpot虚拟机默认Eden和Survivor的大小比例是8:1,也就是每次新生代中可用内存空间占整个新生代容量的90%(80% +10%),只有10%的内存会被“浪费”。当然98%的新生代对象“朝生夕死”只是一个平均数据,我们无法保证不会发生比较坏的情况,比如回收完成之后,存活对象大小占新生代容量的10%以上时,就需要依赖其他内存(如老年代)进行内存分配。

 

结语

关于垃圾收集算法的内容大概就是这些了,单纯从算法角度看,每种算法都或多或少有一些缺点,这就需要我们从其他的角度去优化垃圾回收过程。在具体的垃圾回收器中理解垃圾回收算法;在内存分配和回收策略中认识如何提高回收算法的效率将是我们接下来的重点。

 


 

如有错误,欢迎指摘。

你可能感兴趣的:(Think,in,Java)