Android Studio +MAT 分析内存泄漏实战
对于内存泄漏,在Android中如果不注意的话,还是很容易出现的,尤其是在Activity中,比较容易出现,下面我就说下自己是如何查找内存泄露的。
首先什么是内存泄漏?
内存泄漏就是一些已经不使用的对象还存在于内存之中且垃圾回收机制无法回收它们,导致它们常驻内存,会使内存消耗越来越大,最终导致程序性能变差。
其中在Android虚拟机中采用的是根节点搜索算法枚举根节点判断是否是垃圾,虚拟机会从GC
Roots开始遍历,如果一个节点找不到一条到达GC Roots的路线,也就是没和GC Roots
相连,那么就证明该引用无效,可以被回收,内存泄漏就是存在一些不好的调用导致一些无用对象和GC Roots相连,无法被回收。
既然知道了什么是内存泄漏,自然就知道如何去避免了,就是我们在写代码的时候尽量注意产生对无用对象长时间的引用,说起来简单,但是需要足够的经验才能达到,所以内存泄漏还是比较容易出现的,既然不容易完全避免,那么我们就要能发现程序中出现的内存泄漏并修复它,
下面我就说说如何发现内存泄漏的吧。
查找内存泄漏:
比如说下面这个代码:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
String string = new String();
}
public void click(View view){
Intent intent = new Intent();
intent.setClass(getApplicationContext(),SecondActivity.class);
startActivity(intent);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
public class SecondActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
Thread.sleep(8000000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
new Thread(runnable).start();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
每次跳转到这个Activity中时都会调用一个线程,然后这个线程会执行runnable的run方法 由于Runnable是一个匿名内部对象 所以握有SecondActivity的引用,因此
很简单的两个Activity,可由MainActivity跳转到SecondActivity中,
下面我们从MainActivity跳到SecondActivity
然后从SecondActivity返回MainActivity,连续这样5次
,最终返回MainActivity,按照常理来说,我们从SecondActivity返回MainActivity之后
SecondActivity就该被销毁回收,可实际可能并不是这样。
这时候要判断发没发生内存溢出就要使用工具了!下面有两种方式
1.利用MAT工具查找
首先打开AS中的Android Device Monitor工具 具体位置如下图:
打开后会出现如下的界面
先选中你要检测的应用的包名,然后点击下图画圈的地方,会在程序包名后标记一个图标
接下来要做的就是操作我们的app 来回跳转5次。
之后点击下图的图标 就可导出hprof文件进行分析了
导出文件如下图所示:
得到了hprof文件 我们就可以利用MAT工具进行分析了,
打开MAT工具
如果没有 可以在下面网址下载
MAT工具下载地址
界面如下图所示:
打开我们先前导出的hprof文件 ,不出意外会报下面的错误
这是因为MAT是用来分析Java程序的hprof文件的 与Android导出的hprof有一定的格式区别,因此我们需要把导出的hprof文件转换一下,sdk中提供给我们转换的工具 hprof-conv.exe 在下图的位置
接下来我们cd到这个路径下执行这个命令转换我们的hprof文件即可,如下图
其中 hprof-conv 命令 这样使用
hprof-conv 源文件 输出文件
比如 hprof-conv E:\aaa.hprof E:\output.hprof
就是 把aaa.hprof 转换为output.hprof输出 output.hprof就是我们转换之后的文件,图中 mat2.hprof就是我们转换完的文件。
接下来 我们用MAT工具打开转换之后的mat2.hprof文件即可 ,打开后不报错 如下图所示:
之后我们就可以查看当前内存中存在的对象了,由于我们内存泄漏一般发生在Activity中,因此只需要查找Activity即可。
点击下图中标记的QQL图标 输入 select * from instanceof android.app.Activity
类似于 SQL语句 查找 Activity相关的信息 点击 红色叹号执行后 如下图所示:
接下来 我们就可以看到下面过滤到的Activity信息了
如上图所示, 其中内存中还存在 6个SecondActivity实例,但是我们是想要全部退出的,这表明出现了内存泄漏
其中 有 Shallow size 和 Retained Size两个属性
Shallow Size
对象自身占用的内存大小,不包括它引用的对象。针对非数组类型的对象,它的大小就是对象与它所有的成员变量大小的总和。
当然这里面还会包括一些java语言特性的数据存储单元。针对数组类型的对象,它的大小是数组元素对象的大小总和。
Retained Size
Retained Size=当前对象大小+当前对象可直接或间接引用到的对象的大小总和。(间接引用的含义:A->B->C, C就是间接引用)
不过,释放的时候还要排除被GC Roots直接或间接引用的对象。他们暂时不会被被当做Garbage。
接下来 右击一个SecondActivity
选择 with all references
打开如下图所示的页面
查看下图的页面
看到 this0引用了这个Activity而this0是表示
内部类的意思,也就是一个内部类引用了Activity 而 this$0又被 target引用
target是一个线程,原因找到了,内存泄漏的原因 就是 Activity被 内部类引用 而内部类又被线程使用
因此无法释放,我们转到这个类的代码处查看
public class SecondActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
Thread.sleep(8000000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
new Thread(runnable).start();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
确实 在 SecondActivity中 存在Runnable 内部类对象,然后又被线程 使用,而线程要执行8000秒 因此 SecondActivity对象被引用 无法释放,导致了内存溢出。
要解决这种的内存溢出,要及时在Activity退出时结束线程(不过不大好结束。。),或者良好的控制线程执行的时间即可。
这样我们就找出了这个程序中的内存溢出。
2.直接利用Android Studio的 Monitor Memory 查找内存溢出
还是利用上面那个程序,我就简单点说了。
首先 在手机上运行程序,打开AS的 Minotor 界面 查看Memory 图像
点击 小卡车图标(图中1位置图标) 可以触发一次 GC
点击 图中2位置图标可以查看hprof文件
左边是 内存中的对象,在里面找 Activity 看存不存在我们希望已经回收的Activity 如果
出现我们期望已经回收的Activity,单击 就会在右边显示它的总的个数,点击右边的某个,可以显示 它的GC Roots的树关系图
,查看关系图就可以找出发生内存泄漏的位置(类似于第一种方式)
这样就完成了内存泄漏的查找。
其中内存泄漏产生的原因在Android中大致分为以下几种:
1.static变量引起的内存泄漏
因为static变量的生命周期是在类加载时开始 类卸载时结束,也就是说static变量是在程序进程死亡时才释放,如果在static变量中
引用了Activity 那么 这个Activity由于被引用,便会随static变量的生命周期一样,一直无法被释放,造成内存泄漏。
解决办法:
在Activity被静态变量引用时,使用 getApplicationContext 因为Application生命周期从程序开始到结束,和static变量的一样。
2.线程造成的内存泄漏
类似于上述例子中的情况,线程执行时间很长,及时Activity跳出还会执行,因为线程或者Runnable是Acticvity内部类,因此握有Activity的实例(因为创建内部类必须依靠外部类),因此造成Activity无法释放。
AsyncTask 有线程池,问题更严重
解决办法:
1.合理安排线程执行的时间,控制线程在Activity结束前结束。
2.将内部类改为静态内部类,并使用弱引用WeakReference来保存Activity实例 因为弱引用 只要GC发现了 就会回收它 ,因此可尽快回收
3.BitMap占用过多内存
bitmap的解析需要占用内存,但是内存只提供8M的空间给BitMap,如果图片过多,并且没有及时 recycle bitmap 那么就会造成内存溢出。
解决办法:
及时recycle 压缩图片之后加载图片
4.资源未被及时关闭造成的内存泄漏
比如一些Cursor 没有及时close 会保存有Activity的引用,导致内存泄漏
解决办法:
在onDestory方法中及时 close即可
5.Handler的使用造成的内存泄漏
由于在Handler的使用中,handler会发送message对象到 MessageQueue中 然后
Looper会轮询MessageQueue 然后取出Message执行,但是如果一个Message长时间没被取出执行,那么由于
Message中有 Handler的引用,而 Handler 一般来说也是内部类对象,Message引用 Handler ,Handler引用
Activity 这样 使得 Activity无法回收。
解决办法:
依旧使用 静态内部类+弱引用的方式 可解决
其中还有一些关于 集合对象没移除,注册的对象没反注册,代码压力的问题也可能产生内存泄漏,但是使用上述的几种解决办法一般都是可以解决的。
参考资料:
使用Android studio分析内存泄露
Android Studio +MAT 分析内存泄漏实战的更多相关文章
- 使用MAT 分析内存泄漏实战
在分析前可以阅读下这篇文章,很多内容就都知道了https://www.jianshu.com/p/738b4f3bc44b,下面我来分析上一篇文章内存泄漏的为问题 1.上一篇中已经生成了hprf的文件 ...
- 【转】如何使用MAT分析内存泄漏
原文链接:http://www.lightskystreet.com/2015/09/01/mat_usage/ MAT - Memory Analyzer Tool 使用进阶 Sep 1, 2015 ...
- 使用Memory Analyzer tool(MAT)分析内存泄漏(二)
转载自:http://www.blogjava.net/rosen/archive/2010/06/13/323522.html 前言的前言 写blog就是好,在大前提下可以想说什么写什么,不像投稿那 ...
- 使用Memory Analyzer tool(MAT)分析内存泄漏
前言的前言 写blog就是好,在大前提下可以想说什么写什么,不像投稿那么字字斟酌.上周末回了趟成都办事,所以本文来迟了.K117从达州经由达成线往成都方向走的时候,发现铁路边有条河,尽管我现在也不知道 ...
- 性能监控 | MAT分析内存泄漏
使用MAT分析内存泄漏(二)八周年重印版 - 知乎 .u-safeAreaInset-top { height: constant(safe-area-inset-top) !important; h ...
- 使用android studio检测app内存泄漏【转载】
Android开发中难免会遇到各种内存泄漏,如果不及时发现处理,会导致出现内存越用越大,可能会因为内存泄漏导致出现各种奇怪的crash,甚至可能出现因内存不足而导致APP崩溃. 一般检测android ...
- android 中如何分析内存泄漏
转载:http://blog.csdn.net/fulinwsuafcie/article/details/8363218 前提条件: 1,电脑安装了java 运行环境 2,手机端开启了 USB 调试 ...
- 使用Memory Analyzer tool(MAT)分析内存泄漏(一)
转载自:http://www.blogjava.net/rosen/archive/2010/05/21/321575.html 前言 在平时工作过程中,有时会遇到OutOfMemoryError,我 ...
- Memory Analyzer tool(MAT)分析内存泄漏---理解Retained Heap、Shallow Heap、GC Root
Shallow Heap Size 指对象自身所占用的内存大小,不包含其引用的对象所占的内存大小. 1.数组类型 数组元素对象所占内存的大小总和. 2.非数组类型 对象与它所有的成员变量大小的总和.当 ...
随机推荐
- 06 JVM 是如何处理异常的
在 JAVA 中,异常处理的方式主要是抛出异常和捕获异常.这两大要素共同实现程序控制流的非正常转移. 抛出异常可以分为显示和隐式两种.显示抛出异常的主体是应用程序,它指的是在程序中使用 throw 关 ...
- Spring框架配置beans.xml扩展
Spring学习笔记(二) 续Spring 学习笔记(一)之后,对Spring框架XML的操作进行整理 1 什么是IOC(DI) IOC = inversion of control 控制反转 D ...
- mysql-Innodb事务隔离级别-repeatable read详解1
经验总结: Python使用MySQLdb数据库后,如使用多线程,每个线程创建一个db链接,然后再各自创建一个游标cursor,其中第一个线程读一个表中数据为空,第二个写入该表一条数据并提交,第一个线 ...
- 重做LVM文件系统之减小PV
命令 缩小PV空间到120G,即PV上的vg00已将缩小到120G pvresize --setphysicalvolumesize 120g /dev/sda2 背景 机器上有一块900G本地的本地 ...
- File(IO流)
import java.io.File; import java.io.IOException; import org.junit.Test; /** *java.io.File类 *1.凡是与输入输 ...
- MyEclipse生成get/set注释
我的外网链接 http://yezi-0016.iteye.com/blog/2290421
- autoprefixer小记
autoprefixer配置 // var aPostcss = [require('autoprefixer')({ browsers: ['ios>=3','android>=2',' ...
- 0-Android使用Ashmem机制进行跨进程共享内存
Android使用Ashmem机制进行跨进程共享内存 来源: http://blog.csdn.net/luoshengyang/article/details/6651971 导语: 在Androi ...
- pat 团体天梯赛 L2-012. 关于堆的判断
L2-012. 关于堆的判断 时间限制 400 ms 内存限制 65536 kB 代码长度限制 8000 B 判题程序 Standard 作者 陈越 将一系列给定数字顺序插入一个初始为空的小顶堆H[] ...
- 【字符集及字符编码】UTF-8、UTF-16和UTF-32
UTF-32 用 4 个字节存储每一个字符,以保证能把 UCS 完全表达出来.但实际上 UCS 的字符数量根本不需要用 32 位表示,UTF-32 极大地浪费了空间.另外,由于组合字符的存在,定长表示 ...