您当前的位置: 首页 > 
  • 0浏览

    0关注

    674博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

内存泄漏优化整理

沙漠一只雕得儿得儿 发布时间:2021-12-02 10:07:49 ,浏览量:0

一 Android 中内存泄露的常见场景 & 解决方案 1.1 单例造成的内存泄露

泄漏原因:单例生命周期 = 应用程序的生命周期,若其持有Context是某个Activity,会造成内存泄漏。

解决方案:尽量使用ApplicationContext;或者使用弱引用(WeakReference)来进行改进。

1.2 非静态内部类 / 匿名类

泄漏原因:非静态内部类和匿名类会自动持有外部类的强引用,且其生命周期可能大于外部类的生命周期,造成内存泄漏。

解决方案:将其改成静态内部类;或者使用弱引用(WeakReference)来进行改进。

1.3 集合类

泄漏原因:集合类添加元素后,仍引用着集合元素对象,导致该集合中的元素对象无法被回收,从而导致内存泄露。

解决方案:在集合元素使用之后从集合中删除,等所有元素都使用完之后,将集合置空。

1.4 其他的情况

网络、文件等流忘记关闭; 手动注册广播时,退出时忘记 unregisterReceiver(); Service 执行完后忘记 stopSelf(); EventBus 等观察者模式的框架忘记手动解除注册; static 关键字修饰的成员变量。

二 使用DDMS和MAT工具进行内存优化 2.1 观察heap

首先在Android Studio中打开Android Device Monitor(在Android Studio 3.0以上版本需要在命令行输入monitor呼出)

如图为Android Device Monitor界面,选择进程后,可以点击工具栏上的  

(update heap)来更新统计信息,点击右侧的 Cause GC 按钮或工具栏上的  

即可查看当前的堆情况。

主要关注两项数据:

1、Heap Size 堆的大小,当资源增加,当前堆的空余空间不够时,系统会增加堆的大小,若超过上限 (例如64M,视平台和具体机型而定)则会被杀掉。

2、 Allocated 堆中已分配的大小,这是应用程序实际占用的内存大小,资源回收后,此项数据会变小。

查看操作前后的堆数据,看是否有内存泄漏 。对单一操作(比如添加页,删除页)进行反复操作,如果堆的大小一直增加,则有内存泄漏的隐患。

2.2 利用MAT分析内存(Eclipse Memory Analyzer Open Source Project | The Eclipse Foundation)

2.2.1 获取hprof文件

DDMS 可以将当前的内存 Dump成一个 hprof格式的文件,MAT 读取这个文件后会给出方便阅读的信息,配合它的查找,对比功能,就可以定位内存泄漏的原因。

在应用进行足够多的操作后(或者使用monkey命令:adb shell monkey -p com.gala.video 100),点击工具栏上的  

按钮,将内存信息保存成hprof文件。

2.2.2 转换hprof并打开

该文件还要经过转换才能被 MAT识别,Android SDK提供了这个工具 hprof-conv (位于 sdk/tools下),命令行输入:hprof-conv a.hprof b.hprof 即完成转换。

如下图所示,为MAT打开hprof文件后的overview界面:

2.2.3 Histogram 查询

(1)查找定位

Histogram可以列出每一个类的所有对象。可以分组,也可以查找。如下图所示在表的第一行可以输入正则表达式来匹配结果。

状态栏Objects标示该对象出现的次数,Shallow Heap标示对象本身所占的内存空间,Retained Heap表示对象本身及其直接引用或间接引用一起所占的内存空间。

一般来说若Shallow Heap和Retained Heap的差距很大就需要仔细分析该对象是不是产生了泄漏。

图中第一行NormalModeActivityProxy在运行过程中产生了3个对象,而Shallow Heap和Retained Heap的差距较大。基本可以确定其存在内存泄漏的问题。

(2)快速分析

快速找出某个实例没被释放的原因,可以右健Merge Shortest path to GC root -> exclude all phantom/weak/soft etc. references:找到从GC Root到一个对象或一组对象的共同路径,不包含虚、弱引用、软引用,剩下的就是强引用。

从GC上说,除了强引用外,其他的引用在JVM需要的情况下是都可以 被GC掉的,如果一个对象始终无法被GC,就是因为强引用的存在,从而导致在GC的过程中一直得不到回收,因此就内存溢出了。可以得到如下:

可以看到LoginCallbackRecorder这个单例类没有成功释放掉Listener,从而一直持有了NormalModeActivityProxy的强引用,导致后者无法被GC造成内存泄漏。

关注
打赏
1657159701
查看更多评论
立即登录/注册

微信扫码登录

0.0591s