4.《深入理解Java虚拟机》对象是否可回收?

接下来的几篇博文就讨论如下问题:
1. 哪些内存需要回收(Java堆和方法去区)
2. 什么时候回收;
3. 如何回收。

对象已死吗?

Java堆里面几乎存放了所有的对象,垃圾收集器在对堆进行回收前,第一件事情就是确定哪些对象已经”死去”(即不可能再被任何途径使用的对象).下面介绍两种判断对象是否已死的算法:

1. 引用计数算法

这个算法的实现是:给对象中添加一个引用计数器,每当一个地方引用这个对象时,计数器值+1;当引用失效时,计数器值-1。任何时刻计数值为0的对象就是不可能再被使用的。这种算法实现简单,判断效率也很高,在大部分情况下是个不错的算法。但是,Java中却没有使用这种算法,因为这种算法很难解决对象之间相互引用的情况。看一段代码:

/** * main方法执行 * 虚拟机参数:-verbose:gc */
public class ReferenceCountingGC {
    private Object instance = null;
    private static final int _1MB = 1024 * 1024;

    /** 这个成员属性唯一的作用就是占用一点内存 */
    private byte[] bigSize = new byte[2 * _1MB];

    public static void main(String[] args)
    {
        ReferenceCountingGC objectA = new ReferenceCountingGC();
        ReferenceCountingGC objectB = new ReferenceCountingGC();
        objectA.instance = objectB;
        objectB.instance = objectA;
        objectA = null;
        objectB = null;

        System.gc();
    }
}

上面的面函数中,objA和objB 互相引用,实际上这两个对象已经不可能再被访问,因为它们互相引用着对方,导致它们的引用计数不可能为0,所以引用计数法的GC收集器不可能回收它们。

运行结果:(JDK7)
《4.《深入理解Java虚拟机》对象是否可回收?》
上图显示虚拟机并没有因为两个对象互相引用就不回收它们,这说明了Hotspot虚拟机用的不是引用计数法。

可达性分析算法

在Java中使用的是一种叫做可达性分析算法来判断对象是否”存活”的。
这个算法的基本思想是通过一系列称为“GC Roots”的对象作为起始点,从这些节点向下搜索,搜索所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链(即GC Roots到对象不可达)时,则证明此对象是不可用的。在Java语言中可以作为GC Roots的对象包括:
1. 虚拟机栈中引用的对象
2. 方法区中静态属性引用的对象
3. 方法区中常量引用的对象
4. 本地方法栈中JNI(即Native方法)引用的对象

再谈引用

在JDK1.2之前,Java中引用的定义很传统:如果引用类型的数据中存储的数值代表的是另一块内存的起始地址,就称这块内存代表着一个引用。这种定义很纯粹,但是太过于狭隘,一个对象只有被引用或者没被引用两种状态。我们希望描述这样一类对象:当内存空间还足够时,则能保留在内存中;如果内存空间在进行垃圾收集后还是非常紧张,则可以抛弃这些对象。很多系统的缓存功能都符合这样的应用场景。在JDK1.2之后,Java对引用的概念进行了扩充,将引用分为强引用、软引用、弱引用、虚引用4种,这4种引用强度依次减弱。
1、强引用
代码中普遍存在的类似”Object obj = new Object()”这类的引用,只要强引用还存在,垃圾收集器永远不会回收掉被引用的对象

2、软引用
描述有些还有用但并非必需的对象。在系统将要发生内存溢出异常之前,将会把这些对象列进回收范围进行二次回收。如果这次回收还没有足够的内存,才会抛出内存溢出异常。Java中的类SoftReference表示软引用

3、弱引用
描述非必需对象。被弱引用关联的对象只能生存到下一次垃圾回收之前,垃圾收集器工作之后,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。Java中的类WeakReference表示弱引用

4、虚引用
这个引用存在的唯一目的就是在这个对象被收集器回收时收到一个系统通知,被虚引用关联的对象,和其生存时间完全没关系。Java中的类PhantomReference表示虚引用

回收方法区

虚拟机规范中不要求方法区一定要实现垃圾回收,而且方法区中进行垃圾回收的效率也确实比较低,但是HotSpot对方法区也是进行回收的,主要回收的是废弃常量和无用的类两部分。

判断一个常量是否“废弃常量”比较简单,只要当前系统中没有任何一处引用该常量就好了,但是要判定一个类是否“无用的类”条件就要苛刻很多,类需要同时满足以下三个条件:
1. 该类所有实例都已经被回收,也就是说Java堆中不存在该类的任何实例
2. 加载该类的ClassLoader已经被回收
3. 该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法

在大量使用反射、动态代理、CGLib等ByteCode框架、动态生成JSP以及OSGi这类频繁自定义ClassLoader的场景都需要虚拟机具备类卸载功能,以保证方法区不会溢出。

    原文作者:java虚拟机
    原文地址: https://blog.csdn.net/u010853261/article/details/53967348
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