垃圾收集器在对堆进行回收前,需要先判断堆中哪些对象是“活着”的,哪些对象是“死了”的。
1.引用计数法
给对象添加一个引用计数器,每当有一个地方引用它时,计数器加1,当引用失效时,计数器减1。任何时刻计数器为0的对象就是不可能再被使用的。
引用计数器实现简单,效率高。但是主流的JVM并没有使用引用计数法来管理内存,主要原因是它难以解决对象之间循环引用的问题。例如,对象objA和对象objB都有字段instance,令objA.instance=objB,objB.instance=objA,除此之外这两个对象再无任何引用,实际上这两个对象再无可能被访问,但是因为两个对象之间互相引用,两者的引用计数器都不为0,引用计数法无法回收这两个对象。
2.可达性分析算法
JVM的主流实现是通过可达性分析来判断对象是否存活的。这个算法的基本思路是通过一系列的称为“GC Roots”的对象作为起点,从这些起点开始向下搜索,搜索所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链时,证明此对象是不可用的,将会成为可回收的对象。
Java中,可作为GC Roots的对象包括:
- 虚拟机栈中引用的对象
- 方法区中类静态属性引用的对象
- 方法区中常量引用的对象
- Native方法引用的对象
3.引用
引用可以分为强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Weak Reference)和虚引用(Phantom Reference)四种。
- 强引用是指代码中普遍存在的,类似“Object obj=new Object()”这类的引用,只要强引用还存在,被引用的对象就永远不会被垃圾收集器回收。
- 软引用用来描述一些还有用但非必须的对象。如果内存空间足够,垃圾回收器就不会回收它,如果内存空间不足了,就会回收这些对象的内存。软引用可用来实现内存敏感的高速缓存。软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收,JAVA虚拟机就会把这个软引用加入到与之关联的引用队列中。
// prime holds a strong reference Counter counter = new Counter(); // soft reference variable has SoftReference to Counter Object prime SoftReference soft = new SoftReference(counter) ; // now Counter object is eligible for garbage collection // but only be collected when JVM absolutely needs memory prime = null;
- 弱引用也是用来描述一些非必须的对象,但是引用强度比软引用还要弱,被弱引用关联的对象只能存活到下一次垃圾收集之前。一个使用弱引用的例子是WeakHashMap,它是除HashMap和TreeMap之外,Map接口的另一种实现。WeakHashMap有一个特点:map中的键值(keys)都被封装成弱引用,也就是说一旦强引用被删除,WeakHashMap内部的弱引用就无法阻止该对象被垃圾回收器回收。
Counter counter = new Counter(); // weak reference WeakReference weak = new WeakReference(counter); // now Counter object is eligible for garbage collection counter = null;
- 虚引用的对象可以在任何时候被垃圾回收器回收。
4.生存还是死亡
一个对象真正死亡,至少要经历两次标记过程。如果对象在可达性分析后发现没有与GC Roots相连接的引用链,那它将会被第一次标记并且进行一次筛选,筛选条件是该对象是否有必要执行finalize()方法。当对象没有覆盖finalize()方法或者finalize()方法已经被虚拟机执行过,则认为没有必要执行。如果对象有必要执行finalize()方法,那么该对象会被放置到一个叫F-Queue的队列中,并稍后由一个JVM自动建立的、低优先级的Finalizer线程去执行。这里的“执行”指虚拟机有机会促发这个方法,但不承诺会等待其运行结束。finalize()方法是对象逃脱死亡的最后一次机会,稍后GC将对F-Queue中的对象进行第二次标记,如果对象在finalize()中将自己重新与引用链上的任意对象建立关联(例如将自己this赋值给某个类变量或对象的成员变量),那么第二次标记是将其移出需要回收的集合,否则该对象就真的会被回收了。
5.回收方法区