本文基于周志明的《深入理解java虚拟机 JVM高级特性与最佳实践》所写。特此推荐。
对象存活判定算法
引用计数算法
给对象添加一个引用计数器,当有其他对象引用它时,计数器加1;当引用失效时,计数器减1。任何时刻计数器为0的对象就是不可能在被使用的。引用计数算法实现简单,判定效率也很高,但是很难解决对象间相互循环引用的问题。可达性分析算法(主流实现)
通过一系列被称为“GC Roots”的对象作为起点,从这些节点向下搜索,搜索所走过的路径被称为引用链,当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的。
如图所示,虽然object5、object6、object7虽然互相有关联,但是它们到GC Roots是不可达的,所以它们将会被判定为是可回收的对象。可作为CG Roots的对象包括下面几种:
- 虚拟机栈(栈帧中的本地变量表)中引用的对象。
- 方法区中类静态属性引用的对象。
- 方法区中常量引用的对象
- 本地方法栈中JNI(即一般说的Native方法)引用的对象。
引用
无论是通过引用计数算法判断对象的引用数量,还是可达性分析算法判断对象的引用链是否可达,判定对象是否存活都与“引用”有关。引用可以分为强引用、软引用、弱引用和虚引用4种:
- 强引用就是指在程序中普遍存在的,类似“Object obj = new Object()”这类的引用,只要强引用还在,垃圾收集器就永远不会回收掉被引用的对象。
- 软引用是用来描述 一些还有用但非必需的对象。对于软引用关联着的对象,在系统将要发生内存溢出异常之前,将会把这些对象列进回收范围之中进行第二次回收。如果这次回收还是没有足够的内存,才会抛出内存溢出异常。用SoftReference类来实现。
- 弱引用也是用来描述非必需对象的,但是它的强度比软引用还要弱一些,被弱引用关联的对象只能生存到下一次垃圾收集发生之前。当垃圾收集器开始工作时,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。用WeakReference类来实现。
- 虚引用也被称为幽灵引用或幻影引用,它是最弱的引用关系。一个对象是否有虚引用完成不会影响其生存周期,也无法通过虚引用来获取一个对象实例。设置虚引用的目的是在这个对象被收集器回收时会收到一个系统通知。用PhantomReference类来实现。
finalize()方法最终判定对象是否存活
即使在可达性分析算法中不可达的对象,也并非是“非死不可”的,这时候它们暂时处于“缓刑”阶段,要真正宣告一个对象死亡,至少要经历再经历2次标记过程:
如果对象在可达性分析中没有与GC Roots的引用链,那么此时就会被第一次标记并且进行一次筛选,筛选的条件是是否有必要执行finalize()方法。当对象没有覆盖finalize()方法或者已经被虚拟机调用过,那么就认为是没必要的。
方法区回收
方法区主要回收两部分:
- 废弃常量:常量池中的常量如果被没有任何一个对象引用,那么这个常量就叫做废弃常量。
- 无用类:同时满足以下三个条件的类才会被称为无用类,被垃圾收集器回收:
- 该类的所有实例都已被回收,也就是说Java堆中不存在该类的任何实例。
- 加载该类的ClassLoader已经被回收。
- 该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射机制访问该类的方法。
虚拟机可以对满足以上三个条件的无用类进行回收,这里说的仅仅是“可以”,而不是和对象一样,不使用了就必然被垃圾收集器所回收。
Hotspot提供了-Xnoclassgc参数进行控制。