参考

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

基本步骤:

  1,准备一个有内存泄漏的代码

  2,如何发现内存泄漏

  3,生成.hprof

  4,打开.hprof 文件开始分析

  5,修复代码

1,准备一个有内存泄漏的代码

 mport android.os.Bundle;
import android.support.v7.app.AppCompatActivity; import java.util.ArrayList; public class InnerClassLeaksActivity extends AppCompatActivity { private ArrayList<String> list = new ArrayList<String>(); @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_inner_class_leaks);
//模拟Activity一些其他的对象
for (int i = ; i < ; i++) {
list.add("Memory Leak!");
}
//开启线程
new InnerClassHasLeak().start();
} public class InnerClassHasLeak extends Thread{ @Override
public void run() {
super.run();
//模拟耗时操作
try {
Thread.sleep( * * );
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

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传递进去

  

 import android.os.Bundle;
import android.support.v7.app.AppCompatActivity; import java.lang.ref.WeakReference;
import java.util.ArrayList; public class InnerClassLeaksActivity extends AppCompatActivity { private ArrayList<String> list = new ArrayList<String>(); @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_inner_class_leaks);
//模拟Activity一些其他的对象
for (int i = ; i < ; i++) {
list.add("Memory Leak!");
}
//开启线程
new InnerClassHasLeak().start();
} public class InnerClassHasLeak extends Thread{ @Override
public void run() {
super.run();
//模拟耗时操作
try {
Thread.sleep( * * );
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static class StaticInnerClassNoLeak extends Thread{
private WeakReference<InnerClassLeaksActivity> mLeakActivityRef; public StaticInnerClassNoLeak(InnerClassLeaksActivity activity){
mLeakActivityRef = new WeakReference<InnerClassLeaksActivity>(activity);
}
@Override
public void run() {
super.run();
//模拟耗时操作
try {
Thread.sleep( * * );
} catch (InterruptedException e) {
e.printStackTrace();
}
//如果需要使用LeakActivity,我们需要添加一个判断
InnerClassLeaksActivity activity = mLeakActivityRef.get();
if(activity != null){
//do something
}
}
}
}

  

  不光 Thread有这个问题,Handler也有。

最新文章

  1. SQL Server数据库转换成oracle
  2. JAVA 十六进制与字符串的转换
  3. sql order by按俩个字段排序
  4. javascript中获取非行间样式的方法
  5. windows server 2008 R2 忘记administrator密码
  6. Finding LCM (最小公倍数)
  7. vue.js初学,笔记1,安装
  8. Python 爬虫性能相关
  9. onblur 事件
  10. 回文数的golang实现
  11. bat cmd 获取管理员权限
  12. WCF类型共享技巧【转载】
  13. POJ-3252 Avenger
  14. Android-Kotlin-递归与尾递归
  15. [转]腾讯云Linux云服务器文件上传利器——WinSCP
  16. WIN10系统 截图或者某些程序时屏幕会自动放大怎么办
  17. Centos 安装GIT 1.7.1
  18. 【翻译】JavaScript循环和作用域
  19. Android解析WindowManager(三)Window的添加过程
  20. 【CTF MISC】文件内容反转方法-2017世安杯CTF writeup详解

热门文章

  1. 第十四节:Web爬虫之Ajax数据爬取
  2. BZOJ 1782 洛谷 2982 [Usaco2010 Feb]slowdown 慢慢游
  3. HDU 4780 Candy Factory
  4. Codeforces Round #246 (Div. 2) D. Prefixes and Suffixes
  5. ECMAScript 6 入门学习笔记(二)——变量的解构赋值
  6. ArcGIS AO中控制图层中要素可见状态的总结
  7. Oracle Multitenant Environment (五) Create PDB
  8. Username is not in the sudoers file. This incident will be reported
  9. 《从零開始学Swift》学习笔记(Day 55)——使用try?和try!差别
  10. QT如何修改字符编码格式