概述:
判断对象是否存活(或是否死去):可达性分析算法
概念介绍:引用、两次标记
垃圾收集器在对堆进行回收前,第一件事情就是要确定这些对象之中哪些还存活着,哪些已经死去。
【引用计数算法】
在对象中添加一个引用计数器,每当有一个地方引用时,计数器值加1;当引用失效时,计数器值就减1;任何时刻计数器为0的对象就是不能再使用的。
JVM没有采用,因为引用计数算法很难解决循环引用问题
class OBJ:
Object instance;
test():
objA=new OBJ();
objB=new OBJ();
objA.instance=objB,objB.instance=objA
objA和objB相互引用对方,导致它们的引用计数不为0,无法回收。
【可达性分析算法】
通过一系列称为"GC Roots"的根对象作为起始节点集,从这些节点开始,根据引用关系向下搜索,搜索过程所走过的路径称为“引用链”,如果某个对象到GC Roots间没有任何引用链相连,即GC Roots到这个对象不可达,此对象不能再被使用。
在java中,固定作为GC Roots的对象包括:
虚拟机栈中引用的对象
方法区中类静态属性引用的对象
方法区中常量引用的对象,如字符串常量池里 的引用
本地方法栈(native方法)引用的对象
Java虚拟机内部的引用,如基本数据类型对应的class对象,一些常驻的异常对象。
所有被同步锁持有的对象
反映Java虚拟机内部情况的JMXBean?JVMTI中注册的回调,本地代码缓存等
还可以有其他对象临时加入,共同构成GC Roots 集合,比如分代收集和局部回收时,一个区域的对象可能被其他区域的对象引用,这时候需要将这些关联区域的对象也一并加入GCRoots 集合中。
【引用】
如果引用类型的数据中存储的数值代表另外一块内存的起始地址,就称为是代表一个引用。
在JDK1.2之后,java将引用分为强引用,软应用,弱引用,虚引用
强引用:传统的引用定义。 无论任何情况下,只要强引用存在,垃圾收集器就不会回收掉被引用的对象
软引用:描述一些还有用但非必要的对象。
被软引用关联着的对象,在系统将要发生内存溢出异常前,会把这些对象列进回收范围之中进行第二次回收,如果这次回收还没有足够的内存,才会抛出OOM异常。
弱引用:描述那些非必须对象,但是它的强度比软引用更弱一些。被弱引用关联的对象只能生存到下一次垃圾收集发送为止。当垃圾收集器开始工作,无论当前内存是否足够,都会回收弱引用的对象
虚引用:最弱的引用关系。一个对象是否有虚引用的存在,完全不会对生存时间构成影响,也无法通过虚引用来取得一个对象实例。
【对象宣告死亡的过程】经过两次标记
【两次标记过程】
(1)是否不可达:是否有GCRoots到对象的引用链。
(2)是否有必要执行finalize()方法,如果没有直接回收。
没有必要执行: 对象没有覆盖finalize()方法 或 finalize()方法已经被虚拟机调用过
如果有必要执行finalize()方法,那么该对象会被放置在一个名为F-Queue的队列中,并在稍后一条由虚拟机自动建立的,低调度优先级的Finalizer线程去执行finalize()方法。
收集器会在F-Queue中的对象做第二次标记,如果对象在finalize()中拯救自己,即与引用链上的任意一个对象建立关联即可。
并不鼓励使用finalize()拯救对象,运行代价高昂,不确定性大。