标记-清理(mark-sweep)、标记-复制(mark-copy)、标记-整理(mark-compact) 是 JVM 最常用的三种基本垃圾回收策略。大多数的垃圾回收器都会以不同的方式对这些基本回收策略进行组合,比如在新生代使用标记-复制算法,而在老年代使用标记-整理算法。
接下来的几篇文章会对这几种基本的垃圾回收算法做详细的介绍。
什么是标记-清理算法
标记-清理算法是实现 Tracing GC 语义最直接也最简单的算法。使用标记-清理算法的回收器在回收过程中分为两个阶段:
•追踪(Trace)阶段:回收器从 GC Roots(寄存器、栈、全局变量) 开始遍历对象图,并标记所遇到的每个对象;•清理(Sweep)阶段:回收器检查堆中的每一个对象,并将所有未被标记的对象当作垃圾进行回收。
标记-清理算法是一种间接回收算法,它并非直接检测垃圾本身,而是先确定所有存活对象,然后反过来判断其他对象都是垃圾。
在开始具体的算法内容之前,先明确几个基本概念:
•Mutator:应用的执行者,可以简单的理解为“应用程序”,GC就是在 Mutator 的内部工作。Mutator 主要进行两种操作:创建对象和更新对象间的引用关系。与此同时,也会为用户做些额外的工作,比如数值计算、浏览网页等,在很多地方也被简单地称为赋值器;•Collector:垃圾回收器,有时候也被直接称为回收器;•Allocator:分配器,当 Mutator 需要创建新对象时,就会向 Allocator 申请一个大小合适的空间,Allocator 就会在堆中的可用空间中寻找满足要求的空间,返回给 Mutator。
我们可以用一句话来简单描述这三者是如何协同工作的:当 Mutator 需要创建对象时,就会向 Allocator 申请空间,如果没有足够的内存空间,则唤起 Collector,等待 Collector 完成相关工作后,再次尝试分配内存,如果仍没有足够内存,则说明堆内存耗尽整个流程可以用下面的代码来描述。
New() {
// 赋值器尝试分配内存 object = allocate(); // 如果没有足够内存空间,分配内存失败 if (object == null) {
// 唤起回收器,执行垃圾回收 collect(); // 再次尝试分配内存 object = allocate(); // 如果还是分配失败,则说明堆内存耗尽,抛出错误 if (object == null) {
throw Out of memeory error; } } return object;}
如果使用标记-清理算法的垃