通过例子理解java强引用,软引用,弱引用,虚引用

前言

在看threadlocal源码的时候碰到了弱引用, 所以本文将用例子来理解java中的四种引用类型.

本文源码下载

强引用

在程序代码中普遍存在,类似Object obj = new Object()这类的引用,只要强引用还存在,垃圾收集器永远不会回收掉被引用的对象.

例子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.

软引用

软引用用来描述一些还有用,但并非必需的对象. 对于软引用关联着的对象,在系统将要发生内存溢出异常之前,将会把这些对象列进回收范围之中并进行第二次回收.如果这次回收还是没有足够的内存,才会抛出内存溢出异常. 在JDK 1.2之后, 提供了SoftRefernce类来实现软引用.

例子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

弱引用

用来描述非必需对象的,但是它的强度比软引用更弱一些, 被弱引用关联的对象只能生存到下一次垃圾收集发生之前. 当垃圾收集器工作时,无论当前内存是否足够,都会回收掉只被弱引用关联的对象.在JDK 1.2之后,提供了WeakReference类来实现弱引用.

例子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

参考

1. 深入理解Java虚拟机
2. https://blog.csdn.net/swebin/article/details/78571933

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,233评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,013评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,030评论 0 241
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,827评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,221评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,542评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,814评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,513评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,225评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,497评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,998评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,342评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,986评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,812评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,560评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,461评论 2 266

推荐阅读更多精彩内容