现在的位置: 首页 > 移动开发 > 正文

Android应用防内存泄露小结

2019年07月30日 移动开发 ⁄ 共 1696字 ⁄ 字号 评论关闭

这两天在写完代码之余,算是有时间来review下整个工程的代码,以内存泄露为出发点,对代码进行了详细的解读。

结合项目主要总结了两点:

1. 对Context引用的防范;
    在Android应用中, 对Context对象的引用随处可见, 很多的事情需要有Context对象的方法才能完成。然后, 很多时候,我们对Context对象的引用并没有考虑那么全面(以为一切事情都有JVM GC为我们做),但实际上,任何应用开发,只有在深刻理解其运行环境才能做大性能最大化(至少可以做到不那么糟糕)。在很多情况下, 我们引用Context对象是这样的,一,是在调用别的接口(需要Context对象作为参数)的时候,只管把this传进去而不管其他;二,是在需要别的Context对象的时候,让调用方传递进来,而不管生命周期这些问题。这会导致一些问题,
根本原因是一样的,即调用方和被调用方生命周期不一致,后果是在一个Activity结束掉后,Context对象仍然被引用。如果这种情况是增量式的, 内存泄露问题就出现了。

    增量式的情况如:
        a). Activity内创建非静态Handler对象, 或者静态Handler对象但是没有使用对Activity的弱引用;
            --> 原因: 主线程的创建的Handler会与存储在ThreadLocal里的MessageQueue绑定,而主线程的ThreadLocal的生命周期就是整个进程的生命周期,所以通常长于某个Activity。如果用以上情况创建Handler,因为创建的内部类对象隐式的含有对外部类(即Activity)的引用,所以每次new一个Activity并创建Handler对象的同时,
就意味着将自身的引用储存在了ThreadLocal里,这样,当Activity结束时,(如果进程没销毁),那么Activity对象的堆内存仍然不会收回,而在反复启动结束这个Activity之后,就出现了内存泄露。

        b). 用生命周期较长的(如静态的)集合将mContext储存起来
2. Bitmap的回收
    Bitmap是图片储存在内存里的方式,通常占用内存较大,而负责分配内存的是native方法,JVM在GC的时候是不会回收到native里面的内存的,所以在Bitmap对象用完之后 ,需要我们手动调用recycle方法来通知native方法free掉分配的内存。


附:内存泄露检测方法(这里只针对检测Activity的内存泄露)

1. 启动应用,在要检测的Activity启动之前,Cause GC一下,显示当前内存使用情况;

图1,启动应用,并在启动待检测的Activity之前的内存使用情况

2. 启动要检测的Activity,然后finish,然后启动,然后finish,如此反复多次.......最后finish这个Activity(即回到步骤1时的状态), 然后Cause   GC一下,
如果发现内存使用量是逐渐上升,那么就说明这个Activity存在内存泄露(具体问题定位看图3)

图2, 反复启动销毁待检测Activity, 查看内存使用情况
(即看Activity每次销毁后内存是否被回收,如果不停增加说明每次启动后存在泄漏)

3. 在步骤一、二Casuse GC的时候, Dump一下,用MAT插件打开, 然后把两次的Histogram进行对比,可以清楚的看到是哪些
类型的数据在递增,而没有得到回收。(具体可以定位到Class信息, 只需右键Class Name这一栏的信息,然后选择list objects,选择incoming这一项,如图4)

图3. 对比1与2情况下的内存使用

4. 查看具体的对象占用内存情况,图例中可以看到问题所在——Bitmap对象内存没有被回收。

图4. 针对3所列出的项,右键查看具体的对象占用内存情况
(以便更容易定位到问题所在)

尾注:内存的合理分配与回收是应用性能的保障,特别是移动端对用户体验要求高的应用,因为每一次GC操作,系统会暂停所有的线程运行。如果内存的使用不合理,导致GC过于频繁会使得界面的绘制出现失贞的情况,从而出现卡顿导致用户体验不好。

抱歉!评论已关闭.