美文网首页Android知识点点滴汇聚
android内存泄露MAT分析心得与注意点

android内存泄露MAT分析心得与注意点

作者: 幸福的程序媛 | 来源:发表于2015-10-18 16:26 被阅读1763次

           最近工作项目中出现了内存泄露,于是找来MAT分析,学习了下。之前学习使用MAT时一直不知道怎么分析,啥时候获取hprof文件等,一直对MAT的使用感觉云里雾里,直到昨天,成功的解决了内存泄露的问题才稍稍知道了。

          具体怎么分析推荐两篇文章,一篇郭大神的http://blog.csdn.net/guolin_blog/article/details/42238633,一篇夏大神的http://blog.csdn.net/xiaanming/article/details/42396507 我也是看的这两篇文章才慢慢懂了。这两篇文章里面都对MAT的分析讲的很清楚,但有个问题,两者都没有提到,就是在什么时候去dump java heap.正是这个重要的点没有讲到,我一直对MAT感觉是块鸡肋,一直不知道怎么使用。

            首先要注意的是如果你要排查一个类里面的内存泄露,你首先要退出这个类的时候去dump java heap ,否则如果你在这个类里面时,取得文件去分析时仍然有小红点(小红点是啥意思,具体参考上述郭大神博客),因为这个类正在被使用,所以他肯定没释放。(之前因为这个,在没有内存泄露的时候去分析hprof文件,却发现了红点,疑惑了好久)同时去dump java heap 之前要initiate gc ,如果不的话在mat的Histogram视图里面搜怀疑没释放的类的实例个数时,可能依然有多个,但在其排除软、弱、虚引用后的Path to GC root上却没有发现有表明可能存在泄露的地方。这时就会让人很疑惑,没找到内存泄露的地方,但实例却有多个,这个怎么回事呢,到底有没有泄露呢。这里我的结论是没有泄露也可能有多个实例存在,因为系统没有立刻进行垃圾回收啊,所以你在dump java heap 之前记得点下initiate gc ,过一会然后再去获取hprof文件去分析,这个时候如果仍然存在多个实例,那就说明很可能有泄露了。

    在使用handler作为内部类的时候,你会得到这样的提示

    handler should be static or leaks might occur

    ,去测试了下


    MainActivity.java

    public classMainActivityextendsAppCompatActivity{

    MyHandlermyHandler;

    @Override

    protected voidonCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_main);

    Buttonbutton=(Button)findViewById(R.id.button);

    myHandler=newMyHandler();

    myHandler.sendEmptyMessageDelayed(0,10*1000);

    button.setOnClickListener(newView.OnClickListener() {

    @Override

    public voidonClick(View v) {

    Intentintent=newIntent(MainActivity.this,Main2Activity.class);

    startActivity(intent);

    MainActivity.this.finish();

    }

    });

    }

    @Override

    protected voidonDestroy() {

    super.onDestroy();

    Log.v("PLU","-----onDestroy");

    //  myHandler.removeCallbacksAndMessages(null);

    }

    classMyHandlerextendsHandler{

    //WeakReference wf;

    // public MyHandler(MainActivity mainActivity){

    // wf=new WeakReference(mainActivity);

    // }

    @Override

    public voidhandleMessage(Message msg) {

    super.handleMessage(msg);

    // MainActivity mainActivity=wf.get();

    // mainActivity.log();

    log();

    }

    }

    public voidlog(){

    Log.v("PLU","I AM HANDLER ");

    }

    }


    Main2Activity.java

    public classMain2ActivityextendsAppCompatActivity{

    @Override

    protected voidonCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_main2);

    TextViewtextView=(TextView)findViewById(R.id.tv);

    textView.setOnClickListener(newView.OnClickListener() {

    @Override

    public voidonClick(View v) {

    Intentintent=newIntent(Main2Activity.this,MainActivity.class);

    startActivity(intent);

    Main2Activity.this.finish();

    }

    });

    }

    }

    在onDestroy时没有调handler.removeMessageAndCallback, 在两个Activity之间多次跳转后,调到Main2Activity页面,点击initiate GC后点击dump java heap ,转成标准文件后在MAT里面分析,发现MainActiivty的实例为0个,为什么我没有写成注释掉的那种WeakRefrence引用的写法也没有发生内存泄露呢,主要官方说的may occur leak ,不是说一定会,是因为执行完了就释放了,只是释放的晚一些。在调到Main2Activity页面时此时MainActivity类还没有释放,可以看到仍然有打印MainActivity里面的log,在处理完消息后MainActivity也释放了,如果MainActivity里面handler的handleMessage里面是个死循环一直在打印的话,那MainActivity就一直存在,就释放不掉。在多次切换后就会发生内存泄露,Mat分析时会发现handler持有MainActiivty,存在多个MainActivity实例。想当然的认为,如果将MainActivity的onDestroy的myHandler.removeCallbacksAndMessages(null);解开注释的话,MainActivity也不会发生内存泄露,不会有多个MainActivity实例。后来测试下发现自己错了。该Message在消息队列里面无限循环,导致点击按钮都没有反应。后来将MainActivity.java的内容改为下面这样,


    public classMainActivityextendsAppCompatActivity{

    MyHandlermyHandler;

    HandlerThreadhandlerThread;

    @Override

    protected voidonCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_main);

    Buttonbutton=(Button)findViewById(R.id.button);

    handlerThread=newHandlerThread("plu");

    handlerThread.start();

    myHandler=newMyHandler(MainActivity.this,handlerThread.getLooper());

    myHandler.sendEmptyMessageDelayed(0,10*1000);

    button.setOnClickListener(newView.OnClickListener() {

    @Override

    public voidonClick(View v) {

    Intentintent= newIntent(MainActivity.this,Main2Activity.class);

    startActivity(intent);

    MainActivity.this.finish();

    }

    });

    }

    @Override

    protected voidonDestroy() {

    super.onDestroy();

    Log.v("PLU","-----onDestroy");

    isRunning=false;

    myHandler.removeCallbacksAndMessages(null);

    handlerThread.quit();

    }

    static classMyHandlerextendsHandler{

    WeakReferencewf;

    publicMyHandler(MainActivity mainActivity,Looper looper){

    super(looper);

    wf=newWeakReference(mainActivity);

    }

    @Override

    public voidhandleMessage(Message msg) {

    super.handleMessage(msg);

    MainActivitymainActivity=wf.get();

    // mainActivity.log();

    while(true) {

    mainActivity.log();

    }

    }

    }

    public voidlog(){

    Log.v("PLU","I AM HANDLER ");

    }

    }

    再来回切换Activity,发现仍然会有内存泄露。又陷入疑惑中,难道死循环导致即使使用WeakRefrence,置为静态内部类,也无法回收外部Activity吗,当然实际开发中是不会有这样的需求的。只是为了更好地理解。

    临时的结论是

    1.即使MAT中同一类存在多个实例,也不一定就是发生可内存泄露,也许是你获取hprof文件之前没有调用initiate gc操作,系统刚好还没有去回收并不代表着回收不掉。

    2.在执行死循环的内部类里面,即使将该内部类置为静态的,且使用若引用持有外部类引用,也会导致内存泄露。程序中一定要小心死循环。

    3.在Android Studio 点击dump java heap 时,要处于离开怀疑泄露类页面,且点击initiate gc 后再去获取hprof文件。

    相关文章

      网友评论

      本文标题:android内存泄露MAT分析心得与注意点

      本文链接:https://www.haomeiwen.com/subject/szssfttx.html