目录
1、前言
2、标记-清除算法
3、标记-整理算法
4、标记-复制算法
5、总结
1、前言
说起垃圾回收(Garbage Collection)(本文简称GC)。相信同时对C++和Java有了解的小伙伴都知道,C++在new完对象后,是需要手动delete对象来释放内存的,而Java则不需要。虽然我们Java程序员不需要去手动的释放内存,但是了解JVM如何处理这一过程对我们来说也是很有必要的。
JVM对于垃圾回收这一篇幅其实是很长的,既然要对垃圾进行回收,那么不可避免的要思考三个问题:①哪些需要回收、②什么时候回收、③如何回收。
本文主要先讲解三个经典的垃圾回收算法,后面笔者会陆续更新其他的内容(比如什么是安全点,什么是安全区,经典垃圾回收器等)。
2、标记-清除算法
如上图所示,标记-清除算法就是每次进行垃圾回收时,标记出可以回收的对象,然后直接将这些对象回收(即释放对应区域的内存)即可。
标记-清除算法优点显而易见:那就是简单、高效(对于清除这一步骤来说)。因为我们只需要将标记出的对象直接清除便可,因此效率很高。
但是缺点相信学过操作系统的小伙伴肯定一眼就看出来了:久而久之会存在大量内存碎片,导致大对象无法分配到所需的内存空间。
3、标记-整理算法
如上图所示,标记-整理算法就是每次进行垃圾回收时,标记出可以回收的对象,然后将这些对象回收后,将存活的对象整理到连续的内存空间去。
标记-整理算法实际上就是标记-清除算法的“改进版”,我们上述说到标记-清除算法的缺点就是会存在大量内存碎片,导致大对象无法分配到所需的内存空间。标记-整理算法在标记-清除算法的基础上,加了一个将存活对象移动到连续的内存空间这一步骤。
标记-整理算法的优点肯定大家也想到了——内存利用的更加充分,不会存在内存碎片,导致大对象分配不到所需的内存空间。
但是缺点想必大家也看出来了——回收时,每次都要将对象移动到一起,性能肯定会受到一定的影响。特别是当大部分对象都要回收时,只有少部分离散的对象存活,这个时候性能被影响的会更加明显。
4、标记-复制算法
如上图所示,标记-复制算法就是将内存区域分为两部分(这里我们称为回收区和保留区),垃圾回收时,我们每次标记出需要回收的对象,然后将不需要回收的对象复制到“保留区”后,将存活区的所有对象一起清除(因为我们在保留区保留了不需要回收的对象,所以回收区都可以回收了)。然后原回收区就空了,变成保留区,原保留区有对象,此时变成回收区。
标记-复制算法相对上述标记-整理算法来说,对于大部分对象都要回收时,只有少部分离散的对象存活这个场景来说,更加适用、高效,解决了这个场景下标记-整理算法的不足。
缺点也很明显,那就是我们每次都有一部分内存区域用不上(因为要拿一部分来当保留区)。
5、总结
本文只是比较快速的介绍了一下三个经典的垃圾回收算法,同时也说明了他们的优缺点。这里我们给大家一些总结:
- 对于追求垃圾回收延迟低(即垃圾回收这一过程快)的场景下,推荐使用“标记-清除算法”。
- 对于大部分对象都要回收时,只有少部分离散的对象存活这个场景下,我们推荐使用“标记-复制算法”。(在后面我们讲到分代思想时,新生代大部分都需要回收,因此是使用这个算法。)
- 对于少部分对象要回收,大部分对象都存活的场景下,我们推荐使用“标记-整理算法”(在后面我们讲到分代思想时,老年代大部分都不需要回收,因此是使用这个算法。)