利用Android Monitor(dump java heap)分析内存泄露溢出问题,超详细,包你懂。

1、首先来个内存泄露的例子。

public class HandlerActivity extends BaseActivity {
    ActivityHandlerBinding mDataBinding;
    private MHandler mMhandler = new MHandler(this);

    private Handler mHandler = new Handler(){

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mDataBinding = DataBindingUtil.setContentView(this,R.layout.activity_handler);
        mDataBinding.tvSendHandlerMsg.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                mHandler.sendEmptyMessageDelayed(1,1000 * 60 * 10);
            }
        });
    }

}
泄露原因:
操作:运行本页面的线程一段时间后退出到主页面。
分析:1、在Java中,非静态内部类会隐性地持有外部类的引用,而静态内部类则不会;非静态方法会隐性的持有对象引用,而静态内部类则不会。
* 非静态内部类的外部对象是在构造方法里面传入的,具体可见this$0的用法,网址:https://blog.csdn.net/tkwdmylove/article/details/23193763* 而非静态方法是在调用的时候传入* 2Message对象引用着Handler对象,而Handler对象因为是非静态内部类,所以Handler对象也引用着HandlerActivity实例。
* 所以在Message未执行完的情况下会导致对应的HandlerActivity实例处于泄露的状态,而前者执行完后后者就被GC回收释放。

2、使用AndroidStudio生成并分析hprof图,它不是标准的hprof图。

网上有很多类似的文章,但是都没教我们如何分析出内存溢出的原因。Androidstudio的好处是他可以自动帮我们分析出泄露的Activity,首先我们先进行检测看看是否有Activity泄露。打开Prof文件并进行如下图操作所示:                    

1),看Reference Tree第一行,这就是我们点击的泄露的HandlerActivity对象。Reference Tree其实就是显示当前对象的引用关系的,帮助我们分析泄露的原因,具体怎么分析稍后讲解。

2),接着看第二行表示handleractivity对象被一个this$0的属性引用并且这个属性是HandlerActivity的一个内部类对象的一个成员变量。

3)继续看这张图:                                                                                                             

可以看到this$0这个引用的对象同时被target和mHandler属性引用,target是属于message对象的而mHandler是属于HandlerActivity的。其实挺简单的,hprof的每一行的模式是这样的:xxx in yyyyy,xxx是引用变量的意思而yyyy是指这个引用变量隶属于那个对象。而如果符合从属关系的两行比如this$0与target的关系,target这一行表达的是对上一行的对象HandlerActivity$1的引用而不是对上一行 属性this$0的引用。

致辞Prof文件的解读就讲完了,是不是很简单呢,但是自己摸索的时候还是花了点时间呢。

3、Prof文件是看的懂了,但是怎么分析内存泄露的原因呢?

其实我们看到HandlerActivity对象是被一个自己的内部类对象的this$0属性引用的,然后这个内部类对象又被Message对象中的target属性引用,在GC线程尝试回收activity的时候发现message对象开始一直引用着这个activity导致它无法被回收也就是泄露了,这跟上面分析的基本一致。

额外分析:也许你们会说这么多的引用关系该怎么选择哪条引用链进行分析呢?哼,这你就不懂了吧,我们肯定是要找与我们代码有关系的啊,一般第一条就是了。为什么这么说呢?你可以写一个继承了当前父类的空Activity去测试一下看看会不会泄露,如果不会那就说明肯定是我们的代码导致的或者是我们的代码与父类的代码的结合导致的泄露咯,不过我们重点在关注我们的代码就好了。也许你又会问为什么我会这样想呢,哼,因为我是自己从0开始摸索过来的,认识等肯定比你看我文章高多了,开心,觉得有收获就点个赞鼓励下呗。








猜你喜欢

转载自blog.csdn.net/jiayouwangqiuwangzi/article/details/80688887
今日推荐