分享

单例模式的内存泄漏陷阱

 昵称10504424 2015-08-28

最近项目开发中使用了一个叫做leakcanary的内存泄漏检查工具,当开发中的调试运行时发生内存泄漏,leakcanary会在notification弹出一个内存泄漏报告,最近发生了个内存泄漏并且leakcanary给出了下列报告:

分析下Leakcanary给出的信息,最后一行它说PopOrderActivity这个实例发生了泄漏,即系统gc的时候没有把这个 activity给回收(本该回收的,应该是已经退出这个activity了),倒数第二行即说明了有一个叫做PendingOrderManager的 类含有这个activity的引用,查看代码,这个PendingOrderManager是个单例,同时它的构造函数传入了一个Context参数:

复制代码
public class PendingOrderManager {

    private static PendingOrderManager instance;

    private Context mContext;

    public PendingOrderManager(Context context) {
    
        this.mContext = context;
    }

    public static PendingOrderManager getInstance(Context context) {
        if (instance == null) {
            instance = new PendingOrderManager(context);
        }
        return instance;
    }

...

}
复制代码

之所以要传入个context是因为这个Manager里面需要创建Preference。

那么现在发生内存泄漏的原因也就很明了了,由于PendingOrderManager是一个单例模式,那么这个类的生命周期就伴随整个应用的生命 周期,而它在被PopOrderActivity创建的时候引用了PopOrderActivity,所以当系统GC的时候试图去回收 PopOrderActivity时,发现它却在被另一个任然在内存里的PendingOrderManager所引用,所以GC回收它失败,从而导致了 内存泄漏。

 

那么如何解决这个问题呢?答案很简单,在PendingOrderManager中对context的属性使用弱引用即可:

复制代码
public class PendingOrderManager {

    private static PendingOrderManager instance;

    private WeakReference<Context> wr;

    public PendingOrderManager(Context context) {
        L.d("PendingOrderManager <constructor>");
        wr = new WeakReference<>(context);
    
    }

    public static PendingOrderManager getInstance(Context context) {
        if (instance == null) {
            instance = new PendingOrderManager(context);
        }
        return instance;
    }

...
}
复制代码

 

在PendingOrderManager中原来需要使用Context的地方,用wr.get()即可:

String timesListStr = (String) SPUtils.getPendingOrder(wr.get(), KEY_TIMES_LIST, "");
//这里的wr.get()原来是mContext

 

这里需要注意的一点是,由于PendingOrderManager这个时候含有的“context”可以被回收置空了,那么后面使用context的地方要注意判断是否为空,即对wr.get的地方注意检查空情况。

还有一种方式可以解决这个问题,考虑到每个使用到PendingOrderManager的地方当都会通过这种方式:

(PendingOrderManager.getInstance(mContext).getXXX()

即每次都能传过来一个当前的调用者的context(肯定不为空),那么在PendingOrderManager的getInstance方法里 面除了判定instance是否为空外,最好在判定下wr.get是否为空,这样子若上一个实例化PendingOrderManager的 activity被回收后,可以考虑用新的context来重新创建PendingOrderManager的单例。改造后的getInstance方 法:

复制代码
public class PendingOrderManager {

    private static PendingOrderManager instance;

    private WeakReference<Context> wr;

    public PendingOrderManager(Context context) {
        L.d("PendingOrderManager <constructor>");
        wr = new WeakReference<>(context);
    
    }

    public static PendingOrderManager getInstance(Context context) {
        if (instance == null || wr.get() == null) {
            instance = new PendingOrderManager(context);
        }
        return instance;
    }

...
}
复制代码

 

 

PS:

leakcanary是个很好的工具,下列是一些参考资料:

http://www./cn/posts/leak-canary-read-me/

 

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多