Android性能优化之常见的内存泄漏

Java内存分配策略

java程序运行时的内存分配策略有三种,分别是静态分配,栈式分配,堆式分配。对应的三种存储策略使用的内存空间主要分别是静态存储区(方法区),栈区,堆区。

静态存储区:主要存放静态数据,全局static数据和常量。这块内存在程序编译时就已经分配好,并且在程序整个运行期间都存在。

栈区:当方法被执行时,方法体内的局部变量都在栈上创建,并在方法执行结束时这些局部变量所持有的内存将会被自动释放。

堆区:程序运行时直接new出来的内存,这部分内存在不使用时将会由gc来负责回收。

Java四种引用

强引用:JVM 宁可抛出 OOM ,也不会让 GC 回收具有强引用的对象;

软引用:只有在内存空间不足时,才会被回的对象;

弱引用:在 GC 时,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存;

虚引用:任何时候都可以被GC回收

我们常说的内存泄漏是指new出来的Object无法被GC回收,即为强引用

Android常见的内存泄漏案例

单例造成的内存泄漏

由于单例静态特性使得单例的生命周期和应用的生命周期一样长。如果一个对象已经不需要使用了,而单例对象还持有该对象的引用,那么这个对象将不能被正常回收,导致了内存泄漏。如下这个典例:

Android性能优化之常见的内存泄漏_第1张图片
单例造成的内存泄漏

这是一个普通的单例模式,当创建这个单例的时候,由于需要传入一个Context,所以这个Context的生命周期的长短至关重要:

1、传入的是Application的Context:这将没有任何问题,因为单例的生命周期和Application的一样长

2、传入的是Activity的Context:当这个Context所对应的Activity退出时,由于单例对象还持有该Activity的引用,该Activity不能被正常回收。

所以正确的单例应该修改为下面这种方式:

Android性能优化之常见的内存泄漏_第2张图片
修改后的单例

这样不管传入什么Context最终将使用Application的Context,而单例的生命周期和应用的一样长,这样就防止了内存泄漏。

二、非静态内部类创建静态实例造成的内存泄漏

有时候我们可能会出现这样的写法

Android性能优化之常见的内存泄漏_第3张图片
非静态内部类创建静态实例

这样就在Activity内部创建了一个非静态内部类的单例,每次启动Activity时都会使用该单例的数据,这样虽然避免了资源的重复创建,不过这种写法却会造成内存泄漏,因为非静态内部类默认会持有外部类的引用,而又使用了该非静态内部类创建了一个静态的实例,该实例的生命周期和应用的一样长,这就导致了该静态实例一直会持有该Activity的引用,导致Activity的内存资源不能正常回收。

正确的做法为:将该内部类设为静态内部类或将该内部类抽取出来封装成一个单例,如果需要使用Context,使用ApplicationContext

Handler造成的内存泄漏

Handler的使用造成的内存泄漏问题应该说最为常见了,平时在处理网络任务或者封装一些请求回调等api都应该会借助Handler来处理,对于Handler的使用代码编写一不规范即有可能造成内存泄漏,如下示例:

Android性能优化之常见的内存泄漏_第4张图片
Handler造成的内存泄漏

这种创建Handler的方式会造成内存泄漏,由于mHandler是Handler的非静态匿名内部类的实例,所以它持有外部类Activity的引用,而消息队列是在一个Looper线程中不断轮询处理消息,那么当这个Activity退出时消息队列中还有未处理的消息或者正在处理消息,而消息队列中的Message持有mHandler实例的引用,mHandler又持有Activity的引用,所以导致该Activity的内存资源无法及时回收,引发内存泄漏,所以另外一种做法为:

Android性能优化之常见的内存泄漏_第5张图片
处理Handler造成的内存泄漏

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

Android性能优化之常见的内存泄漏_第6张图片
移除消息队列中的消息

使用mHandler.removeCallbacksAndMessages(null);是移除消息队列中所有消息和所有的Runnable。

四、资源未关闭造成的内存泄漏

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

10 条提升 Android 性能的建议

https://academy.realm.io/cn/posts/droidcon-farber-improving-android-app-performance/

你可能感兴趣的:(Android性能优化之常见的内存泄漏)