LeakCanary
LeakCanary 通过 hook Android 的生命周期来自动检测 Activity 和 Fragment 何时被销毁,何时应该被垃圾回收,这些被 destroy 的对象被传递给 ObjectWatcher,ObjectWatcher 持有对它们的弱引用
检测对象类型
- 已销毁的 Activity 实例
- 销毁的 Fragment 实例
- 销毁的 Fragment View 实例
- 已清除的 ViewModel实例
弱引用与垃圾回收
弱引用(WeakReference)可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收器回收,虚拟机就会把这个弱引用加入到与之关联的引用队列中,我们可以用此特性来检查一个对象是否被垃圾回收器回收成功
强引用
-般的obiect obj= new Object0),就属于强引用。在任何情况下,只有有强引用关联(与根可达)还在,垃圾回收器就永远不会回收掉被引用的对象。
软引用 SoftReference
- 一些有用但是并非必需,用软引用关联的对象,系统将要发生内存溢出(0uyOfMemory)之前,这些对象就会被回收(如果这次回收后还是没有足够的空间,才会抛出内存溢出)。
参见代码:
VM参数-Xms10m-Xmx10m -XX:+PrintGc
例如,一个程序用来处理用户提供的图片。如果将所有图片读入内存,这样虽然可以很快的打开图片,但内存空间使用巨大,一些佳用较少的图片浪费内存空间,需要手动从内存中移除。如果每次打开图片都从磁盘文件中读取到内存再显示出来,虽然内存占用较少,但一些经常使用的图片每次打开都要访问磁盘,代价巨大。这个时候就可以用软引用构建缓存。
弱引用WeakReference
- 一些有用(程度比软引用更低)但是并非必需,用弱引用关联的对象,只能生存到下一次垃圾回收之前,GC发生时,不管内存够不够,都会被回收。
- 当一个对象仅仅被WeakReference(弱引用)指向,而没有任何其他strong reference(强引用)指向的时候,如果这时GC运行,那么这个对象就会被回收,不论当前的内存空间是否足够,这个对象都会被回收。
- 注意:软引用 SoftReference和弱引用 WeakReference,可以用在内存资源紧张的情况下以及创建不是很重要的数据缓存。当系统内存不足的时候,缓存中的内容是可以被释放的。
- 实际运用(WeakHashap、ThreadLocal)
GC回收后,弱引用不存在代码示例:
fun main(){
val referenceQueue = ReferenceQueue<Any?>()
var obj:Any? = Object()
val weakReference = WeakReference(obj, referenceQueue)
println("before GC:${weakReference.get()}")
obj = null
System.gc()
Thread.sleep(1000)
println("after GC:${weakReference.get()}")
}
虚引用
- 幽灵引用,最弱(随时会被回收掉)
- 垃圾回收的时候收到一个通知,就是为了监控垃圾回收器是否正常工作。
防止内存泄漏
单例导致内存泄露
为了避免这样单例导致内存泄露,我们可以将context参数改为全局的上下文
fun appSettings(context:Context){
val applicationContext = context.applicationContext
}
静态变量导致内存泄漏
静态变量存储在方法区,它的生命周期从类加载开始,到整个进程结束。一旦静态变量初始化后,它所持有的引用只有等到进程结束才会释放。 比如下面这样的情况,在Activity中为了避免重复的创建info,将sInfo作为静态变量。
非静态内部类导致内存泄露
非静态内部类(包括匿名内部类)默认就会持有外部类的引用,当非静态内部类对象的生命周期比外部类对象的生命周期长时,就会导致内存泄露。非静态内部类导致的内存泄露在Android开发中有一种典型的场景就是使用Handler,很多开发者在使用Handler是这样写的。