非静态内部类隐式持有对外部类对象的引用
示例代码:
public class Outer {
int outerField = 0;
/**
* 非静态内部类
*/
class Inner {
void innerMethod() {
System.out.println("outerField: " + outerField);
}
}
}
调用内部类中的方法,断点调试:
可以看到,内部类中持有外部类的一个对象 this$0
Handler 内存泄露
原因
Handler 匿名内部类或者非静态内部类持有对外部类对象的强引用,外部类对象销毁后占据的内存无法释放。
引用链
ActivityThread -> Looper -> MessageQueue -> Message -> Handler -> 外部类(例如:Activity、Service等)。
解决方法
原理
阻断引用链
具体方法
- Handler 改为静态内部类,静态内部类不会隐式持有对外部类对象的引用,阻断 Handler -> 外部类 引用环节。
- Handler 持有对外部类弱引用对象的引用,将 Handler -> 外部类 这一环节变为弱引用,弱引用在 JVM 执行 GC 时会被回收。
- 当外部类对向销毁时,清除消息队列中的所有消息,消息在被清除时,会释放
Message -> Handler 引用。
参考
从源码角度看Handler为何会发生内存泄露
深入理解Java中为什么内部类可以访问外部类的成员
Java内存泄漏系列--匿名内部类导致内存泄露--原因/解决方案
强引用 弱引用 软引用 虚引用 的区别以及使用场景