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

       最近工作项目中出现了内存泄露,于是找来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分析心得与注意点)