3招搞定android内存泄漏

最近做了内存泄漏的总结,这里先把PPT搬上来,有人看再做优化。



什么是内存泄漏?

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

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

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


APP内存回收算法

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

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

DavikART的内存回收

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

ART也使用了标注并清理算法Java堆包括Image SpaceZygote SpaceAllocationSpaceLargeObject 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,这种情况下的GCstopworld

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

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


第二招
弱引用跟踪:
LeakCanary


Java的几种引用关系

强引用GC不会回收该对象

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

弱引用 WeakReferenceGC会回收

虚引用 PhantomReferenceGC会回收,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=515msgc=54ms, heap dump=136msanalysis=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、非静态内部类的静态实例容易造成内存泄漏

2activity使用静态成员

3、使用handler时的内存问题

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

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

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

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

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

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

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


参考资料:

Android内存泄漏分析及调试

Androidnative进程内存泄露的调试技巧(一)



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值