WeakReference解决内存泄漏

内存泄漏是经常会遇到的问题,内存泄漏简单说就是已经分配出去的内存,不再使用时没有被正确释放,不能再次分配。代码中内存泄漏问题本质就是长生命周期的对象持有了短生命周期的对象,短生命周期对象不能被销毁。内存泄漏严重时会导致OOM。

Java中的有四种内存,常用的分别是强引用、软引用和弱引用(Kotlin也是)。 强引用不用多说,只要是通过new关键字创建的对象都是,无论什么时候JVM都不会回收强引用,宁可触发OOM。

被SoftReference包住的称为软引用,像是SoftReference<new Object()>的形式,内存不够时会回收掉只有软引用的对象(一个对象只被软引用)。

弱引用和软引用的形式类似,弱引用的软件词:WeakReference,GC时发现只有弱引用的对象(一个对象只被弱引用)会立马回收。

上面的描述和其他博客的看起来没有太大的差别,除了加粗的”只有“,关键页正是”只有“。

举例:

public class DemoActivity extends AppCompatActivity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        SomethingManager.getInstance().setsSomethingListener(new SomethingManager.SomethingListener() {
            @Override
            public void doSomething() {
                // do something
            }
        });
    }
}


class SomethingManager {
    static SomethingManager sSomethingManager;

    public static SomethingManager getInstance() {
        if (sSomethingManager == null) {
            sSomethingManager = new SomethingManager();
        }
        return sSomethingManager;
    }

    private SomethingListener sSomethingListener;

    public void setsSomethingListener(SomethingListener sSomethingListener) {
        this.sSomethingListener = sSomethingListener;
    }
    
        
    void run(){
        sSomethingListener.doSomething();
    }

    interface SomethingListener {
        void doSomething();
    }

写一个最简单的例子,一个单例对象持有一个listener,理想的情况当然是Activity销毁的时候移除listener。总会有不理想的人忘记了,当Activity销毁时就发生了内容泄漏:长生命周期的对象引用了段生命周期的对象。用弱引用解决:

public class DemoActivity extends AppCompatActivity {
    private SomethingManager.SomethingListener mSomethingListener = new SomethingManager.SomethingListener() {
        @Override
        public void doSomething() {
            // do something
        }
    };

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        SomethingManager.getInstance().setsSomethingListener(new WeakReference<>(mSomethingListener));
    }
}


class SomethingManager {
    static SomethingManager sSomethingManager;

    public static SomethingManager getInstance() {
        if (sSomethingManager == null) {
            sSomethingManager = new SomethingManager();
        }
        return sSomethingManager;
    }

    private WeakReference<SomethingListener> sSomethingListener;

    public void setsSomethingListener(WeakReference<SomethingListener> sSomethingListener) {
        this.sSomethingListener = sSomethingListener;
    }

    void run() {
        SomethingListener somethingListener = sSomethingListener.get();
        if (somethingListener != null) {
            somethingListener.doSomething();
        }
    }

    interface SomethingListener {
        void doSomething();
    }
}

与刚才的代码有两处不同:

  1. DemoActivity中新增了SomethingListener类型的成员变量;
  2. 以弱引用的形式传入SomethingListener实例;

只持有弱引用的对象被GC发现会被立即回收,为了防止被回收,增加一个强引用类型的成员变量,当DemoActivity示例被回收后,强引用对象不存在了,这时候SomethingListener对象只在SomethingManager中有一个弱引用对象,GC时就会回收,因此解决了内存泄漏问题。