Android内存管理是由JVM进行管理的,不熟悉可以参考
http://note.youdao.com/yws/public/redirect/share?id=eabcd96832004fb98c00afd34b42cfde&type=false
http://note.youdao.com/yws/public/redirect/share?id=59ae46025f3eaac9eee144e8eb14f8d6&type=false
查看内存使用情况工具
eclipse中ddms的heap和as中的memory monitor都可以查看内存使用情况。
使用heap
查看内存检查的信息
点击Heap标签,切换到Head视图中。点击“CauseGC”按钮,此时在Heap视图中,就会看到当前选中的进程的内存使用量的详情。
点击“CauseGC“按钮相当于向虚拟机请求了一次gc操作。当内存使用信息第一次显示以后,无须再不断的点击“CauseGC“,Heap视图会定时刷新,在对应用不断的操作过程中就可以看到内存使用的变化。
为了寻找内存的性能问题,Android Studio提供了工具来帮助开发者。
Memory Monitor:查看整个app所占用的内存,以及发生GC的时刻,短时间内发生大量的GC操作是一个危险的信号。
什么是内存泄漏
这个话题很广,要从java内存管理说起。这里就不详细说了。
简单理解内存泄漏:一个对象还在存在对其的引用,但该对象已经不再被使用了,所以不能被gc回收,导致内存泄漏。
导致内存泄漏的原因,主要是全局的静态变量持有了对象的引用导致对象的生命周期发生了改变,或者是比该对象生命周期长的对象持有该对象的引用,都会导致内存不能被及时回收。
Android中主要的内存泄漏一般是activity的对象,activity的对象不能被系统回收导致,其内部的成员也成为了内存泄漏,包括bitmap对象等很占用内存的资源。
这里注意虽然activity执行了ondestory()生命周期函数,但是系统并没有将其回收掉,类似的还有fragment的生命周期ondestory()。
检查内存泄漏
判断我们的程序是否有内存泄露的可能性这里需要注意一个值,Heap视图中,有一个Type叫做dataobject,即数据对象,也就是我们程序中大量存在的类类型的对象。它有一个TotoalSize列,就是当前进程中所有Java数据对象的内存总量。一般情况下,这个值的大小决定了是否会有内存泄露。
不断操作当前应用,同时注意观察dataobject的TotoalSize值。
正常情况下:TotalSize值都会稳定在一个有限的范围内,也就是由于程序中的代码良好,没有造成对象不被垃圾回收的情况。所以说虽然我们不断的操作会不对生成很多新的对象,而现在虚拟机不断的进行GC的过程,这些对象都会被回收了,内存占有量会回落到一个稳定水平。
异常情况:如果代码中存在没有释放对象引用的情况,则dataobject的TotoalSize值在每次GC后会不明显回落,随着操作次数的增多TotoalSize的值会越来越大。
内存泄漏指的是那些程序不再使用的对象无法被GC识别,这样就导致这个对象一直留在内存当中,占用了宝贵的内存空间。
显然,这还使得每级Generation的内存区域可用空间变小,GC就会更容易被触发,从而引起性能问题。
activity内存泄漏的几种情况
1.activity的内部静态成员变量持有activity的引用,导致activity对象不能被回收,如静态View sView = new View(this);
这时需要使用applicationContext;
2.全局的单例类直接或者间接持有了activity的引用导致activity不能被回收。
直接包括:使用了静态的context作为成员,很多开发者喜欢类似回退栈的集合,用于退出程序,这里需要使用弱引用或者软引用
间接情况有:将使用到了context的类的对象作为成员,导致不能被回收,比如全局的集合
3.属性动画的无下限循环没有停止,导致View被持有,导致activity被持有,此时需要在ondestory()中停止动画。cancel()。在View的onDetachedFromWindow()中停止也可以。
4.handler,runnable等内部类会持有外部类activity的实例,如果handler,或者runnable生命周期比activity生命周期长,则会产生内存泄漏。
这时使用静态的handler,在其内部使用软引用持有activity的引用,runable类似。
检查内存泄露工具
检查内存泄漏的原理
java的内存是有jvm管理的,当gc发生时,会从gc roots开始遍历内存中的对象,检查其是否还存在引用,如果没有引用指向它就说明可以将其回收,否则还不能回收,因此我们检查内存泄漏的步骤是:
1、了解 OutOfMemoryError 情况。
2、重现问题。
3、在发生内存泄露的时候,把内存 Dump 出来。
4、计算这个对象到 GC roots 的最短强引用路径。
5、确定引用路径中的哪个引用是不该有的,然后修复问题。
在其中找到到gcroot的最短引用路径,检查该引用是否正常,如果我们并不想在此保持该对象的引用那么说明该对象发生了内存泄漏。
mat内存泄漏检查工具
可以参考:http://blog.csdn.net/guolin_blog/article/details/42238633
Histogram可以列出内存中每个对象的名字、数量以及大小。
Dominator Tree会将所有内存中的对象按大小进行排序,并且我们可以分析对象之间的引用结构。
遇到的坑:
转换hprof文件
DDMS Dump出的文件要经过转换才能被MAT识别,Android SDK提供了这个工具hprof-conv(位于sdk/tools下)
/hprof-conv xxx-a.hprof xxx-b.hprof
用MAT打开转换后的 hprof文件
将hprof文件必须在tools文件夹下,转换成功,用mat打开
方法一,dominator tree
在有红色标记行Path to GC Roots -> exclude weak references,红色表示可以被访问到,还有引用,虽然一些对象现在不能被GC Roots访问到,但不代表着其所持有的其它引用也不会被GC Roots访问到。
例如:bitmap对象
方法二,histogram
正则表达式框中搜索“MainActivity”
MainActivity右键 -> List objects -> with incoming references查看具体MainActivity实例,
MainActivity的实例右键 -> Path to GC Roots -> exclude weak references
有Shallow Heap而没有Retained Heap的,那么Shallow Heap又是什么意思呢?就是当前对象自己所占内存的大小
首先Retained Heap表示这个对象以及它所持有的其它引用(包括直接和间接)所占的总内存,
leakcanary 自动检测内存泄漏
leakcanary是一个内存泄漏快速定位的第三方框架,使用简单方便。
其根据内存泄漏的检查原理,将这些步骤程序化,能快速检测到内存泄漏。比自己一个个排查快多了。
工作机制:
1.RefWatcher.watch() 创建一个 KeyedWeakReference 到要被监控的对象。
2.然后在后台线程检查引用是否被清除,如果没有,调用GC。
3.如果引用还是未被清除,把 heap 内存 dump 到 APP 对应的文件系统中的一个 .hprof 文件中。
4.在另外一个进程中的 HeapAnalyzerService 有一个 HeapAnalyzer 使用HAHA 解析这个文件。
5.得益于唯一的 reference key, HeapAnalyzer 找到 KeyedWeakReference,定位内存泄露。
6.HeapAnalyzer 计算 到 GC roots 的最短强引用路径,并确定是否是泄露。如果是的话,建立导致泄露的引用链。
7.引用链传递到 APP 进程中的 DisplayLeakService, 并以通知的形式展示出来。
leakcanary不仅可以监控activity还可以监控任意对象。
使用说明:http://www.liaohuqiu.net/cn/posts/leak-canary-read-me/
参考资料:
http://blog.tingyun.com/web/article/detail/155
http://note.youdao.com/yws/public/redirect/share?id=91910c229b38f88e11a67d6b56c427ce&type=false
http://note.youdao.com/yws/public/redirect/share?id=5c16ff9a0350e42be93ac1da77ecd660&type=false
http://note.youdao.com/yws/public/redirect/share?id=5370058ff83fad6e70df5573d87ff6b8&type=false