Android内存如何泄露
- 对象的生命周期溢出
- 对象无限创建引起内存爆满
生命周期溢出
内存泄露说到底是,对象的生命周期管理失误,导致对象的生命周期溢出。大概可以理解成,儿子变成了兄弟(O_O),所以不再管的了他。
![](https://img.haomeiwen.com/i2166887/6e9e7547ad3fa0c1.png)
如图,对象B本来由对象O管理,但由于使用失误导致对象B溢出了本来的生命周期,由儿子变成了兄弟。
此处对象B发生了内存泄露,场景可以参照:
- 对象B被程序级变量保存,如单例模式,全局静态变量,程序级对象持有等等。
- 对象B内调用了registerReceiver注册广播或注册到Android系统服务,把自己托付给了Android系统。
- 对象B内使用了非静态内部类(匿名或非匿名一样),不小心跟着内部类跑出了生命周期。
- 对象B跑到了另一个线程,认了另一个爸爸。比如Handler,Runnable,AsyncTask等。
- 占有了系统资源不释放。比如File,Stream,Cursor等。
无论哪种,都是生命周期没管理好导致的问题。
当对象O释放的时候,如下图,对象O变成了待释放,当GC后,就只剩对象B还活着。
![](https://img.haomeiwen.com/i2166887/315a013421d64053.png)
![](https://img.haomeiwen.com/i2166887/0b2677577f83e52c.png)
此过程,大致有以下两点:
- 对象没有被GCRoot引用,对象占用的内存并不是立即释放,而是等待下一次GC。
- 内存泄露只泄漏那个泄露的对象占用的内存。比如对象B内有一个Bitmap,如果对象O释放的时候同时释放了对象B内的Bitmap,那么对象B泄露的只是一个空壳对象,泄漏几个字节;反之,如果没有时机释放对象B内的Bitmap,则对象B泄露的就是一个Bitmap几十兆的内存。(前者长时间运行内存不怎么涨,Crash几率低,比较难发现;后者短时间运行内存就爆满,容易Crash,相对容易发现)
当出现内存泄露后,不断重复操作,就会出现下面的现象,一堆对象B挂在树上,像葫芦娃。这种现象和下面对象不断创建类似,但本质是不一样的。
![](https://img.haomeiwen.com/i2166887/6fdbdb9ed66a0bd2.png)
这是常见的内存泄露模型。但也有一直保持两个的。为什么呢?因为再次操作,新的对象B替代了旧的对象B,使得旧的对象B得到了释放的机会。最简单的就是setObject(B)这种设置属性的操作,新的对象B会替换旧的对象B。这也是常见的泄漏模型。
如何避免生命周期溢出?
最简单的原则是,主动管理内部对象或线程的生命周期。
首先,不要假想Android系统或第三方接口会帮我们管理传递给他们的对象。就好比假想handler或postDelay会在退出的时机释放所有Runnable一样,这个假设即使成立,也应当自己主动管理。然后
注意下面的类型:
- 静态变量(静态属性或全局静态变量)。
- 单例模式(单独拿出来是因为大部分人不怎么注意单例)。
- 内部类(匿名或非匿名),要么主动释放要么改为静态内部类。
- Runnable/Handler/AsyncTask/post/postDelay等线程操作,主动释放。
- 资源操作,切记释放。
- 系统服务,申请或注册后主动释放和反注册。
- 避免引用传递过深。
对象不断创建,内存爆满
内存问题另一个是,逻辑错误导致对象不断创建,最后导致内存爆满。这并不是内存泄露,而是属于没有正确和合理使用内存。
![](https://img.haomeiwen.com/i2166887/106847690535a972.png)
和上面内存泄露不停创建对象区别是,这里更偏向于方案不合理,方案本身出了内存问题。而不是由于生命周期管理失当,溢出引发的。
注意下面的方案:
- 多线程/并发。避免多线程处理多张图片!(解码过程瞬间撑爆内存)
- 对象缓存。列表的ItemView缓存要适当,含有图片时用完最好先释放图片后再加入缓存重用。一些缓存机制也要注意。
- (大)对象集合。
网友评论