一.什么是内存泄漏
内存泄漏
(英语:Memory leak)是在计算机科学中,由于疏忽或错误造成程序未能释放已经不再使用的内存。 — — — 维基百科
二.内存泄漏的影响
使得应用程序容易发生 OOM
- Android系统为每个应用分配的内存有限,若程序的发生的内存泄漏较多,会导致所需的内存超过系统所给的限额。
最终:OOM → Crash
三. GC Roots
对于使用可达性分析的垃圾回收算法来说,GC roots是一个比较特别的存在,它用来帮助GC判断哪些对象可以被回收,如果这个对象被是GC roots 或者被GC roots引用就不会被回收。
哪些可以作为GC roots?
- Class : 被系统ClassLoader加载的class,自定义的ClassLoader加载的class不是GC roots, 注意:静态变量是属于类的。
- Thread: 处于活动状态的线程.
- Stack Local: 方法中的变量和参数.
- JNI Local: JNI方法中的变量和参数.
- JNI Global: 全局的JNI reference,也就是JNI中全局创建的引用.
- Monitor Used: 同步的对象,例如被
Synchronized
锁住的对象. - Held by JVM: 取决于各个JVM的实现,系统的ClassLoader和JVM本身会用到的一些对象.
在开发中最常见的就是方法中的变量作为GC roots,如下代码:
我们创建了80M大小的内存区域并用变量 b指向这块区域,然后我们调用gc。
垃圾回收器首先进行了一次Minor GC,log中可以看到年轻代从3932K 降到了 560K,然后对象b转移到了老年代。
随后垃圾回收器又进行了一次Full GC,老年代中的内存从81.9M 降到 439K, 也就是我们创建的80M内存被正常回收了。
再看下面一段代码
可以看到在Full GC时,分配的80M内存并没有被回收。
原因: ByteArray对象被作为GC roots的变量b持有,无法被回收
四.内存泄漏 in Android
内存泄漏的根本原因在于对象始终被GC roots引用,或者本身作为GC roots当不在使用时没有正确置空或释放。
持有Activity的GC roots的引用链如下图
Activity被置空的时机:
在onDestory()方法执行时,ActivityThread通过handleDestroyActivity()方法将 Activity置空
那么Activity被引用者置空后,对GC roots就不可达,可以正常被回收。
1.Handler使用不规范
内部类/匿名内部类 隐式持有外部类的引用
Activity → Handler → Message → MessageQueue → Looper(),有Looper()存在于整个应用的生命周期,所以,如果Activity销毁时,Handler发送的消息还未得到Looper()处理,将导致Activity泄漏
内部类静态化 + WeakReference
- 若垃圾收集器发现一个对象是弱可达的(weakly reachable), 会清除所有指向该
对象的弱引用
- 通过static将内部类作为独立于外部类的Class
- 通过弱引用持有Activity,不耽误Activity回收
- 及时移除未处理的消息以及callback
消除message对Handler的引用
2.匿名内部类
- AsyncTask
- Thread
- 匿名接口回调
匿名内部类与内部类都会隐式持有外部类引用
3.单例模式
单例模式的生命周期 = 整个app运行时,
若需要引用context,请使用applicationContext
4.注册的监听器未注销
- EventBus
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
EventBus.getInstance().register(this)
}
}
在Activity销毁时需unregister(this)
- SensorManger
void registerListener() {
SensorManager sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
Sensor sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ALL);
sensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_FASTEST);
}
View smButton = findViewById(R.id.sm_button);
smButton.setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View v) {
registerListener();
nextActivity();
}
});
在Activity销毁时需SensorManger.unregisterListener(this)
-
ContentObserver
getContentResolver().registerContentObserver( SOME_URI, true, yourObserver); getContentResolver().unregisterContentObserver(yourObserver);
5. 资源对象未及时关闭
- file
- cursor
- bitmap
- …
六.内存泄漏分析工具
-
Android Studio Monitor
-
LeakCanary