Android四种引用:强引用、软引用、弱引用和虚引用

文章整理自 Android开发四种引用

Reference子类都具有如下特点:
1.Reference子类不能无参化直接创建,必须至少以强引用对象为构造参数,创建各自的子类对象;
2.以强引用对象为构造参数创建对象,使得原本强引用所指向的堆内存中的对象将不再只与强引用本身直接关联,与Reference的子类对象的引用也有一定联系。且此种联系将可能影响到对象的垃圾回收。

强引用 - Strong reference

实际编码中最常见的一种引用类型。常见形式如:A a = new A();

强引用本身存储在栈内存中,其存储指向对内存中对象的地址。一般情况下,当对内存中的对象不再有任何强引用指向它时,垃圾回收机器开始考虑可能要对此内存进行的垃圾回收。如当进行编码:a = null,此时,刚刚在堆中分配地址并新建的a对象没有其他的任何引用,当系统进行垃圾回收时,堆内存将被垃圾回收。

软引用 - Soft Reference

通过对象的强引用为参数,创建了一个SoftReference对象,并使栈内存中的srA指向此对象。

import java.lang.ref.SoftReference;

public class ReferenceTest {

    public static void main(String[] args) {
        A a = new A();
        SoftReference<A> srA = new SoftReference<A>(a);
        a = null;
        if (srA.get() == null) {
            System.out.println("a对象进入垃圾回收流程");
        } else {
            System.out.println("a对象尚未被回收" + srA.get());
        }

        // 垃圾回收
        System.gc();

        if (srA.get() == null) {
            System.out.println("a对象进入垃圾回收流程");
        } else {
            System.out.println("a对象尚未被回收" + srA.get());
        }
    }
}

class A {

}

输出结果:

a对象尚未被回收A@4807ccf6
a对象尚未被回收A@4807ccf6

当 a = null后,堆内存中的A对象将不再有任何的强引用指向它,但此时尚存在srA引用的对象指向A对象。当第一次调用srA.get()方法返回此指示对象时,由于垃圾回收器很有可能尚未进行垃圾回收,此时get()是有结果的。当程序执行System.gc();强制垃圾回收后,通过srA.get(),发现依然可以得到所指示的A对象,说明A对象并未被垃圾回收。

软引用所指示的对象开始被垃圾回收需要满足如下两个条件:
1.当其指示的对象没有任何强引用对象指向它;
2.当虚拟机内存不足时。
因此,SoftReference变相的延长了其指示对象占据堆内存的时间,直到虚拟机内存不足时垃圾回收器才回收此堆内存空间。

弱引用 - Weak Reference

import java.lang.ref.WeakReference;

public class ReferenceTest {

    public static void main(String[] args) {
        A a = new A();
        WeakReference<A> wrA = new WeakReference<A>(a);
        a = null;
        if (wrA.get() == null) {
            System.out.println("a对象进入垃圾回收流程");
        } else {
            System.out.println("a对象尚未被回收" + wrA.get());
        }

        // 垃圾回收
        System.gc();

        if (wrA.get() == null) {
            System.out.println("a对象进入垃圾回收流程");
        } else {
            System.out.println("a对象尚未被回收" + wrA.get());
        }
    }
}

class A {

}

输出结果:

a对象尚未被回收A@52e5376a
a对象进入垃圾回收流程

当进行垃圾回收后,wrA.get()将返回null,表明其指示对象进入到了垃圾回收过程中。
WeakReference不改变原有强引用对象的垃圾回收时机,一旦其指示对象没有任何强引用对象时,此对象即进入正常的垃圾回收流程。

主要使用场景见于:当前已有强引用指向强引用对象,此时由于业务需要,需要增加对此对象的引用,同时又不希望改变此引用的垃圾回收时机,此时WeakReference正好符合需求,常见于一些与生命周期的场景中。

public class MainActivity extends AppCompatActivity {

    private int page;
    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            if (msg.what == 1) {
                page++;
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        new Thread(new Runnable() {
            @Override
            public void run() {
                Message msg = Message.obtain();
                msg.what = 1;
                handler.sendMessage(msg);
            }
        }).start();
    }
}

将会看到警示信息如下:

This Handler class should be static or leaks might occur (anonymous android.os.Handler) less... (Ctrl+F1)
Inspection info:Since this Handler is declared as an inner class, it may prevent the outer class from being garbage collected. If the Handler is using a Looper or MessageQueue for a thread other than the main thread, then there is no issue. If the Handler is using the Looper or MessageQueue of the main thread, you need to fix your Handler declaration, as follows: Declare the Handler as a static class; In the outer class, instantiate a WeakReference to the outer class and pass this object to your Handler when you instantiate the Handler; Make all references to members of the outer class using the WeakReference object. Issue id: HandlerLeak

在Activity中使用Handler时,一方面需要将其定义为静态内部类形式,这样可以使其与外部类(Activity)解耦,不再持有外部类的引用,同时由于Handler中的handlerMessage一般都会多少需要访问或修改Activity的属性,此时,需要在Handler内部定义指向此Activity的WeakReference,使其不会影响到Activity的内存回收同时,可以在正常情况下访问到Activity的属性。

import java.lang.ref.WeakReference;

public class MainActivity extends AppCompatActivity {
    
    private int page;
    private MyHandler mMyHandler = new MyHandler(this);

