垃圾收集器有两个重要的功能:第一,先识别和标记死亡对象;第二,使用合理的垃圾回收算法回收垃圾。那常见的垃圾回收算法有哪些呢?HotSpot 官方默认的虚拟机采用的有什么哪种垃圾回收算法呢?接下来我们一起来看。
常见的垃圾回收算法有以下 4 个:
- 标记-清除算法;
- 复制算法;
- 标记-整理算法;
- 分代算法。
1.标记-清除算法
标记-清除(Mark-Sweep)算法属于早期的垃圾回收算法,它是由标记阶段和清除阶段构成的。标记阶段会给所有的存活对象做上标记,而清除阶段会把没有被标记的死亡对象进行回收。 而标记的判断方法就是前面讲的引用计数算法和可达性分析算法。 标记-清除算法的执行流程如下图所示:
从上图可以看出,标记-清除算法有一个最大的问题就是会产生内存空间的碎片化问题,也就是说标记-清除算法执行完成之后会产生大量的不连续内存,这样当程序需要分配一个大对象时,因为没有足够的连续内存而导致需要提前触发一次垃圾回收动作。
优点:实现简单。
缺点:产生不连续的内存碎片,如果程序需要分配一个连续内存的大对象时,就需要提前触发一次垃圾回收。
2.复制算法
复制算法是将内存分为大小相同的两块区域,每次只使用其中的一块区域,这样在进行垃圾回收时就可以直接将存活的东西复制到新的内存上,然后再把另一块内存全部清理掉。 这样就不会产生内存碎片的问题了,其执行流程如下图所示:
从上图可以看出:使用复制算法是可以解决内存碎片的问题的,但同时也带来了新的问题。因为需要将内存分为大小相同的两块内存,那么内存的实际可用量其实只有原来的一半,这样此算法导致了内存的可用率大幅降低了。
优点:执行效率高,没有内存碎片的问题。
缺点:空间利用率低,因为复制算法每次只能使用一半的内存。
3.标记-整理算法
标记-整理算法是由两个阶段组成的:标记阶段和整理阶段。其中标记阶段和标记-清除算法的标记阶段一样,不同的是后面的一个阶段,标记-整理算法的后一个阶段不是直接对内存进行清除,而是把所有存活的对象移动到内存的一端,然后把另一端的所有死亡对象全部清除,执行流程图如下图所示:
优点:解决了内存碎片问题,比复制算法空间利用率高。
缺点:因为有局部对象移动,所以效率不是很高。
4.分代算法
分代算法并不能是某种具体的算法,而是一种策略,我们就姑且称它为分代算法吧。目前 HotSpot 虚拟机使用的就是此算法,在 HotSpot 虚拟机中将垃圾回收区域堆划分为两个模块:新生代和老生代,如下图所示:
为什么要将堆分为新生代和老生代呢? 因为对象分为两种,绝大多数对象都是朝生夕灭的,也就是用完一次之后就不用了,而剩下一小部分对象是要重复使用多次的,将不同的对象划分到不同的区域,不同的区域使用不同的算法进行垃圾回收,这样可以大大提高 Java 虚拟机的工作效率。
不同区域不同算法
在分代算法中对于不同区域采用的具体算法也是不同的,新生代存放的大部分数据是朝生夕灭的,所以新生代使用的是效率最高的复制算法;而老生代使用的是标记-清除或标记-整理算法,如果标记-清除可以满足需要那么就使用效率更好的标记-清除算法,如果标记-清除算法不能满足需要就使用标记-整理算法。
小结
标记-清除算法效率较高,但存在内存碎片;复制算法效率最高,也没有内存碎片,但内存利用率不高,而标记-整理算法效率不算高,但不存在内存碎片,并且不存在内存利用率的问题;而 HotSpot 在 JDK 8 之前使用的是分代算法(分代策略),将垃圾回收区域分为新生代和老生代,新生代采用复制算法,老生代采用标记-清除或标记-整理算法。
特殊说明
以上内容来自我的《Java 面试突击训练营》,这门课程是有着十几年工作经验(前 360 开发工程师),10 年面试官经验的我,花费 4 年时间打磨完成的一门视频面试课。学完训练营的课程之后,基本可以应对目前市面上绝大部分公司的面试了,并且课程配备了 9 大就业服务,帮助上千人找到 Java 工作,其中上百人拿到大厂 Offer,学员最高薪资 70W 年薪,面试课目录和 9 大服务如下:
加我微信咨询:vipStone【备注:训练营】