内存泄漏原因
内存泄漏是指程序在申请内存后,无法被释放或归还给系统的现象。在Android中,内存泄漏导致的后果会使得应用Crash。
常见的内存泄漏场景有:全局集合类强引用没清理会造成内存泄漏(特别是static修饰的集合)、静态成员变量、单例类、非静态内部类/匿名内部类等。
解决方案包括:内部子类改为静态内部类、使用弱引用、使用HandlerThread、使用AsyncTask、使用WeakHashMap等。
Handler 可能存在的内存泄漏情况
Handler 允许我们发送延时消息,如果在延时期间用户关闭了 Activity,那么该 Activity 会泄露。
这个泄露是因为 Message 会持有 Handler,而又因为 Java 的特性,内部类会持有外部类,使得 Activity 会被 Handler 持有,这样最终就导致 Activity 泄露。
解决方案:将 Handler 定义成静态的内部类,在内部持有 Activity 的弱引用,并及时移除所有消息。
public class HandlerActivity extends AppCompatActivity {
private Button bt_handler_send;
private static class MyHandler extends Handler {
//弱引用持有HandlerActivity , GC 回收时会被回收掉
private WeakReference<HandlerActivity> weakReference;
public MyHandler(HandlerActivity activity) {
this.weakReference = new WeakReference(activity);
}
@Override
public void handleMessage(Message msg) {
HandlerActivity activity = weakReference.get();
super.handleMessage(msg);
if (null != activity) {
//执行业务逻辑
Toast.makeText(activity,"handleMessage",Toast.LENGTH_SHORT).show();
}
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.HandlerActivity);
//创建 Handler
final MyHandler handler = new MyHandler(this);
bt_handler_send = findViewById(R.id.bt_handler_send);
bt_handler_send.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread(new Runnable() {
@Override
public void run() {
//使用 handler 发送空消息
handler.sendEmptyMessage(0);
}
}).start();
}
});
}
@Override
protected void onDestroy() {
//移除所有回调及消息
myHandler.removeCallbacksAndMessages(null);
super.onDestroy();
}
}
排查内存泄露的工具
MAT
MAT(Memory Analyzer Tool)工具是一款功能强大的Java堆内存分析器。
MAT可以分析heap dump文件。在进行内存分析时,只要获得了反映当前设备内存映像的hprof文件,通过MAT打开就可以直观地看到当前的内存信息。一般说来,这些内存信息包含:所有的对象信息,包括对象实例、成员变量、存储于栈中的基本类型值和存储于堆中的其他对象的引用值。所有的类信息,包括classloader、类名称、父类、静态变量 GCRoot到所有的这些对象的引用路径线程信息,包括线程的调用栈及此线程的线程局部变量(TLS)
缺点:MAT不是一个万能工具,它并不能处理所有类型的堆存储文件。但是比较主流的厂家和格式,例如SuN,HP,SAP所采用的HPROF二进制堆存储文件,以及IBM的PHD堆存储文件等都能被很好的解析。
LeakCanary
LeakCanary 本质上是一个基于 MAT(Memory Analyzer Tool)进行 Android 应用程序内存泄漏自动化检测的的开源工具。
LeakCanary原理:将 Activity 包装到 WeakReference 中,对象如果能够被回收,则说明引用可达,垃圾回收器就会将该 WeakReference 引用存放到 ReferenceQueue 中;否则可能发生了内存泄漏。