Android内存管理(12)*「实例」用Monitor 生成.hprof文件 并分析内存泄漏

参考 

  http://blog.csdn.net/xiaanming/article/details/42396507

 

基本步骤: 

  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也有。

 

posted @ 2016-04-03 23:11  f9q  阅读(614)  评论(0编辑  收藏  举报