基本的垃圾回收算法
判断对象是否可被回收
- 引用计数法,被零引用的对象可回收。但是很难解决相互引用的问题
- 从 gc root 开始搜索,搜索不到的对象可回收。Java 的垃圾回收用的是这种方法。
简要对比三种基本算法
| mark-sweep | mark-compact | copying
--- | :---: | :---: | :---:
速度 | 中等 | 最慢 | 最快
空间开销 | 少(但会堆积碎片) | 少(不堆积碎片) | 通常需要活对象的 2 倍大小(不堆积碎片)
时间开销
mark-sweep: mark 阶段与活对象的数量成正比,sweep 阶段与整堆大小成正比
mark-compact: mark 阶段与活对象的数量成正比,compact 阶段与活对象的大小成正比
copying: 与活对象大小成正比
在分代式假设中,年轻代中的对象在 minor GC 时的存活率应该很低,这样用 copying 算法就是最合算的,因为其时间开销与活对象的大小成正比,如果没多少活对象,它就非常快;而且 young gen 本身应该比较小,就算需要 2 倍空间也只会浪费不太多的空间。
而年老代被 GC 时对象存活率可能会很高,而且假定可用剩余空间不太多,这样 copying 算法就不太合适,于是更可能选用另两种算法,特别是不用移动对象的 mark-sweep 算法。
JVM 内存模型
Java 8 已经没有永久代了。
eden 满的时候触发 minor gc,升到老年代的对象大于老年代的剩余空间时触发 full gc,或者小于时被HandlePromotionFailure 参数强制 full gc。gc 与非 gc 时间耗时超过了 GCTimeRatio 的限制引发 OOM,调优诸如通过 NewRatio 控制新生代老年代比例,通过 MaxTenuringThreshold 控制进入老年代前生存次数等。
垃圾回收器
According to JDK 7, there are 5 GC types:
- Serial GC
- Parallel GC
- Parallel Old GC (Parallel Compacting GC)
- Concurrent Mark & Sweep GC (or "CMS")
- Garbage First (G1) GC
CMS 的 Marking 阶段可细分为 Initial Marking、Concurrent Marking、Remarking。
假设有对象 b
class B {
A a;
...
}
Field a 在被标记(Concurrent Marking)时是指向对象 a1,在并发标记完成前,Field a 转而指向 a2。我们要保证 a2不会被回收(而 a1 可以被错误保留,等待下次 gc)。这就需要再次标记(Remarking)来恢复 a2。如何来做 Remarking,简单地说,就是在 Concurrent Marking 阶段,记下所有改动的 reference,在 Remarking 阶段再 trace 一遍从变动过的 reference 开始的局部 object graph。
参考资料