Android 内存泄露分析
来源:互联网 发布:淘宝客服该怎么做 编辑:程序博客网 时间:2024/05/18 17:45
编程工具:Android Studio
分析内存工具:Android Studio和MAT
内存泄露的例子:
// MainActivity类public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } public void openNextActivity(View view) { // 跳转到内存泄露的Activity startActivity(new Intent(this, SecondActivity.class)); }}// SecondActivity类 : 存在内存泄露public class SecondActivity extends AppCompatActivity { private List<Student> dataList = new ArrayList<>(); @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_second); // 模拟储存10000个数据,这样更分析更明显 for (int i = 0; i < 10000; i++) { dataList.add(new Student("test" + i, i, "school" + i)); } new Thread(runnable).start(); } public static class Student { public String name; public int age; public String school; public Student(String name, int age, String school) { this.name = name; this.age = age; this.school = school; } } // 内存类,依赖外部类,持有外部类SecondActivity实例 private Runnable runnable = new Runnable() { @Override public void run() { // 模拟延时 try { Thread.sleep(100 * 60 * 60 * 1000); } catch (InterruptedException e) { e.printStackTrace(); } } };}
初步分析
我们可以利用Android Studio的【Monitors窗口】查看应用内存情况:
用于对比,先把存在内存泄露的Activity恢复正常:
// SecondActivity类 : 不存在内存泄露问题public class SecondActivity extends AppCompatActivity { private List<Student> dataList = new ArrayList<>(); @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_second); // 模拟储存10000个数据,这样更分析更明显 for (int i = 0; i < 10000; i++) { dataList.add(new Student("test" + i, i, "school" + i)); } } public static class Student { public String name; public int age; public String school; public Student(String name, int age, String school) { this.name = name; this.age = age; this.school = school; } }}
然后反复启动10-20次SecondActivity,内存使用情况是这样的:
从结果可以看到,几次GC之后,内存可以正常被回收。
我们来看看SecondActivity存在内存泄露的情况:
对比上一个图,几次GC之后,程序占用的内存一直上升,说明本该被回收的内存一直没有被释放。初步分析,存在内存泄露问题。
接下来我详细分析哪里存在内存泄露。
进一步分析
Android Studio自带的界面,查看内存泄露还不是很智能,我们可以借助第三方工具。
常见的工具就是MAT了,下载地址 http://eclipse.org/mat/downloads.php ,这里我们需要下载独立版的MAT。
生成hprof文件
hprof文件可以让Android Studio帮我们生成,具体做法是:
启动程序,MainActivity中点击按钮跳转到SecondActivity(初步分析,存在内存泄露),返回点击返回键,然后点击按钮跳转到SecondActivity,再点击返回,反复几次,然后点击【Monitors窗口】下【Momery】右边的【Dump Java Heap】按钮:
此时hprof文件已经生成了,在【Captrues窗口】可以找到:
但是此时的hprof文件还不能被MAT识别,我们右击我们需要的hprof文件,导出标准的hprof文件:
分析hprof文件
我们用MAT分析内存工具打开导出的hprof文件:
打开之后,点击工具栏中的直方图图标查看内存详细情况(就是哪些类使用了多少内存):
打开的界面会显示所有的内存使用情况,所以需要我们过滤一下(一般关键字为包名):
过滤之后显示的界面是这样的:
我们从这里可以看到,因为SecondActivity被我启动了7次,产生了7个SecondActivity实例,而且都没有回收,因为每个SecondActivity实例都保存了10000个Student实例,所以Student实例有70000个,我们查查SecondActivity为什么没有被回收。
右击【com.johan.demo.SecondActivity】,选择【Merge Shortest Paths to GC Roots】,然后选择【with all reference】:
然后展开一个查看:
发现SecondActivity实例被Thread匿名内部类的this$0引用,Thread还存活,所以持有的SecondActivity实例也认为是“存活”的,所以GC不能回收,造成内存泄露。
这就找到了内存泄露的原因!!
SecondActivity内存泄露的原因是静态内部类持有外部类导致的,我们这么改造一下:
public class SecondActivity extends AppCompatActivity { private List<Student> dataList = new ArrayList<>(); @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_second); for (int i = 0; i < 10000; i++) { dataList.add(new Student("test" + i, i, "school" + i)); } new Thread(new MyRunnable()).start(); } public static class Student { public String name; public int age; public String school; public Student(String name, int age, String school) { this.name = name; this.age = age; this.school = school; } } // 声明为静态内部类 public static class MyRunnable implements Runnable { @Override public void run() { // 模拟延时 try { Thread.sleep(100 * 60 * 60 * 1000); } catch (InterruptedException e) { e.printStackTrace(); } } }}
我们重新导出hprof文件,查看内存详细情况:
如图,MyRunnable对象有7个,证明SecondActivity已经启动了7次,而SecondActivity只剩下一个(应该是还没有来得回收),所以SecondActivity实例可以被GC正常回收了,内存泄露问题得以解决!!其实这里还有泄露,就是MyRunnable,我们关闭SecondActivity的时候,应该中断线程。不过还是建议使用线程池,靠谱!
LeakCanary
目前除了导出hprof文件分析内存泄露,还有一种更简单的办法,就是在程序代码中集成LeakCanary。
项目地址:https://github.com/square/leakcanary
有兴趣的自己试一下!!!
- Android内存泄露分析
- android内存泄露分析
- Android内存泄露分析
- Android内存泄露分析
- Android 内存泄露分析
- Android 内存泄露 分析
- android内存泄露分析
- Android内存泄露分析
- Android 内存泄露分析
- Android内存泄露分析
- Android内存泄露分析
- Android 内存泄露分析
- android 内存泄露分析
- Android 内存泄露 案例分析
- android内存泄露分析-MAT
- Android内存泄露与分析
- android开发内存泄露分析
- MAT分析Android内存泄露
- 找出两个不相交连续子数组的最大和
- 《Python全栈开发》学习过程笔记【3】
- input requried 提示文字修改
- js模拟QQ好友列表的实现
- 第十一届湖南大学生程序设计竞赛 阶乘除法(思维)
- Android 内存泄露分析
- Quick Sorting/Extract
- Extjs6.2 Form中组件的使用及正则验证
- SQL 如何创建索引
- 切片上传视频
- maven下载配置
- checkstyle直接用java命令,不调用eclipse等IDE
- 几款适合vue移动端的第三方小组件
- 在Hibernate中@ManyTOMany 单/双向外键关联