如何判断一个对象是否存活(即是否还分配在堆中),那看他是否还在用。
1. 引用计数算法
这是一种判断方式,相应的方法就是:如果一个对象被引用,那将被引用的对象中的一个计数器加一,引用失效就减一。在任何时刻引用计数器为0那就是没有应用的话,那该对象就可被回收了。
存在着的缺陷:当两个对象相互引用的时候,就会造成一种循环,引用计数器无法判断他们是否还有用。
Java虚拟机不用这种。
2. 可达性分析算法
通过“GC Roots”的跟对象作为起始节点集,从这些结点开始,根据应用关系向下搜索,搜索过程中走过的路径叫做引用链,如果某个对象(或某个对象链)不在引用链上,会被标记为可回收对象(关于是否可以真正回收可以查看3.2.4)。
被固定可作为GCRoot对象有以下几种:
-
虚拟机栈中引用的对象
-
静态属性引用对象
-
常量引用对象
-
本地方发展中Native方法引用的对象
-
一些常驻的异常对象
-
被同步锁持有的对象
-
反应Java虚拟机内部信息的对象(一些工具)
3. 再谈引用
无论是引用计数算法还是可达性分析算法判断对象是否可以被回收,我们都离不开引用的关系。
JDK1.2版本:强引用、软引用、弱引用、虚引用
强引用:是最传统的“引用”的定义,是指在程序代码之中普遍存在的引用赋值,即类似“Object obj=new Object()”这种引用关系。无论任何情况下,只要强引用关系还存在,垃圾收集器就永远不会回收掉被引用的对象。
软引用:是用来描述一些还有用,但非必须的对象。只被软引用关联着的对象,在系统将要发生内存溢出异常前,会把这些对象列进回收范围之中进行第二次回收,如果这次回收还没有足够的内存,才会抛出内存溢出异常。在JDK 1.2版之后提供了SoftReference类来实现软引用。
弱引用:也是用来描述那些非必须对象,但是它的强度比软引用更弱一些,被弱引用关联的对象只能生存到下一次垃圾收集发生为止。当垃圾收集器开始工作,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。在JDK 1.2版之后提供了WeakReference类来实现弱引用。
虚引用:也称为“幽灵引用”或者“幻影引用”,它是最弱的一种引用关系。一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得--一个对象实例。为-一个对象设置虚引用关联的唯一目的只是为了能在这个对象被收集器凹收时收到一个系统通知。在JDK1.2版之后提供了PhantomReference类来实现虚引用。
4. 生存还是死亡?
可达性算法补充。
可达性算法判定为不可达对象后,这个对象还不是“非死不可”,这个对象要真正死亡,还需要至少经历两次标记过程。
第一次标记:可达性分析算法发现没有和GC Root相连,会被第一次标记。
中间流程:如果对象没有覆盖finalize()方法,或者finalize()方法已经被虚拟机调用了,那么虚拟机就会直接调用(finalize方法只能被执行一次)。
第二次标记:如果该对象finalize方法判定为有必要执行,那就会把该对象放到F-Queue队列中。finalize方法是对象逃脱死亡命运的最后一次机会,如果这时候该对象和GC Root上的引用链连接上了,那么就可以逃脱被回收的命运,不然就会被回收。
5. 回收方法区
方法区的垃圾收集主要回收两部分内容:废弃的常量和不再使用的类型。
判断常量是否可以被回收:
-
没有任何字符串对象引用该常量,且虚拟机中也没有其他地方引用这个字面量。
判定一个类型是否属于“不再被使用的类”:
-
该类所有的实例都已被回收,也就是Java堆中不存在该类及其任何派生子类的实例。
-
加载该类的类加载器已经被回收,这个条件除非是经过精心设计的可替换类加载器的场景,否则通常很难达成的。
-
该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类方法。