四种引用类型 —— 软引用与弱引用的应用

四种引用类型的概念

强引用 StrongReference

如果一个对象具有强引用,那么垃圾回收器绝对不会回收它,当内存不足时宁愿抛出 OOM 错误,使得程序异常停止。

Object object = new Object(); 即是一个强引用。

软引用 SoftReference

如果一个对象只具有软引用,那么垃圾回收器在内存充足的时候不会回收它,而在内存不足时会回收这些对象。软引用对象被回收后,Java 虚拟机会把这个软引用加入到与之关联的引用队列中。

弱引用 WeakReference

如果一个对象只具有弱引用,那么垃圾回收器在扫描到该对象时,无论内存充足与否,都会回收该对象的内存。与软引用相同,弱引用对象被回收后,Java 虚拟机会把这个弱引用加入到与之关联的引用队列中。

虚引用 PhantomReference

虚引用并不决定对象生命周期,如果一个对象只具有虚引用,那么它和没有任何引用一样,任何时候都可能被回收。虚引用主要用来跟踪对象被垃圾回收器回收的活动。与软引用和弱引用不同的是,虚引用必须关联一个引用队列。

当垃圾回收器准备回收一个对象之前,如果发现它还具有虚引用,就会在对象回收前把这个虚引用加入到与之关联的引用队列中。程序可以通过判断引用队列中是否加入了虚引用,来了解被引用的对象是否将要被回收,那么就可以在其被回收之前采取必要的行动。

软引用构建敏感数据的缓存

软引用非常适合于创建缓存。当系统内存不足的时候,缓存中的内容是可以被释放的。

例如,一个程序用来处理用户提供的图片。如果将所有图片读入内存,这样虽然可以很快的打开图片,但内存空间使用巨大,一些使用较少的图片浪费内存空间,需要手动从内存中移除。如果每次打开图片都从磁盘文件中读取到内存再显示出来,虽然内存占用较少,但一些经常使用的图片每次打开都要访问磁盘,代价巨大。这个时候就可以用软引用构建缓存。

// 获取对象并缓存
Object object = new Object();
SoftReference softRef = new SoftReference(object);
// 从软引用中获取对象
Object object = (Object) softRef.get();
if (object == null){
    // 当软引用被回收后重新获取对象
    object = new Object();
}

弱引用解决内存泄露问题

虽然 Java 有垃圾回收机制,但是只要是自己管理的内存,就应该警惕内存泄露的问题,例如对象池、缓存中的过期对象都有可能引发内存泄露的问题。用 WeakHashMap 来作为缓存的容器可以有效解决这一问题。WeakHashMap 和 HashMap 几乎一样,唯一的区别就是它的键使用弱引用。当 WeakHashMap 的键标记为过期时,这个键对应的条目就会自动被移除。这就避免了内存泄漏问题。

另外在 Android 中常常用到 Handler 消息处理机制。当使用内部类(包括匿名内部类)来创建 Handler 时,Handler 对象会隐式的持有一个其外部类对象(通常是一个 Activity)的引用。而 Handler 通常会开启一个耗时的工作线程(例如下载获取数据),工作线程完成任务后,通过消息机制通知 Handler 在主线程做相应的处理。如果在工作线程执行的过程中关闭了 Activity,Activity 应该在 GC 检查时被回收掉。但因为 Activity 被 Handler 持有引用,Handler 又被 Message 持有引用,Message 被 MessageQueue 持有引用,MessageQueue 被 Looper 持有引用,而 Looper 是线程本地变量,线程不销毁,它就不会被销毁。这样一个引用链,导致 Activity 无法被回收,造成内存泄漏。

1. 通过程序逻辑控制防止内存泄漏

比如在 Activity 关闭时停掉工作线程,移除消息队列中的消息对象,这样切断了 Activity 的引用,就可以正常的被回收了。

2. 将 Handler 声明成静态内部类

静态类不持有外部类的对象,所以 Activity 可以被正常的回收。但这个时候 Handler 无法操作 Activity 中的对象了,所以这个时候需要增加一个对 Activity 弱引用。代码如下:

static class MyHandler extends Handler {
    private WeakReference<Activity> reference;

    public MyHandler(Activity activity) {
        // 持有 Activity 的弱引用
        reference = new WeakReference<Activity>(activity);
    }

    @Override
    public void handleMessage(Message msg) {
        Activity activity = reference.get();
        if (activity != null && !activity.isFinishing()) {
            switch (msg.what) {
                // 处理消息
            }
        }
    }
}
posted @ 2017-02-26 01:40  银色子弹  阅读(7571)  评论(0编辑  收藏  举报