    private static class MyHandler extends Handler {

        private WeakReference<MainActivity> wrActivity;

        public MyHandler(MainActivity activity) {
            this.wrActivity = new WeakReference<MainActivity>(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            if (wrActivity.get() == null) {
                return;
            }
            MainActivity mActivity = wrActivity.get();
            if (msg.what == 1) {
                mActivity.page++;
            }
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        new Thread(new Runnable() {
            @Override
            public void run() {
                Message msg = Message.obtain();
                msg.what = 1;
                mMyHandler.sendMessage(msg);
            }
        }).start();
    }
}

对于SoftReference和WeakReference,还有一个构造器参数为ReferenceQueue<T>,当SoftReference或WeakReference所指示的对象确实被垃圾回收后,其引用将被放置于ReferenceQueue中。注意上文中,当SoftReference或WeakReference的get()方法返回null时,仅是表明其指示的对象已经进入垃圾回收流程,此时对象不一定已经被垃圾回收。而只有确认被垃圾回收后,如果ReferenceQueue,其引用才会被放置于ReferenceQueue中。

import java.lang.ref.WeakReference;

public class ReferenceTest {

    public static void main(String[] args) {
        A a = new A();
        WeakReference<A> wrA = new WeakReference<A>(a);
        a = null;
        if (wrA.get() == null) {
            System.out.println("a对象进入垃圾回收流程");
        } else {
            System.out.println("a对象尚未被回收" + wrA.get());
        }

        // 垃圾回收
        System.gc();

        if (wrA.get() == null) {
            System.out.println("a对象进入垃圾回收流程");
        } else {
            System.out.println("a对象尚未被回收" + wrA.get());
        }
    }
}

class A {
    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("in A finalize");
    }
}

输出结果:验证了上文中的“进入垃圾回收流程”的说法。

a对象尚未被回收A@46993aaa
a对象进入垃圾回收流程
in A finalize
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;

public class ReferenceTest {

    public static void main(String[] args) {
        A a = new A();
        ReferenceQueue<A> rq = new ReferenceQueue<A>();
        WeakReference<A> wrA = new WeakReference<A>(a, rq);

        a = null;
        
        if (wrA.get() == null) {
            System.out.println("a对象进入垃圾回收流程");
        } else {
            System.out.println("a对象尚未被回收" + wrA.get());
        }

        System.out.println("rq item:" + rq.poll());

        // 垃圾回收
        System.gc();

        if (wrA.get() == null) {
            System.out.println("a对象进入垃圾回收流程");
        } else {
            System.out.println("a对象尚未被回收" + wrA.get());
        }
        
        System.out.println("rq item:" + rq.poll());
    }
}

class A {
    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("in A finalize");
    }
}

输出结果:验证了“仅进入垃圾回收流程的SoftReference或WeakReference引用尚未被加入到ReferenceQueue”。

a对象尚未被回收A@302b2c81
rq item:null
a对象进入垃圾回收流程
rq item:null
in A finalize
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;

public class ReferenceTest {

    public static void main(String[] args) {
        A a = new A();
        ReferenceQueue<A> rq = new ReferenceQueue<A>();
        WeakReference<A> wrA = new WeakReference<A>(a, rq);

        a = null;

        if (wrA.get() == null) {
            System.out.println("a对象进入垃圾回收流程");
        } else {
            System.out.println("a对象尚未被回收" + wrA.get());
        }

        System.out.println("rq item:" + rq.poll());

        // 垃圾回收
        System.gc();

        if (wrA.get() == null) {
            System.out.println("a对象进入垃圾回收流程");
        } else {
            System.out.println("a对象尚未被回收" + wrA.get());
        }

        try {
            Thread.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("rq item:" + rq.poll());
    }
}

class A {
    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("in A finalize");
    }
}

输出结果:

a对象尚未被回收A@6276e1db
rq item:null
a对象进入垃圾回收流程
in A finalize
rq item:java.lang.ref.WeakReference@645064f

虚引用 - PhantomReference

与SoftReference或WeakReference相比,PhantomReference主要差别体现在如下几点:
1.PhantomReference只有一个构造函数PhantomReference(T referent, ReferenceQueue<? super T> q),因此,PhantomReference使用必须结合ReferenceQueue;
2.不管有无强引用指向PhantomReference的指示对象,PhantomReference的get()方法返回结果都是null。

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

public class ReferenceTest {

    public static void main(String[] args) {
        A a = new A();

        ReferenceQueue<A> rq = new ReferenceQueue<A>();
        PhantomReference<A> prA = new PhantomReference<A>(a, rq);
        System.out.println("prA.get():" + prA.get());

        a = null;
        System.gc();

        try {
            Thread.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("rq item:" + rq.poll());
    }
}

class A {
    
}

输出结果:

prA.get():null
rq item:java.lang.ref.PhantomReference@1da12fc0

Thread.sleep(1);确保垃圾回收线程能够执行。否则,进进入垃圾回收流程而没有真正被垃圾回收的指示对象的虚引用是不会被加入到PhantomReference中的。

与WeakReference相同,PhantomReference并不会改变其指示对象的垃圾回收时机。
ReferenceQueue的作用主要是用于监听SoftReference/WeakReference/PhantomReference的指示对象是否已经被垃圾回收。

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

推荐阅读更多精彩内容