Java虚拟机02——对象存活判断和4种引用

对象存活判断

垃圾收集器在对堆进行回收前,第一件事情就是要肯定这些对象之中哪些还“存活”着,哪些已经“死去”java

引用计数法

给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1;任什么时候候计数器为0的对象是不可能再被使用的。算法

  • 缺点:难以解决对象之间互相循环引用的问题

例子:引用计数算法的缺陷ide

-XX:+PrintGCDetails 经过此命令能够打印GC信息函数

public class RefrenceCountingGC {
    public Object instance = null;
    private static final int _1MB=1024* 1024;

    private byte[] bigSize = new byte[2 * _1MB];

    public static void testGC() {
        RefrenceCountingGC objA = new RefrenceCountingGC();
        RefrenceCountingGC objB = new RefrenceCountingGC();
        objA.instance = objB;
        objB.instance = objA;

        objA = null;
        objB = null;

        // 调用GC
        System.gc();
    }

    public static void main(String[] args) {
        testGC();
    }
}
复制代码

image.png

上面例子objA与objB互相依赖,从结果来看,内存大小从7014k -> 832k,虚拟机进行了回收,证实虚拟机不是经过引用计数法来判断存活的。测试

可达性分析算法

经过一系列的成为“GC Roots” 的对象做为起点,从这些节点开始向下搜索,当一个对象到GC Roots没有任何GC链链接时(从GC Roots到这个对象不可达)则证实这个对象是不可活的。如图:spa

image.png

GC Roots的对象包括下面几种code

  • 虚拟机栈(栈帧中的本地变量)中引用的对象
  • 方法区中类静态属性引用的对象
  • 方法区中常量引用的对象
  • 本地方法栈中JNI(Natice方法)引用的对象

4种引用

通过上面描述得知,对象的存活都与“引用”有关。在JDK1.2以后,Java对引用的概念进行了扩充,将引用分为强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Week Reference)、虚引用(Phantom Reference),引用强度依次减弱cdn

强引用 > 软引用 > 弱引用 > 虚引用对象

强引用

是使用最广泛的引用,相似“Object obj = new Object()”。只要强引用还存在,垃圾收集器就不会回收被引用的对象blog

软引用

描述一些还有用但并不是必须的对象。在系统将要发生内存溢出以前,将会把这些对象列进回收范围之中进行二次回收。若是此次回收尚未足够的内存,才会抛出内存溢出异常。

弱引用

描述非必须对象,强度比软引用还弱一些。被弱引用关联的对象只能生存到下次垃圾收集以前。当垃圾收集器工做时,不管当内存是否足够,都会回收掉只被弱引用关联的对象。

虚引用

一个对象是否有虚引用的存在,彻底不会对其生存时间构成影响,也没法经过虚引用来得到一个对象实例。为一个对象设置虚引用关联的惟一目的就是在这个对象被收集回收时收到一个系统通知、

4种引用代码实践

判断对象是否存活

若是经过可达性算法分析一个对象不可达,此时会被第一次标记,而且进行一次筛选,筛选的条件是此对象是否有必要执行finalize()方法。当对象没有覆盖该方法或已经被执行了,则不须要执行finalize()方法。

若是断定须要执行finalize()方法,那这个对象将会放在F-Queue的队列中,虚拟机会触发这个方法,但不承诺会等待它运行结束(若是方法缓慢,将致使崩溃)。稍后GC将会对F-Queue进行第二次标记,并把标记的对象移到“即将回收”的集合中。

综上,finalize()函数是在JVM回收内存时执行的,仅执行一次,但JVM并不保证在回收内存时必定会调用。

测试引用例子

先建立一个Demo类,重写它的finallize()方法,若是被GC了,则会打印信息

public class RefrenceDemo {

    @Override
    protected void finalize() throws Throwable {
        System.out.println("哎呀,我被回收了");
        super.finalize();
    }
}
复制代码

测试强引用

测试代码:

public static void main(String[] args) {
        RefrenceDemo demo = new RefrenceDemo();
        System.gc();
    }
复制代码

结果: 无任何输出,则证实强引用对象没有被回收

测试软引用

测试代码:

VM参数: -Xms20m -Xmx20m

public static void main(String[] args) {
        List<String> temp = new ArrayList<>();
        SoftReference<RefrenceDemo> ref = new SoftReference<>(new RefrenceDemo());
        for (int i = 0; i < 10000; i++) {
            temp.add(String.valueOf(i));
        }
        System.gc();
    }	
复制代码

运行代码,此时内存充足,并无输出任何结果

将代码中的10000改为100000

public static void main(String[] args) {
        List<String> temp = new ArrayList<>();
        SoftReference<RefrenceDemo> ref = new SoftReference<>(new RefrenceDemo());
        for (int i = 0; i < 100000; i++) {
            temp.add(String.valueOf(i));
        }
        System.gc();
    }
复制代码

运行结果:

image.png

此时,finalize()方法被执行了,说名内存不足,须要回收软引用的对象。

测试弱引用

VM参数: -Xms20m -Xmx20m

将上例的10000缩小到1000,此时内存空间是足够的。

public static void main(String[] args) {
        WeakReference<RefrenceDemo> ref = new WeakReference<>(new RefrenceDemo());

        System.out.println(ref.get());
        
        List<String> temp = new ArrayList<>();
        for (int i = 0; i < 1000; i++) {
            temp.add(String.valueOf(i));
        }

        System.gc();
    }
复制代码

运行结果:

image.png

在GC前,引用还存活,GC后便执行了finalize()方法,说明弱引用只能活到GC前

测试虚引用

VM参数: -Xms20m -Xmx20m

public static void main(String[] args) {
        ReferenceQueue queue = new ReferenceQueue();
        PhantomReference<RefrenceDemo> ref = new PhantomReference<>(new RefrenceDemo(),queue);

        System.out.println(ref.get());

        System.gc();
    }

复制代码

运行结果:

image.png

这说明虚引用在实例化后,就被终止了

相关文章
相关标签/搜索