识别垃圾
引用计数
简单来说就是对象被引用一次就在对象头的引用次数上加1,当对象的引用次数为零时就说明对象可以被回收了。
但是这种方法容易出现循环引用问题,就是两个对象互相引用,但不存在外部引用,按理来说这个对象已经可以被回收了,但是因为他们之间还存在引用,所以引用数不会清零导致无法回收。
可达性分析
以一系列作为GC Roots的对象为起点,如果向下搜索可以搜到的对象为可用对象,搜不到的对象就可以被回收了
垃圾回收算法
标记-清除
可达性分析并标记不需要回收的对象,在标记完成后统一回收掉所有没有被标记的对象。
这种方式会产生大量不连续的空间碎片。
复制
把堆分成两部分,然后把其中一部分所有存活的对象复制到另一部分,在把前者的所有对象都清除掉。
这样存活的对象都排列紧密,但是缺点就是要多使用一倍的内存来清理垃圾。
标记-整理
先和标记-清除算法一样标记不需要回收的对象,然后将存活对象都往一边移动,再清理掉另一端所有的区域。
因为要频繁移动标记对象所以效率很低。
分代收集
根据对象存活周期的不同将堆分成新生代和老生代(Java8以前还有个永久代),默认比例为 1 : 2,新生代又分为 Eden 区, from Survivor 区(简称S0),to Survivor 区(简称 S1),三者的比例为 8: 1 : 1,这样就可以根据新老生代的特点选择最合适的垃圾回收算法,我们把新生代发生的 GC 称为 Young GC(也叫 Minor GC),老年代发生的 GC 称为 Old GC(也称为 Full GC)。
- Eden 区快要满时会触发Minor GC,因为 Eden 区的大部分对象都会被回收,所以Minor GC采用复制算法把存活的对象移动到S0区,同时清空Eden 区
- 下一次Minor GC会将Eden 区和S0区的存活对象都移动到S1区,然后清空前两个区,后面Minor GC中S0和S1角色互换继续重复步骤。
- 当对象年龄达到阈值,或者对象太大S0和S1存不下,或S0(或S1) 区相同年龄的对象大小之和大于 S0(或S1)空间一半以上,对象就会移动至老年代。