面试题之GC垃圾回收算法
一、简介
今天我们总结一道常见的关于垃圾回收的面试题:
- JVM垃圾回收算法有哪几种?各自的优缺点?
我们都知道,JVM中堆可分为:
【a】新生代:
- Eden区域
- From(s0)区域
- To(s1)区域
【b】老年代
如下图:
JVM垃圾回收的主要区域之一就是我们的堆。JVM在进行GC时,并非每次都对上面三个内存区域一起回收的,大部分时候回收的都是指新生代。
GC按照回收的区域又分了两种类型:
- 普通GC(minor GC)
- 全局GC(major GC or Full GC)
Minor GC和Full GC的区别
- 普通GC(minor GC):只针对新生代区域的GC,指发生在新生代的垃圾收集动作,因为大多数Java对象存活率都不高,所以Minor GC非常频繁,一般回收速度也比较快。
- 全局GC(major GC or Full GC):指发生在老年代的垃圾收集动作,出现了Major GC,经常会伴随至少一次的Minor GC(但并不是绝对的),Major GC的速度一般要比Minor GC慢上10倍以上 。
二、GC垃圾回收算法
【a】复制算法
复制算法原理:
Minor GC会把Eden中的所有活的对象都移到Survivor区域中,如果Survivor区中放不下,那么剩下的活的对象就被移到老年代中,也即一旦收集后,Eden是就变成空的了。
当对象在 Eden ( 包括一个 Survivor 区域,这里假设是 from 区域 ) 出生后,在经过一次 Minor GC 后,如果对象还存活,并且能够被另外一块 Survivor 区域所容纳( 上面已经假设为 from 区域,这里应为 to 区域,即 to 区域有足够的内存空间来存储 Eden 和 from 区域中存活的对象 ),则使用复制算法将这些仍然还存活的对象复制到另外一块 Survivor 区域 ( 即 to 区域 ) 中,然后清理所使用过的 Eden 以及 Survivor 区域 ( 即 from 区域 ),并且将这些对象的年龄设置为1,以后对象在 Survivor 区每熬过一次 Minor GC,就将对象的年龄 + 1,当对象的年龄达到某个值时 ( 默认是 15 岁,通过-XX:MaxTenuringThreshold 来设定参数),这些对象就会成为老年代。
- 优点:
- 效率高
- 缺点:
- 1、使用复制算法浪费了一半的内存
- 2、如果对象的存活率很高,那么我们需要将很多对象都复制一遍,并将所有引用地址重置一遍,复制所花费的时间,在对象存活率达到一定程度时,将会变的不可忽视。 所以使用复制算法,对象的存活率要非常低才行。
【b】标记清除算法
原理:标记清除算法分为标记和清除两个阶段,先标记出要回收的对象,然后统一回收这些对象。如下图描述:
用通俗的话解释一下标记清除算法,就是当程序运行期间,若可以使用的内存被耗尽的时候,GC线程就会被触发并将程序暂停,随后将要回收的对象标记一遍,最终统一回收这些对象,完成标记清理工作接下来便让应用程序恢复运行。
- 优点:
- 标记- 清除算法比较简单,也是最基础的算法。
- 缺点:
- 需要暂停整个应用,会产生内存碎片
- 标记-清除算法效率并不高,如果有大量对象需要被回收,也就是需要对大量对象进行标记,显然标记这些对象耗费的时间将会比较长,所以标记的效率并不高
【c】标记压缩算法
原理:在整理压缩阶段,不再对标记的对像做回收,而是通过所有存活对像都向一端移动,然后直接清除边界以外的内存。可以看到,标记的存活对象将会被整理,按照内存地址依次排列,而未被标记的内存会被清理掉。如此一来,当我们需要给新对象分配内存时,JVM只需要持有一个内存的起始地址即可,这比维护一个空闲列表显然少了许多开销。
- 优点:
- 弥补标记/清除算法当中,产生大量内存碎片的缺点;
- 消除复制算法当中,内存减半的高额代价;
- 缺点:
- 效率不是很好
【d】分代收集算法
现代商用虚拟机基本都采用分代收集算法来进行垃圾回收。分代收集算法的思想,就是根据对象的不同生命周期,采用不同的垃圾回收算法,根据每一代的特点,选择最适合该代的回收算法,达到最高的回收效率。对象存活率比较低(新生代),使用复制算法,复制成本低;对象存活率高(老年代),采用标记-清理算法或者标记-整理算法。
三、总结
以上就是关于GC垃圾回收算法的一些总结,如果面试遇到相关问题,可以从垃圾回收的原理,然后讲述大体的流程,再说上优点、缺点,这道题应该就差不多了。更多关于GC相关的知识可以参考我的另外一篇文章: