Android内存如何泄露

Android内存如何泄露

  1. 对象的生命周期溢出
  2. 对象无限创建引起内存爆满

生命周期溢出

内存泄露说到底是,对象的生命周期管理失误,导致对象的生命周期溢出。大概可以理解成,儿子变成了兄弟(O_O),所以不再管的了他。

生命周期溢出.png

如图,对象B本来由对象O管理,但由于使用失误导致对象B溢出了本来的生命周期,由儿子变成了兄弟。

此处对象B发生了内存泄露,场景可以参照:

  1. 对象B被程序级变量保存,如单例模式,全局静态变量,程序级对象持有等等。
  2. 对象B内调用了registerReceiver注册广播或注册到Android系统服务,把自己托付给了Android系统。
  3. 对象B内使用了非静态内部类(匿名或非匿名一样),不小心跟着内部类跑出了生命周期。
  4. 对象B跑到了另一个线程,认了另一个爸爸。比如Handler,Runnable,AsyncTask等。
  5. 占有了系统资源不释放。比如File,Stream,Cursor等。
    无论哪种,都是生命周期没管理好导致的问题。

当对象O释放的时候,如下图,对象O变成了待释放,当GC后,就只剩对象B还活着。


等待GC.png
GC后.png

此过程,大致有以下两点:

  1. 对象没有被GCRoot引用,对象占用的内存并不是立即释放,而是等待下一次GC。
  2. 内存泄露只泄漏那个泄露的对象占用的内存。比如对象B内有一个Bitmap,如果对象O释放的时候同时释放了对象B内的Bitmap,那么对象B泄露的只是一个空壳对象,泄漏几个字节;反之,如果没有时机释放对象B内的Bitmap,则对象B泄露的就是一个Bitmap几十兆的内存。(前者长时间运行内存不怎么涨,Crash几率低,比较难发现;后者短时间运行内存就爆满,容易Crash,相对容易发现)

当出现内存泄露后,不断重复操作,就会出现下面的现象,一堆对象B挂在树上,像葫芦娃。这种现象和下面对象不断创建类似,但本质是不一样的。


这是常见的内存泄露模型。但也有一直保持两个的。为什么呢?因为再次操作,新的对象B替代了旧的对象B,使得旧的对象B得到了释放的机会。最简单的就是setObject(B)这种设置属性的操作,新的对象B会替换旧的对象B。这也是常见的泄漏模型。

如何避免生命周期溢出?
最简单的原则是,主动管理内部对象或线程的生命周期。

首先,不要假想Android系统或第三方接口会帮我们管理传递给他们的对象。就好比假想handler或postDelay会在退出的时机释放所有Runnable一样,这个假设即使成立,也应当自己主动管理。然后

注意下面的类型:

  1. 静态变量(静态属性或全局静态变量)。
  2. 单例模式(单独拿出来是因为大部分人不怎么注意单例)。
  3. 内部类(匿名或非匿名),要么主动释放要么改为静态内部类。
  4. Runnable/Handler/AsyncTask/post/postDelay等线程操作,主动释放。
  5. 资源操作,切记释放。
  6. 系统服务,申请或注册后主动释放和反注册。
  7. 避免引用传递过深。

对象不断创建,内存爆满

内存问题另一个是,逻辑错误导致对象不断创建,最后导致内存爆满。这并不是内存泄露,而是属于没有正确和合理使用内存。

内存爆满.png

和上面内存泄露不停创建对象区别是,这里更偏向于方案不合理,方案本身出了内存问题。而不是由于生命周期管理失当,溢出引发的。

注意下面的方案:

  1. 多线程/并发。避免多线程处理多张图片!(解码过程瞬间撑爆内存)
  2. 对象缓存。列表的ItemView缓存要适当,含有图片时用完最好先释放图片后再加入缓存重用。一些缓存机制也要注意。
  3. (大)对象集合。

你可能感兴趣的:(Android内存如何泄露)