【Java基础】强引用,软引用,弱引用,虚引用

1、强引用

在程序代码中普遍存在,类似Object obj = new Object()这类的引用,只要强引用还存在,当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足的问题。垃圾收集器永远不会回收掉被引用的对象.

例子1: 强引用

先解释finalize()方法: 每一个对象的finalize()(从Object继承的方法)都只会被系统自动调用一次,如果对象面临下一次回收,它的finalize()不会被再次执行.

package com.reference.test;

public class FinalizeEscapeGC {
    public static FinalizeEscapeGC SAVE_HOOK = null;
    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("finalize method executed");
        FinalizeEscapeGC.SAVE_HOOK = this;
    }
    public static void helpGC() throws Throwable {
        SAVE_HOOK = null;
        System.gc();
        Thread.sleep(500);
        if (SAVE_HOOK != null) {
            System.out.println("yes, i am still alive.");
        } else {
            System.out.println("no, i am dead.");
        }
    }
    
    public static void main(String[] args) throws Throwable {
        SAVE_HOOK = new FinalizeEscapeGC();
        helpGC(); // 第一次执行了finalize自救
        helpGC(); // finalize执行过了一次便不再执行了
    }
}

 结果如下: 在这里这个FinalizeEscapeGC对象有一个强引用SAVE_HOOK指向它, 如果不设置为null,垃圾回收器将不会回收该对象. 主动设置为null, 可以帮助垃圾收集器回收被引用的对象.

finalize method executed
yes, i am still alive.
no, i am dead.

2、软引用

软引用用来描述一些还有用,但并非必需的对象。对于软引用关联着的对象,在系统将要发生内存溢出异常之前,将会把这些对象列进回收范围之中并进行第二次回收.如果这次回收还是没有足够的内存,才会抛出内存溢出异常. 在JDK 1.2之后, 提供了SoftRefernce类来实现软引用。软引用可用来实现内存敏感的高速缓存。
软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收器回收,Java虚拟机就会把这个软引用加入到与之关联的引用队列中。

两次标记

一个对象从被判定为死亡对象到被垃圾收集器回收掉还要经历两次标记的过程,该过程可以认为是该对象在死刑的缓刑阶段。第一次标记:当可达性分析确认该对象没有引用链与GC Roots相连,则对其进行第一次标记和筛选,筛选的条件是重写了finalize()方法并没有执行过,对于重写了且并没有执行finalize()方法的对象这将其放置在一个F-Queue队列中,并在稍后由一个由虚拟机自动建立的低优先级的Finalizer线程去执行它。此处执行只保证执行该方法,但是不保证等待该方法执行结束,之所以这样子设计是为了系统的稳定性和健壮性考虑,以免该方法执行时间较长或者死循环导致系统崩溃。在此之后,系统会对对象进行第二次标记,如果在第一次标记之后的对象在执行finalize()方法时没有被引用到一个新的变量,这该对象将被回收掉。finalize方法只能被执行一次,并且一般不推荐也不建议重写Object的该方法,如果需要关闭外部资源,比如数据库,I/O等完全可在finally块中完成。

例子2: 软引用

下面的例子中TestSoftReference的一个对象由一个强引用tsr和一个软引用sr共同指向, 然后tsr = null去除强引用,此时还剩下一个软引用指向该对象,主动调用gc方法, 看看会发生什么?按照软引用的定义来看, 在内存还足够的时候不会回收软引用.

package com.reference.test;

import java.lang.ref.SoftReference;
public class TestSoftReference {
    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("finalize method executed");
    }

    public static void main(String[] args) {
        TestSoftReference tsr = new TestSoftReference();
        System.out.println("tsr instance: " + tsr);
        SoftReference sr = new SoftReference(tsr);
        /**
         *  此时TestSoftReference的一个对象有两个引用指向它:
         *  1. 一个强引用tsr
         *  2. 一个软引用sr
         */
        System.out.println("before gc: " + sr.get());
        tsr = null;  // 此时只有一个软引用sr指向该对象
        System.gc(); // 启动垃圾回收器
        System.out.println("after  gc: " + sr.get());
    }
}

