2019独角兽企业重金招聘Python工程师标准>>>
作为学习笔记,记录一下JVM常见的几种垃圾收集算法。
1. 复制算法(Copying)
- 思路
将可用的内存划分为大小相等的两块,每次只使用其中的一块,当这一块内存使用完了,就将存活的对象复制到另一块,然后整个清理已使用过的那一块内存。如下图所示: - 不足
完全浪费了一半的内存空间! - 改进
现代商业虚拟机都用复制算法来回收新生代。
由于新生代中98%的对象都是“朝生夕死”的,所以可以将内存划分为一块较大的Eden空间和两块较小的Survivor空间。
每次使用Eden和其中一块Survivor空间来存储,当这两块空间存储满时,就将活着的对象一次性复制到另外一块Survivor中,最后清理掉之前的Eden和Survivor空间。
HotSpot虚拟机默认Eden和Survivor的大小比例是8:1。
如果遇到另一块Survivor空间内存不足时(不足以存放上一次新生代收集下的存活对象),这时,就通过分配担保机制使对象直接进入老年代。
2. 标记 - 清除算法(Mark-Sweep)
- 思路
首先标记出所有要回收的对象,在标记完成后,统一回收所有被标记的对象。 - 不足
效率不高。
会产生大量的内存碎片,浪费空间。
3. 标记 - 整理算法(Mark-Compact)
对第二个标记-清除算法的改进。
- 思路
首先标记所有要回收的对象,让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。 - 不足
速度较慢。
4. 分代收集算法(Generational Collection)
新生代采用“复制算法”,老年代采用“标记-清除”或“标记-整理”算法。
彩蛋
- 并发垃圾收集器(CMS)为什么没有采用标记-整理算法来实现?
- HotSpot的算法实现
枚举根节点 - 通过OopMap提前存储对象的偏移量、哪些位置是引用等信息。
安全点 - “是否具有让程序长时间执行的特征”。如:方法调用、循环跳转、异常跳转等,OopMap只记录安全点。采用“主动式中断”,使线程跑到安全点。
安全区域 - 一段代码片段中,引用关系不会发生变化。
注:以上三个图片不是小王绘制,是从另一篇博客(关于JVM的几个垃圾收集算法思想)中拷贝过来的。