您的位置:首页 > 移动开发 > Android开发

AndroidStudio + MAT 内存泄漏分析

2017-07-13 17:11 351 查看
一概要:

内存泄漏是一些已经不使用的对象,依然占有内存且垃圾回收机制不法回收它们。最终导致常驻内存越来越大

,影响到程序的性能。

在Android 虚拟机中,采用Mark-Sweep方式实现垃圾回收。Mark标记,Sweep检测。虚拟机会从GC Roots开始

遍历,如果某个节点无法找到一条到达GC Roots的路径,则表示该引用无效,可以被回收。内存泄漏就是存在一些

不好的调用,导致一些无效的引用于GC Roots相连,无法被回收。

知道了原因,我们理应在开发中尽量避免这些。但是万一发生了(代码又不是一个人写的),我们要准确找出它们。

所以我们就要用到检测内存泄漏的工具。

转载:http://blog.csdn.net/u012760183/article/details/52068490

二使用:

实例代码(我们进入主页面后,点击启动TestSecondActivity,然后按返回退出):

Intent intent2 = new Intent(MainActivity.this, TestSecondActivity.class);
startActivity(intent2);


public class TestSecondActivity extends AppCompatActivity{

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_testsecond);
Runnable runnable = new Runnable() {
@Override
public void run() {
LogUtil.i("TestSE", "thread:" + Thread.currentThread() + "start");
try{
Thread.sleep(80000l);
}catch (Exception e){

}
LogUtil.i("TestSE", "thread:" + Thread.currentThread() + " end");
}
};
new Thread(runnable).start();
}
}

我们会通过两种方式,来检测内存泄漏:

#使用MAT用具查找

1,首先打开Android Studio中的Android Device Monitor如下图。



2,打开后会出现下面界面,选中你要检测应用的包名,然后点击下图画圈的地方(Update Heap),会在程序包

名后标记一个图标



3,接下就是我们的操作,,重复启动回退TestSecondActivity 两次,然后再点下图画圈地方(Dump HPROF file)

生成hprof文件。



生成的hprof文件。



4,因为MAT是用于分析java hprof的工具,我们生成的是Android属性的hprof文件,所以我们要转化一下(我将刚刚

生成的文件重名android.hprof)。

Android Sdk中为我们提供了工具:hprof-conv.exe。



转化成了java属性的文件java.hprof。

5,使用MAT打开hprof文件,(如果还没有安装MAT的,先下载安装:https://www.eclipse.org/mat/)。



打开后如图:



6,之后我们查看内存中的对象,由于我们的内存泄漏一般都发生在Activity中所以我们之查找Activity即可。

点击下图中的QQL图标,输入select * from instanceof android.app.Activity,类似于SQL语句。红色感叹号为执行按钮。



#内存中有两个TestSecondActivity,但是我们已经退出了TestSecondActivity,说明发生了内存泄漏。

#上面有两个属性。Shallow Size对象自身占用大小,不包括引用。Retained Size对象自身大小+ 直接或间接引用对象

的大小。

7,右击TestSecondActivity,—— Path to GC Roots——All Reference。



看到this$0引用了这个Activity,this$0表示内部类意思。Activity被一个内部类引用,而这个内部类又被target(Thread)引用。

从上面的代码可知:

TestSecondActivity中有内部类runnable,而runnable被Thread使用。工作线程Thread需要sleep 80秒。所以导致

TestSecondActivity无法释放。

解决方法。我们在Activity onDestroy中shutdown 线程Thread就可以了(当然先把Thread变成全局变量)

#直接使用Android Studio上的Monitor Memory查找

1,依然是利用上面的代码,运行程序,打开Android Studio的Monitor界面,查看Memory图像。



2,点击下卡车图标(图中1),可以执行一次GC;点击图中2的位置,可以查看hprof文件。(依次点击1,2)

3,查看结果



#同样监听到了TestSecondActivity的内存泄漏。

三注意

Android中内存引起内存泄漏的常见使用。

1,static变量引起的内存泄漏。

因为static生命周期是类加载时开始,类卸载结束。也就是说static变量在程序死亡时才结束。而如果static变量引用

Activity,那么Activity的资源将一直得不到释放。造成内存泄漏。

解决方法,如果需要Context对象,尽量使用getApplicationContext。假如必须使用Activity那么最好在onDestroy方

法中,将Activity引用设置为null。

2,线程造成的内存泄漏。

类似于上面的例子,线程执行时间长,即使Activity结束了任然在执行。因为new Thread生成一个匿名内部类(内部

类的创建必须依靠外部类),因此握有Activity实例,Activity无法释放。AsyncTask更为严重,因为AsyncTask维持一

个生命周期。

解决方法:合理安排线程,控制线程在Activity结束时结束。

3,Bitmap占用过多的内存。

Bitmap解析需要占用内存,但是Android只提供了8M内存给Bitmap,如果Bitmap过多且不能及时的回收。则会造成内

存溢出。

解决方法:及时recycle,加载图片前先适当的压缩图片。

4,资源未被及时关闭造成内存泄漏

例如Cursor,没有及时关闭,会因持有Activity引用,造成内存泄漏。

解决方法:在onDestroy中及时的关闭。

5,Handler的使用

Handler对象会发送Message到MessageQueue,Looper对象会轮询MessageQueue对象去除Message执行。如果

Message对象长时间未被取出执行。则Message对象持有Handler对象,Handler对象持有Activity(可能)。造成泄漏。

解决方法,在onDestroy中,Handler removeMessage。

欢迎指正!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: