案例:(本篇还以上篇案例为例子)
public class CommUtil { private static CommUtil instance; private Context context; private CommUtil(Context context) { this.context=context; } public static CommUtil getInstance(Context context){ if(null==instance){ instance=new CommUtil(context); } return instance; }}复制代码
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); CommUtil instance = CommUtil.getInstance(this); }}复制代码
(将Activity的实例被一个单例对象所持有,在旋转屏幕的时候造成内存泄漏) 将项目运行起来 打开Android Profiler 观察Memory 并且试着多次旋转屏幕就会发现内存一直在增高
点击Dump java Heap生成一份内存快照hprof文件Dump java Heap: 点击就会生成app运行内存快照.hprof文件 然后将APP完全退出 重新启动 打开Android Monitor 点击Dump java Heap点击生成一份还没操作前(旋转屏幕)的内存快照hprof文件(为以后作对比用) 现在就已经生成好了2份hprof文件 一份是没有旋转过屏幕的 一份是旋转过屏幕多次的 然后选中Android Studio 最左边的Captures 进行将hprof文件导出(因为MAT不支持Android Studio生成的未经转换的hprof文件 在导出过程中Android Studio会为我们转换好)
MAT上场了
安装 Memory Analyzer 插件
导入生成的两个文件
这个时候可能会报错
Unknown HPROF Version (JAVA PROFILE 1.0.3) (java.io.IOException) 这是应为studio生成的.hprof文件和eclipse支持的.hprof文件格式问题
转换格式: 进入SDK目录输入 hprof-conv inputfilepath outputfilepath之后从新导入即可
先来看看MAT都有哪些功能:
Overview视图 该视图会首页总结出当前这个Heap dump占用了多大的内存,其中涉及的类有多少,对象有多少,类加载器,如果有没有回收的对象,会有一个连接,可以直接参看(图中的Unreachable Objects Histogram)。
histogram视图: 列举内存中对象存在的个数和大小 Dominator tree视图:该视图会以占用总内存的百分比来列举所有实例对象,注意这个地方是对象而不是类了,这个视图是用来发现大内存对象的 Top Consumers: 该视图会显示可能的内存泄漏点 Duplicate Classes: 该视图显示重复的类等信息 本次重点围绕histogram视图讲解 其他3个视图也只是给我提供一些可能会造成内存泄漏的信息 点击打开Histograms视图
打开下面的面板Navigation History 选中histogram右键add to Compare Basket 添加到比较容器中(2个hprof文件都做以上同样的操作 将histogram 添加到比较容器中)
这时比较容器中就有2份hprof文件 一份未做任何操作 一份做了多次旋转屏幕(注意:导入的比较容器的顺序很重要 不然后面的数据看起来让人费解)然后点击Compare the results (红色感叹号)进行比较
通过比较后就会生成一个比较结果表ComPared Tables
但是这个表内容太多 如何快速过滤出我们与我们自己写的项目内容有关的呢 接着在Class Name 输入我们的项目包名
通上图就明显看出经过2个hprof比较 在我们操作了旋转多次屏幕后MainActivity的实例增加了然后我们就去分析下该hprof文件是什么导致MainActivity内存泄漏了
在Class Name处可以根据输入的类名去查找对应的对象实例(比如我们输入MainActivty):
Objects:实例个数 Shallow Heap:所占内存大小 Retained Heap:释放后能回收多少内存 通过上图看到了MainActivtiy在旋转屏幕后产生了2个实例 再去观察MainActivtiy具体被哪些对象引用呢 鼠标选中MainActivtiy 实例右键 选择with incoming references:
看到MainActivty被引用的地方这么多 而且一屏还显示不完 我们又如何去判断是哪个导致内存泄漏的呢 MAT还有一个功能 就是通过遍历GC Root树去将那些有可能被GC回收的实例 将他们去除(备注:在GC Root树中能找到的对象绝对不存在有内存泄漏的实例 因为他们在运行时会被回收的嘛 只有找不到的那些才是)鼠标右键:
看那些单词就知道是什么意思 (排除 软引用 弱引用 虚引用) 最后我们就看到了Commutil的instance引用了MainActivty实例 而后旋转屏幕的时候 系统又创建了一个 所以有2个引用着MainActivtiy的实例 造成的内存泄漏:
然后就去修改代码: public class MainActivity extends AppCompatActivity {
@Overrideprotected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); CommUtil instance = CommUtil.getInstance(getApplicationContext()); }}复制代码