您的位置:首页 > 移动开发 > Android开发

3招搞定android内存泄漏

2016-03-17 21:30 232 查看
最近做了内存泄漏的总结,这里先把PPT搬上来,有人看再做优化。



什么是内存泄漏?

内存泄漏,就是指程序申请使用的内存没有及时释放。

Android应用的内存泄漏主要在虚拟机层,也有Native层的。

有的内存泄漏可能导致程序占用的内存增高,直至OOM;有的内存泄漏比较隐蔽,也可能造成严重后果。比如binder通信泄漏,会导致TransactionTooLargeException。同时,内存占用偏高时会引发频繁GC,导致应用卡顿。

APP内存回收算法

标注并清理 GC会从内存遍历的根节点(GCRoots,比如threadstack中的变量,JNI中的全局变量,zygote中的classloader等)开始,对heap遍历一次。保留所有被GCRoots直接或间接引用的对象,剩下的对象作为垃圾回收。

标注并清理法克服了引用计数法中的循环引用问题,对程序几乎没有额外性能开销;缺点是垃圾回收时会暂停进程内其他线程,容易产生内存碎片。

Davik和ART的内存回收

主流的大部分Davik采取的都是标注与清理(Mark and Sweep)回收算法,也有实现了拷贝GC的,
java堆实际上是由一个Active堆和一个Zygote堆组成的。

ART也使用了标注并清理算法,Java堆包括Image
Space、Zygote Space、AllocationSpace和LargeObject
Space四个,分别采用不同的回收策略,提高了GC效率,减少pause时间,而且还在内存分配上对大内存的有单独的分配区域,同时还能有算法在后台做内存整理,减少内存碎片。GC的效率提高了2-3倍。  







第一招

 看内存占用:AndroidStudio\Android
monitor\Memory+allocator

反复操作功能模块,看内存占用





正常情况下内存占用都会稳定在一个有限的范围内,也就是说由于程序中的代码良好,没有造成对象不被垃圾回收的情况,所以说虽然我们不断的操作会不断的生成很多对象,而在虚拟机不断的进行GC的过程中,这些对象都被回收了,内存占用量会会落到一个稳定的水平;如果有缓存,也会在初次操作内存升高后维持在一个稳定水平,因为后面的操作会从缓存中读取,而非新建对象。

反之,如果代码中存在没有释放对象引用的情况,则内存在每次GC后不会有明显的回落,随着操作次数的增多,内存占用会越来越大,直到到达一个上限后导致进程OOM被kill掉。



看GC的回收信息

LogCat中的GC信息

【GC回收原因】【释放了多少内存】【内存信息】【gc中断时间】【gc总耗时】

D/dalvikvm: GC_FOR_ALLOCfreed 447K, 20% free 10501K/12984K,

paused 15ms, total 15ms

在堆上分配对象时,内存不足触发的GC

D/dalvikvm: GC_EXPLICITfreed 155K, 19% free 10592K/12984K,

 paused2ms+3ms, total 27ms

应用程序强制GC,如调用System.gc,线程被杀死或Binder通信中断

 D/dalvikvm: GC_CONCURRENTfreed 506K, 26% free 10219K/13676K,

 paused1ms+1ms, total 12ms

当我们应用程序的堆内存达到一定量,或者可以理解为快要满的时候,系统会自动触发

GC操作来释放内存,异步进行,但也会暂停工作线程

D/dalvikvm: GC_BEFORE_OOMfreed 9K, 6% free 54334K/57576K,

 paused 342ms, total 342ms

OOM之前,执行最后一次回收

ART中的GC类型

kGcCauseForAlloc,当要分配内存的时候发现内存不够的情况下引起的GC,这种情况下的GC会stopworld

kGcCauseBackground,当内存达到一定的阀值的时候会去出发GC,这个时候是一个后台gc,不会引起stopworld

kGcCauseExplicit,显示调用的时候进行的gc,如果art打开了这个选项的情况下,在system.gc的时候会进行gc

第二招

弱引用跟踪: LeakCanary

Java的几种引用关系

强引用GC不会回收该对象

软引用
SoftReference系统内存不足时释放

弱引用
WeakReference,GC会回收

虚引用
PhantomReference,GC会回收,get方法返回结果始终为null,

必须用ReferenceQueue

代码中配置

LeakCanary默认对Activity进行了监控,Fragment和其他对象需要

手动监控。
public class CommentListContentFragment extends Fragment {

@Override
public void onStop() {
super.onStop();
MyApplication.getInstance().getWatcher().watch(mAdapter);
}

@Override
public void onDestroy() {
super.onDestroy();
MyApplication.getInstance().getWatcher().watch(this);
}
}






看log信息:

D/LeakCanary: In com.jingdong.app.mall:5.0.0:27074.
D/LeakCanary: * com.jingdong.app.mall...ProductListActivity has leaked:
D/LeakCanary: * GC ROOT static com.jingdong.app.mall...SearchEngine.engine
D/LeakCanary: * references com.jingdong.app.mall...SearchEngine.httpGroupUtil
D/LeakCanary: * references com.jingdong.cleanmvp...HttpGroupUtil.httpGroup
D/LeakCanary: * references com.jingdong.common...HttpGroupAdapter.httpGroupSetting
D/LeakCanary: * references com.jingdong.common..HttpGroup$HttpGroupSetting.myActivity
D/LeakCanary: * leaks com.jingdong.app.mall...ProductListActivity instance
D/LeakCanary: * Reference Key: f990321d-a4d4-4bb7-b0de-238e68f3e1ce
D/LeakCanary: * Device: samsung samsung GT-N7100 t03gzc
D/LeakCanary: * Android Version: 4.3 API: 18 LeakCanary: 1.3.1
D/LeakCanary: * Durations: watch=515ms, gc=54ms, heap dump=136ms, analysis=4560ms

也可以分析dump信息

Dump存放目录:sdcard/download/leakcanary/detected_leaks

第三招 分析内存快照:dump MAT

反复操作后,手动gc,然后利用Android Studio获取内存快照,用hprof-conv工具转换









Shallow size

对象本身占用的内存大小。一个普通对象的shallowsize,取决于对象的成员变量(field)的类型和个数。数组的shallowsize,取决于成员类型和数组长度。集合的shallowsize就是集合中所有对象的shallowsize之和。



Retained size

指对象自身的shallow size和其支配的对象的shallowsize之和。也就是说,GC回收这个对象能释放的内存大小。


  


可以使用OQL语言,查询某个对象。点击!感叹号执行。



也可以截取反复操作前,和反复操作后的dump,同时导入mat中对比,再看那些增加的对象及其引用。





常见android app内存泄漏

1、非静态内部类的静态实例容易造成内存泄漏

2、activity使用静态成员

3、使用handler时的内存问题

4、注册某个对象后未反注册

5、集合中对象没清理造成的内存泄露

6、资源对象没关闭造成的内存泄露

7、线程未终止造成的内存泄露

8、Bitmap使用不当,没有及时回收

9、构造Adapter时,没有使用缓存的convertView

10、在经常调用的方法中创建对象,如循环中创建对象

参考资料:

Android内存泄漏分析及调试

Androidnative进程内存泄露的调试技巧(一)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息