基本步骤:
1,准备一个有内存泄漏的代码
2,如何发现内存泄漏
3,生成.hprof
4,打开.hprof 文件开始分析
5,修复代码
1,准备一个有内存泄漏的代码
1 mport android.os.Bundle;
2 import android.support.v7.app.AppCompatActivity;
3
4 import java.util.ArrayList;
5
6 public class InnerClassLeaksActivity extends AppCompatActivity {
7
8 private ArrayList<String> list = new ArrayList<String>();
9
10 @Override
11 protected void onCreate(Bundle savedInstanceState) {
12 super.onCreate(savedInstanceState);
13 setContentView(R.layout.activity_inner_class_leaks);
14 //模拟Activity一些其他的对象
15 for (int i = 0; i < 10000; i++) {
16 list.add("Memory Leak!");
17 }
18 //开启线程
19 new InnerClassHasLeak().start();
20 }
21
22 public class InnerClassHasLeak extends Thread{
23
24 @Override
25 public void run() {
26 super.run();
27 //模拟耗时操作
28 try {
29 Thread.sleep(10 * 60 * 1000);
30 } catch (InterruptedException e) {
31 e.printStackTrace();
32 }
33 }
34 }
35 }
2,如何发现内存泄漏
- 打开 android-sdks/tools/monitor 工具
- 选中想要检测的程序,然后按 Update Heap
- 进入到想要检测的程序模块,点 Cause GC
- 旋转屏幕,每运行一会后再按一次 Cause GC 按钮。重复进入该模块执行旋转,GC。
Cause GC是手动产生一次GC清理下内存,如果多次GC后,可用部分内存变小,使用部分变多,说明有明显的内存泄漏。
其中 Free变少,% Used变大 说明有内存泄漏。
3,生成.hprof
- 点击 Dump HPROF file 按钮生成 .hprof 文件
- 用 android-sdks/platform-tools/hprof-conv 工具 将生成的 xx.hprof 转为标准格式的 xxx.hprof
hprof-conv xx.hprof xxx.hprof
4,打开.hprof 文件开始分析
a,在打开.hprof 文件时,在向导页面选 Leak Suspects Report
b,在概述页面可以在全局角度查看内存使用情况
c,进入内存泄漏详细报告页面
Keywords 部分.
其中Suspect 1 有两个 java.lang.Object[] 和 android.content.res.Resources ,本文检测第一个关键字。
d,生成柱状图
e,开始定位一个个可疑泄漏,在第一行 <Regex>中输入第一个关键字 java.lang.Object[]
f,在java.lang.Ojbect[]上 点 Merge Shortest Paths to GC Roots -> exclude all phantom/weak/soft etc.references
Merge Shortest Paths to GC Roots 可以查看一个对象到RC Roots是否存在引用链相连接, 在JAVA中是通过可达性(Reachability Analysis)来判断对象是否存活,这个算法的基本思想是通过一系列的称谓"GC Roots"的对象作为起始点,从这些节点开始向下搜索,搜索所走得路径称为引用链,当一个对象到GC Roots没有任何引用链相连则该对象被判定为可以被回收的对象,反之不能被回收,我们可以选择 exclude all phantom/weak/soft etc.references(排查虚引用/弱引用/软引用等)因为被虚引用/弱引用/软引用的对象可以直接被GC给回收.
g,可以看到LeakActivity存在GC Roots链,即存在内存泄露问题,可以看到 InnerClassLeaksActivity被 InnerClassHasLeak 的this$0持有。
除了使用Merge Shortest Paths to GC Roots 我们还可以使用
- List object - With outgoing References 显示选中对象持有那些对象
- List object - With incoming References 显示选中对象被那些外部对象所持有
- Show object by class - With outgoing References 显示选中对象持有哪些对象, 这些对象按类合并在一起排序
- Show object by class - With incoming References 显示选中对象被哪些外部对象持有, 这些对象按类合并在一起排序
5,修复代码
我们就外部类或者static的内部类,如果我们需要用到外部类里面的一些东西,我们可以将外部类Weak Reference传递进去
1 import android.os.Bundle;
2 import android.support.v7.app.AppCompatActivity;
3
4 import java.lang.ref.WeakReference;
5 import java.util.ArrayList;
6
7 public class InnerClassLeaksActivity extends AppCompatActivity {
8
9 private ArrayList<String> list = new ArrayList<String>();
10
11 @Override
12 protected void onCreate(Bundle savedInstanceState) {
13 super.onCreate(savedInstanceState);
14 setContentView(R.layout.activity_inner_class_leaks);
15 //模拟Activity一些其他的对象
16 for (int i = 0; i < 10000; i++) {
17 list.add("Memory Leak!");
18 }
19 //开启线程
20 new InnerClassHasLeak().start();
21 }
22
23 public class InnerClassHasLeak extends Thread{
24
25 @Override
26 public void run() {
27 super.run();
28 //模拟耗时操作
29 try {
30 Thread.sleep(10 * 60 * 1000);
31 } catch (InterruptedException e) {
32 e.printStackTrace();
33 }
34 }
35 }
36 public static class StaticInnerClassNoLeak extends Thread{
37 private WeakReference<InnerClassLeaksActivity> mLeakActivityRef;
38
39 public StaticInnerClassNoLeak(InnerClassLeaksActivity activity){
40 mLeakActivityRef = new WeakReference<InnerClassLeaksActivity>(activity);
41 }
42 @Override
43 public void run() {
44 super.run();
45 //模拟耗时操作
46 try {
47 Thread.sleep(10 * 60 * 1000);
48 } catch (InterruptedException e) {
49 e.printStackTrace();
50 }
51 //如果需要使用LeakActivity,我们需要添加一个判断
52 InnerClassLeaksActivity activity = mLeakActivityRef.get();
53 if(activity != null){
54 //do something
55 }
56 }
57 }
58 }
不光 Thread有这个问题,Handler也有。