继介绍稳定性ANR类故障和Crash/Tombstone类故障后,本章将介绍第三大类故障资源泄露及其典型场景、分析定位和解决方法。
1
资源泄露问题原因分析
2
典型泄露场景
- Cursor
- InputStream OutputStreamIO流
- BraodcastReceiver
- ContentObserver
- Bitmap
应该在资源使用完毕,或者Activity销毁时及时关闭或者注销,否则这些资源将不会被回收,造成内存泄漏。
常见内存泄露代码举例
context改为context.getApplicationContext():因为单例的生命周期和Application的一致。
这样就在Activity内部创建了一个非静态内部类的实例,每次启动Activity时都会使用该实例的数据,这样虽然避免了资源的重复创建,不过这种写法却会造成内存泄漏,因为非静态内部类默认会持有外部类的引用,而又使用了该非静态内部类创建了一个静态的实例,该实例的生命周期和应用的一样长,这就导致了该静态实例一直会持有该Activity的引用,导致Activity的内存资源不能正常回收。正确的做法为: 将该内部类设为静态内部类或将该内部类抽取出来封装成一个单例,如果需要使用Context,请使用ApplicationContext。
当这个Activity被finished后,延时发送的消息会继续在主线程的消息队列中存活10分钟,直到他们被处理。这个message持有handler对象,这个handler对象又隐式持有着MainActivity对象.直到消息被处理前,这个handler对象都不会被释放, 因此MainActivity也不会被释放。注意,这个匿名Runnable类对象也一样。匿名类的非静态实例持有一个隐式的外部类引用,因此MainActivity将被泄露。
3
内存分析工具、常用命令及分析步骤
3.1 内存分析工具
3.2 内存分析常用命令
3.3 分析内存泄露步骤
4
避免内存泄露的建议
1)对于生命周期比Activity长的对象如果需要应该使用ApplicationContext。
2)在涉及到Context时先考虑ApplicationContext,当然它并不是万能的,对于有些地方则必须使用Activity的Context,对于Application,Service,Activity三者的Context的应用场景如下:
其中:NO1表示Application和Service可以启动一个Activity,不过需要创建一个新的task任务队列。而对于Dialog而言,只有在Activity中才能创建。
3)对于需要在静态内部类中使用非静态外部成员变量(如:Context、View ),可以在静态内部类中使用弱引用来引用外部类的变量来避免内存泄漏。
4)对于生命周期比Activity长的内部类对象,并且内部类中使用了外部类的成员变量,将内部类改为静态内部类,静态内部类中使用弱引用来引用外部类的成员变量。
5)对于不再需要使用的对象,显示的将其赋值为null,比如使用完Bitmap后先调用recycle(),再赋为null。
6)保持对对象生命周期的敏感,特别注意单例、静态对象、全局性集合等的生命周期。