根据Java内存运行时区域的划分,我们知道程序计数器、虚拟机栈和本地方法栈随着线程生成和回收,所以垃圾回收不需要考虑这部分,主要考虑java堆。

如何确定对象已死?

  • 引用计数法

    给每个对象添加一个引用计数器,任何时刻计数器为0的对象就是不可能再被使用的。

    缺点:无法解决循环引用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    public class ReferenceCountingGC{
    public Object instance = null;
    private static final int _1MB = 1024 * 1024;
    /**
    * 这个成员属性唯一意义就是占点内存,以便能在GC日志中看清楚是否被回收过
    */
    private byte[] bigSize = new byte[2 * _1MB]

    public static void testGC(){
    ReferenceCountingGC objA = new ReferenceCountingGC();
    ReferenceCountingGC objB = new ReferenceCountingGC();
    objA.instance = objB;
    objB.instance = objA;

    objA = null;
    objB = null;

    //假设在这行发生GC,objA和objB能否被回收?
    System.gc();
    }
    }
  • 可达性分析

    通过一系列成为“GC root”的对象作为起始点,从这些节点乡下搜索所走过的路径称为引用链,当一个对象到GC roots 没有任何引用链,则证明不可用。

    可以当做GC Roots的有:

    • 虚拟机栈(栈帧中的本地变量表)中引用的对象
    • 方法区中静态属性引用的对象
    • 方法区中常量引用的对象
    • 本地方法栈中JNI(即一般说的Native方法)引用的对象

引用

  • Strong Reference 保证不会被回收
  • Soft Reference 内存溢出之前会被回收
  • Weak Reference 关联的对象只能存活到下一次垃圾回收之前
  • Phantom Reference 和对象生存完全无关,只是在垃圾回收时收到系统通知
  • 依次减弱

finalize()方法

final 是一个修饰符,修饰的变量表示不可更改,修饰类表示不可被继承(所以不可能存在同时被final和abstract修饰的类),修饰的方法表示不可被重载。

finally 异常处理时提供 finally 块来执行任何清除操作。如果抛出一个异常,那么相匹配的 catch 子句就会执行,然后控制就会进入 finally 块(如果有的话,永远都被被执行)。

finalize() Java 技术允许使用 finalize() 方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。这个方法是由垃圾收集器在确定这个对象没有被引用时对这个对象调用的。它是在 Object 类中定义的,因此所有的类都继承了它。子类覆盖 finalize() 方法以整理系统资源或者执行其他清理工作。finalize() 方法是在垃圾收集器删除对象之前对这个对象调用的。

任何一个对象的finalize()方法只会被系统自动调用一次,对象可以通过覆盖finalize()方法使得避免被垃圾回收。

方法区的回收

不强制要求虚拟机在方法区实现回收

永久代的垃圾回收主要两部分:废弃常量和无用的类。

无用的类:

  • 该类所有实例已经被回收
  • 加载该类的ClassLoader已经被回收
  • 该类对用的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。

HotSpot算法实现

  • 枚举根节点
  • 安全点
  • 安全区域

垃圾收集器

  • Serial
  • ParNew
  • Parallel Scavenge
  • Serial old
  • Parallel old
  • CMS
  • G1