结果如下: 可以看到只存在一个软引用指向该对象时, 即使主动调用System.gc()方法也没有回收该对象的内存空间.

tsr instance: com.reference.test.TestSoftReference@15db9742
before gc: com.reference.test.TestSoftReference@15db9742
after  gc: com.reference.test.TestSoftReference@15db9742

3、弱引用

只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。
弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。

例子3: 弱引用

import java.lang.ref.WeakReference;
public class TestWeakReference {

    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("finalize method executed");
    }

    public static void main(String[] args) {
        TestWeakReference twr = new TestWeakReference();
        WeakReference wr = new WeakReference(twr);
        /**
         *  此时TestSoftReference的一个对象有两个引用指向它:
         *  1. 一个强引用twr
         *  2. 一个弱引用sr
         */
        System.out.println("before gc: " + wr.get());
        twr = null;  //去掉强引用twr
        System.gc();
        System.out.println("after  gc: " + wr.get());
    }
}

结果如下: 可以看到弱引用已经被回收了.

before gc: com.reference.test.TestWeakReference@15db9742
after  gc: null
finalize method executed

接下来就会有个问题, 那弱引用对应的对象被回收了, 那弱引用本身该怎么办呢, 因为它本身也对应了一个WeakReference的对象.

ReferenceQueue

这个ReferenceQueue是专门用来存放引用的, 当软引用,弱引用,虚引用对应的那个对象被回收后的同时,该引用会自动加入到你所定义的ReferenceQueue中.

例子4: 引用队列

import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;

public class TestReferenceQueue {
    public static void main(String[] args) {
        ReferenceQueue rq = new ReferenceQueue();
        WeakReference wr = new WeakReference(new TestReferenceQueue(), rq);
        System.out.println("弱引用对应的对象:" + wr.get() + ", 弱引用本身:" + wr);
        System.out.println("队列中对象:" + rq.poll());
        /**
         * TestReferenceQueue中的对象只有一个引用 就是wr弱引用
         * 因此直接调用gc就可以
         */
        System.gc();
        System.out.println("弱引用对应的对象:" + wr.get() + ", 弱引用本身:" + wr);
        System.out.println("队列中对象:" + rq.poll());
    }
}

结果如下: 在弱引用对应的对象被回收后该弱引用对象本身也进入到了ReferenceQueue中.ReferenceQueue清除失去了弱引用对象的弱引用本身, 软引用,虚引用也是如此.

弱引用对应的对象:com.reference.test.TestReferenceQueue@15db9742, 弱引用本身:java.lang.ref.WeakReference@6d06d69c
队列中对象:null
弱引用对应的对象:null, 弱引用本身:java.lang.ref.WeakReference@6d06d69c
队列中对象:java.lang.ref.WeakReference@6d06d69c

虚引用

虚引用是最弱的一种引用关系. 一个对象是否有虚引用的存在, 完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例. 为一个对象设置虚引用关联的唯一目的就是希望能在这个对象被收集器回收时收到一个系统通知. 在JDK 1.2之后, 提供了PhantomReference类来实现虚引用。

虚引用必须与ReferenceQueue关联起来.

package com.reference.test;

import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;

public class TestPhantomReference {
    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("finalize method executed");
    }

    public static void main(String[] args) {
        ReferenceQueue rq = new ReferenceQueue();
        TestWeakReference twr = new TestWeakReference();
        PhantomReference pr = new PhantomReference(twr, rq);
        System.out.println("before gc: " + pr.get() + ", " + rq.poll());
        twr = null;  //去掉强引用twr
        System.gc();
        System.out.println("after  gc: " + pr.get() + "," + rq.poll());
    }
}

结果如下:

before gc: null, null
after  gc: null,null
finalize method executed

转自 https://www.jianshu.com/p/e5364c05cc80

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值