MAT分析android内存泄漏
转载请标明出处:https://www.cnblogs.com/tangZH/p/10955429.html
泄漏,泄漏,漏~
内存泄漏怎么破,什么是内存泄漏?与内存溢出有什么区别?
内存泄漏(Memory Leak):是指程序中己动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。
内存溢出(out of memory):是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory;
内存泄漏不一定会引起奔溃,但是内存溢出一定会
Java里头有GC垃圾回收机制,他是怎么判断该不该回收呢?
Q1:某对象没有任何引用的时候才进行回收?
A:不。无法往上追溯到GCroot引用点的才回收。
Q2:某对象被别的对象引用就不能进行回收?
A:不。软引用,弱引用,虚引用都可以
哪些可以作为GCroot引用点:
- Javastack中引用的对象
- 方法区中静态引用指向的对象
- 方法区中常量引用指向的对象
- Native方法中JNI引用指向的对象
- Thread-活着的线程
adb命令验证是否存在内存泄漏:
1、打开要测试的apk,然后返回退出到主界面
2、AS的Terminal中输入命令:
adb shell dumpsys meminfo com.status.mattest -d
com.status.mattest为包名
然后便可以看到内存的一些情况
拉到下面可以看到:
我们退出APK之后,对象本应该都被回收,然而这里可以看到,还有view以及Activity占用着内存,由此可以知道内存泄漏了。
我们点击AS上的按钮,进行分析。
运行apk后会出现该界面:
我们点击MEMORY进入内存分析界面:
这时候我们需要进行刚刚的操作,打开apk,然后返回键退出,然后点击上图中垃圾桶形状的图标进行垃圾回收,多点几次,直到内存没有什么变化了。
然后点击上图中类似于下载按钮的图标获取内存快照。
获取完之后,左边会出现下面这图,Head Dump便是获取内存快照后出现的,我们可以点击红圈中的图形进行保存
跟着出现的还有这个窗口。
我们可以通过包来分类查看自己的项目.
Shallow Heap(浅堆) 表示该对象自身占用的堆内存,不包括它引用的对象。
针对非数组类型的对象,它的大小就是对象与它所有的成员变量大小的总和。
Retained Heap(深堆) 表示当前对象大小+当前对象可直接或间接引用到的对象的大小总和。
换句话说,Retained Size就是当前对象被GC后,从Heap上总共能释放掉的内存。
不过,释放的时候还要排除被GC Roots直接或间接引用的对象。他们暂时不会被被当做Garbage。
从图中可以看出,出现了内存泄漏的是CustomUtils,MainActivity,MainActivity$1代表MaiActivity里面的一个方法。
点击MainActivity,可以看到这么对东西,眼花缭乱,我们根本不知道是哪个引起了内存泄漏。那咋办。铺垫结束,mat该上场了。
MAT
下载mat https://www.eclipse.org/mat/downloads.php
安装步骤很简单。
打开MAT后,点击File -> Open Heap Dump 打开刚刚保存的内存快照,会发现打不开。
因为我们保存的内存快照是不能直接在MAT上打开的,需要进行转化。
在AS的Terminal窗口输入:hprof-conv -z A.hprof B.hprof
A.hprof为刚刚保存后的快照文件,B.hprof为转换后的文件,也就是我们要在MAT上打开的文件
然后我们在MAT上将其打开。
点击Finish
点击Histogram
可以通过包名来分类查看
从下图中可以看到引起内存泄漏的类,Objects那一列不为0的就是,与我们之前看到的一样。
MainActivity右键
选择图中的选项,意思是过滤掉虚引用,软引用,弱引用
之后可以看到引用的路径,CustomUtils里面的instance引用了MainActivity的context,导致了MainActivity内存泄漏。
打开代码看一下
package com.status.mattest; import android.content.Context; public class CustomUtils { private static CustomUtils instance; private CustomUtils(Context context) { this.mContext = context; } private Context mContext; public static CustomUtils getInstance(Context context) { if (instance == null) { instance = new CustomUtils(context); } return instance; } }
MainActivity中:
package com.status.mattest; import android.content.Intent; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.TextView; public class MainActivity extends AppCompatActivity { private TextView textView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); CustomUtils customUtils = CustomUtils.getInstance(this); textView = findViewById(R.id.tv); textView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { startActivity(new Intent(MainActivity.this, Test1Activity.class)); } }); } }
MainActivity中调用了单例CustomUtils,并且把context传了进去,而我们知道instance作为静态对象,是GCroot引用点,所以activity关闭了它也没法被回收,那么最为被instance引用的Activity自然也无法被回收,所以导致了内存泄漏。
解决:
把单例模式里面的context换为全局application的context,也就是说单例里面的context的周期应该与进程一样,而不能够与应用一样。当我们关闭应用的时候,进程还在,并没有被杀死。