一概要:
内存泄漏是一些已经不使用的对象,依然占有内存且垃圾回收机制不法回收它们。最终导致常驻内存越来越大
,影响到程序的性能。
在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。
欢迎指正!