JVM GC垃圾回收机制之对象存活判断

1.引用计数算法

1.1概念
在对象中添加一个引用计数器,每当有一个地方 引用它时,计数器值就加一;当引用失效时,计数器值就减一;任何时刻计数器为零的对象就是不可能再被使用的。
1.2 缺陷和问题

        在Java领域,至少主流的Java虚拟机里面都没有选用引用计数算法来管理内存,主要原因是,这个看似简单的算法有很多例外情况要考虑,必须要配合大量额外处理才能保证正确地工作,譬如单纯的引用计数就很难解决对象之间相互循环引用的问题。

例子:代码中中的testGC()方法:对象objA和objB都有字段instance,赋值令objA.instance=objB及objB.instance=objA,除此之外,这两个对象再无任何引用,实际上这两个对象已经不可能再被访问,但是它们因为互相引用着对方,导致它们的引用计数都不为零,引用计数算法也就无法回收它们。
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();
}
}

2.可达性分析算法

2.1 概念
        通过一系列称为“GC Roots” 的根对象作为起始节点集,从这些节点开始,根据引用关系向下搜索,搜索过 程所走过的路径称为“ 引用链 Reference Chain ),如果某个对象到 GC Roots 间没有任何引用链相连, 或者用图论的话来说就是从GC Roots 到这个对象不可达时,则证明此对象是不可能再被使用的。

2.2 什么是被作为GC Root的对象
在虚拟机栈(栈帧中的本地变量表)中引用的对象,譬如各个线程被调用的方法堆栈中使用到的
参数、局部变量、临时变量等。
在方法区中类静态属性引用的对象,譬如 Java 类的引用类型静态变量。
在方法区中常量引用的对象,譬如字符串常量池( String Table )里的引用。
在本地方法栈中 JNI (即通常所说的 Native 方法)引用的对象。
Java虚拟机内部的引用,如基本数据类型对应的Class 对象,一些常驻的异常对象(比如NullPointExcepiton、 OutOfMemoryError )等,还有系统类加载器。
所有被同步锁( synchronized 关键字)持有的对象。
反映 Java 虚拟机内部情况的 JMXBean JVMTI 中注册的回调、本地代码缓存等。
        除了这些固定的GC Roots 集合以外,根据用户所选用的垃圾收集器以及当前回收的内存区域不同,还可以有其他对象“ 临时性 地加入,共同构成完整 GC Roots 集合。

3.再谈引用

3.1 强引用
        强引用是最传统的“ 引用 的定义,是指在程序代码之中普遍存在的引用赋值,即类似 “Object
obj=new Object()” 这种引用关系。无论任何情况下,只要强引用关系还存在,垃圾收集器就永远不会回收掉被引用的对象。
3.2 软引用
        软引用是用来描述一些还有用,但非必须的对象。只被软引用关联着的对象,在系统将要发生内存溢出异常前,会把这些对象列进回收范围之中进行第二次回收,如果这次回收还没有足够的内存,才会抛出内存溢出异常。
3.3 弱引用
        弱引用也是用来描述那些非必须对象,但是它的强度比软引用更弱一些,被弱引用关联的对象只能生存到下一次垃圾收集发生为止。当垃圾收集器开始工作,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。
3.4 虚引用
        一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。为一个对象设置虚引用关联的唯一目的只是为了能在这个对象被收集器回收时收到一个系统通知。

4.对象的生死判断

4.1概念
        即使在可达性分析算法中判定为不可达的对象,也不是“ 非死不可 的,这时候它们暂时还处于 缓刑” 阶段。
        要真正宣告一个对象死亡,至少要经历两次标记过程:如果对象在进行可达性分析后发现
有与GC Roots 相连接的引用链,那它将会被第一次标记,随后进行一次筛选,筛选的条件是此对象是否有必要执行finalize() 方法。假如对象没有覆盖 finalize() 方法,或者 finalize() 方法已经被虚拟机调用过,那么虚拟机将这两种情况都视为“ 没有必要执行 ”,直接被回收。
4.2对象最后一次活命机会
        如果对象覆盖了finalize()方法并且从来没有调用过,JVM会将该对象放入一个叫F-Queue队列中,并在稍后由一个由JVM自动创建的低优先级的Finalizer线程去执行它。JVM并不保证会等待finalize方法执行完毕。finalize()方法是对象逃脱GC的唯一一次机会,如果对象在该方法中重新与引用链上的任一对象建立了关联,那么JVM将会从待回收列表中移除该对象,否则该对象就没有机会逃脱被GC的命运了。

5.回收方法区

方法区的垃圾收集主要回收两部分内容:废弃的常量和不再使用的类型。
5.1判断废弃的常量
        假如一个字符串“java” 曾经进入常量池中,但是当前系统又没有任何一个字符串对象的值是“java” ,换句话说,已经没有任何字符串对象引用常量池中的“java” 常量,且虚拟机中也没有其他地方引用这个字面量。如果在这时发生内存回收,而且垃圾收集器判断确有必要的话,这个“java” 常量就将会被系统清理出常量池。常量池中其他类(接口)、方法、字段的符号引用也与此类似。
5.2判断不在使用的类
        该类所有的实例都已经被回收,也就是Java 堆中不存在该类及其任何派生子类的实例。
        加载该类的类加载器已经被回收,这个条件除非是经过精心设计的可替换类加载器的场景,如 OSGi、 JSP 的重加载等,否则通常是很难达成的。
        该类对应的java.lang.Class 对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。
        Java虚拟机被允许对满足上述三个条件的无用类进行回收,这里说的仅仅是 被允许 ,而并不是和对象一样,没有引用了就必然会回收。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值