美文网首页
Android性能优化-内存泄漏的8个Case

Android性能优化-内存泄漏的8个Case

作者: 飞奔的蚂蚁 | 来源:发表于2017-04-18 10:29 被阅读0次

    1. 什么是内存泄漏?

    JVM内存管理


    关于内存泄漏我们要知道,JVM内存分配的几种策略。

    1. 静态的

    静态的存储区,内存在程序编译的时候就已经分配好了,这块内存在程序整个运行期间都一直存在,它主要存放静态数据、全局的static数据和一些常量。

    2.栈式的

    在执行方法时,方法一些内部变量的存储都可以放在栈上面创建,方法执行结束的时候这些存储单元就会自动被注释掉。栈 内存包括分配的运算速度很快,因为内在在处理器里面。当然容量有限,并且栈式一块连续的内存区域,大小是由操作系统决定的,他先进后 出,进出完成不会产生碎片,运行效率高且稳定

    3.堆式的

    也叫动态内存 。我们通常使用new 来申请分配一个内存。这里也是我们讨论内存泄漏优化的关键存储区。GC会根据内存的使用情况,对堆内存里的垃圾内存进行回收。

    堆内存是一块不连续的内存区域,如果频繁地new/remove会造成大量的内存碎片,GC频繁的回收,导致内存抖动,这也会消耗我们应用的性能
    我们知道可以调用 System.gc();进行内存回收,但是GC不一定会执行。

    面对GC的机制,我们是否无能为力?其实我们可以通过声明一些引用标记来让GC更好对内存进行回收。

    类型 回收时机 生命周期
    StrongReference (强引用) 任何时候GC是不能回收他的,哪怕内存不足时,系统会直接抛出异常OutOfMemoryError,也不会去回收 进程终止
    SoftReference (软引用) 当内存足够时不会回收这种引用类型的对象,只有当内存不够用时才会回收 内存不足,进行GC的时候
    WeakReference (弱引用) GC一运行就会把给回收了 GC后终止
    PhantomReference (虚引用) 如果一个对象与虚引用关联,则跟没有引用与之关联一样,在任何时候都可能被垃圾回收器回收 任何时候都有可能

    开发时,为了防止内存溢出,处理一些比较占用内存并且生命周期长的对象时,可以尽量使用软引用和弱引用。

    Tip:成员变量全部存储在堆中(包括基本数据类型,引用及引用的对象实体),因为他们属于类,类对象最终还是要被new出来的。
    局部变量的基本数据类型和引用存在栈中,应用的对象实体存储在堆中。因为它们属于方法当中的变量,生命周期会随着方法一起结束

    内存泄漏的定义

    当一个对象已经不需要使用了,本该被回收时,而有另外一个正在使用的对象持有它的引用,从而导致了对象不能被GC回收。

    这种导致了本该被回收的对象不能被回收而停留在堆内存中,就产生了内存泄漏。

    内存泄漏与内存溢出的区别
    • 内存泄漏(Memory Leak)

    进程中某些对象已经没有使用的价值了,但是他们却还可以直接或间接地被引用到GC Root导致无法回收。当内存泄漏过多的时候,再加上应用本身占用的内存,日积月累最终就会导致内存溢出OOM

    • 内存溢出(OOM)

    当 应用的heap资源超过了Dalvik虚拟机分配的内存就会内存溢出

    内存泄漏带来的影响
    • 应用卡顿

    泄漏的内存影响了GC的内存分配,过多的内存泄漏会影响应用的执行效率

    • 应用异常(OOM)

    过多的内存泄漏,最终会导致 Dalvik可分配的内存越来越少,更加容易出现OOM

    2. Android开发常见的内存泄漏

    (1) 单例造成的内存泄漏

    错误示例

    当调用getInstance时,如果传入的context是Activity的context。

    只要这个单例没有被释放,那么这个Activity也不会被释放一直到进程退出才会释放。

    解决方案

    能使用Application的Context就不要使用Activity的Content,Application的生命周期伴随着整个进程的周期

    (2)非静态内部类创建静态实例造成的内存泄漏

    错误示例

    解决方案

    将非静态内部类修改为静态内部类。(静态内部类不会隐式持有外部类)

    (3)Handler造成的内存泄漏

    错误示例

    mHandler是Handler的非静态匿名内部类的实例,所以它持有外部类Activity的引用,我们知道消息队列是在一个Looper线程中不断轮询处理消息,那么当这个Activity退出时消息队列中还有未处理的消息或者正在处理消息,而消息队列中的Message持有mHandler实例的引用,mHandler又持有Activity的引用,所以导致该Activity的内存资源无法及时回收,引发内存泄漏。

    解决方案

    创建一个静态Handler内部类,然后对Handler持有的对象使用弱引用,这样在回收时也可以回收Handler持有的对象,这样虽然避免了Activity泄漏,不过Looper线程的消息队列中还是可能会有待处理的消息,所以我们在Activity的Destroy时或者Stop时应该移除消息队列中的消息

    (4)线程造成的内存泄漏

    错误示例

    异步任务和Runnable都是一个匿名内部类,因此它们对当前Activity都有一个隐式引用。如果Activity在销毁之前,任务还未完成, 那么将导致Activity的内存资源无法回收,造成内存泄漏

    解决方案

    使用 静态内部类,避免了Activity的内存资源泄漏,当然在Activity销毁时候也应该取消相应的任务AsyncTask::cancel(),避免任务在后台执行浪费资源

    (5)资源未关闭造成的内存泄漏

    错误示例

    对于使用了BraodcastReceiver,ContentObserver,File,Cursor,Stream,Bitmap等资源的使用,应该在Activity销毁时及时关闭或者注销,否则这些资源将不会被回收,造成内存泄漏

    解决方案

    在Activity销毁时及时关闭或者注销

    (6)使用了静态的Activity和View

    错误示例

    解决方案

    应该及时将静态的应用 置为null,而且一般不建议将View及Activity设置为静态

    (7)注册了系统的服务,但onDestory未注销

    错误示例

    解决方案
    //不需要用的时候记得移除监听sensorManager.unregisterListener(listener);

    (8)不需要用的监听未移除会发生内存泄露

    错误示例

    解决方案

    Tip:tv.setOnClickListener();//监听执行完回收对象,不用考虑内存泄漏
    tv.getViewTreeObserver().addOnWindowFocusChangeListene,add监听,放到集合里面,需要考虑内存泄漏

    相关文章

      网友评论

          本文标题:Android性能优化-内存泄漏的8个Case

          本文链接:https://www.haomeiwen.com/subject/utrfzttx.html