优化系列 04:内存泄露场景一览

Published: 12 Jul 2018 Category: 优化

内存泄露

Java内存泄漏指的是进程中某些对象(垃圾对象)已经没有使用价值了,但是它们却可以直接或间接地引用到gc roots导致无法被GC回收。无用的对象占据着内存空间,使得实际可使用内存变小,形象地说法就是内存泄漏。

内存溢出

内存溢出(out of memory)通俗理解就是内存不够,通常在运行大型软件或游戏时,软件或游戏所需要的内存远远超出了你主机内安装的内存所承受大小,就叫内存溢出。此时软件或游戏就运行不了,系统会提示内存溢出,有时候会自动关闭软件。

Android内存泄露

Android的虚拟机是基于寄存器的Dalvik,它的最大堆大小一般是16M,有的机器为24M。因此我们所能利用的内存空间是有限的。如果我们的内存占用超过了一定的水平就会出现OutOfMemory的错误。当前目前已经达到了256M或者更大(机型)

为什么会出现内存不够用的情况呢?我想原因主要有两个:

  • 由于我们程序的失误,长期保持某些资源(如Context)的引用,造成内存泄露,资源得不到释放。
  • 保存了多个耗用内存过大的对象(如Bitmap),造成内存超出限制。

内存泄露和内存溢出区别

  • 内存泄露memory leak
    • 是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存迟早会被占光。
    • 是指向系统中申请分配内存进行使用(new),但使用完后,却没有归还。申请到的内存自己也不能再访问,而系统也不能再次将它分配给需要的程序。
  • 内存溢出out of memory
    • 是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory;比如申请了一个integer,但给他存了long才能存下的数。比如栈:栈满时,进行进栈,内存溢出。栈空时,进行出栈,内存也会溢出。分配内存不足以放下数据项序列,称为内存溢出。

内存泄露会导致内存溢出。

已知可导致内存泄露的场景

1. 静态变量导致的内存泄露
2. 属性动画导致的内存泄露

从 Android3.0开始,Google提供了属性动画,属性动画中有一类无限循环的动画,如果在Activity中播放此类动画且没有在 onDestroy中去停止动画,那么动画会一直播放下去,尽管已经无法在界面上看见动画效果了,并且这个时候 Activity的 View会被动画持有,而View又持有了Activty,最终Activity无法释放。下面的动画是无限动画,会泄露当前的Activity,解决方法是在Activity的onDestroy中调用animator.cancel()来停止动画。

3. 单例模式导致的内存泄露

Android的单例模式在我们项目开发中经常会用到,不过使用的不恰当的话也会造成内存泄漏。因为单例的静态特性使得单例的生命周期和应用的生命周期一样长, 这就说明了如果一个对象已经不需要使用了,而单例对象还持有该对象的引用,那么这个对象将不能被正常回收,这就导致了内存泄漏。

4. Bitmap使用不当造成内存泄露

当我们使用Bitmap对象开辟对象时,较大的Bitmap对象会被创建,其占用内存过多,超出堆的上限时,便会抛出内存泄露的危险。Bitmap对象不在使用时调用recycle()释放内存时会造成内存泄露。如果一个Bitmap对象比较占内存,当它不在被使用的时候,可以调用Bitmap.recycle()方法回收此对象的像素所占用的内存,但这不是必须的,视情况而定。

5. handler造成的内存泄露

Handler持有对UI主线程消息队列MessageQueue和循环Looper的引用。子线程可以通过Handler将消息发送到UI线程的消息队列MessageQueue中。UI主线程通过Looper循环查询消息队列的UI_MQ,当发现有消息存在时会将消息从队列中取出。首先分析消息,通过消息的参数判断该消息对应的Handler,然后将消息分发到指定的Handler进行处理。

6. 匿名内部类在异步线程中的使用引起的内存泄漏

参考链接:

  1. https://blog.csdn.net/u013233097/article/details/50313993
  2. https://www.cnblogs.com/shen-hua/p/7233300.html