# 内存优化(LeakCanary原理) ## 概述 内存泄漏是Android开发中的常见问题,会导致应用内存占用持续增长,最终可能引发OOM(Out of Memory)崩溃。LeakCanary是Square公司开源的内存泄漏检测工具,本文档深入解析其工作原理和使用方法。 ## 内存泄漏基础 ### 什么是内存泄漏 内存泄漏是指程序在运行过程中,由于某些原因导致已分配的内存无法被回收,造成内存浪费和潜在的性能问题。 ### Android中的内存泄漏场景 1. **静态变量持有Context** ```java // 错误示例 public class MyApplication { private static Context sContext; // 持有Activity引用 } // 正确做法 public class MyApplication { private static Context sAppContext; // 使用Application Context } ``` 2. **非静态内部类持有外部类引用** ```java // 错误示例 public class MainActivity extends Activity { private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { // 隐式持有MainActivity引用 } }; } // 正确做法:使用静态内部类 + WeakReference public class MainActivity extends Activity { private static class MyHandler extends Handler { private WeakReference mActivityRef; MyHandler(MainActivity activity) { mActivityRef = new WeakReference<>(activity); } @Override public void handleMessage(Message msg) { MainActivity activity = mActivityRef.get(); if (activity != null) { // 使用activity } } } } ``` 3. **监听器未注销** ```java // 错误示例 public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); EventBus.getDefault().register(this); // 注册但未注销 } } // 正确做法 @Override protected void onDestroy() { super.onDestroy(); EventBus.getDefault().unregister(this); } ``` 4. **资源未关闭** ```java // 错误示例 FileInputStream fis = new FileInputStream(file); // 使用后未关闭 // 正确做法:使用try-with-resources try (FileInputStream fis = new FileInputStream(file)) { // 使用fis } // 自动关闭 ``` ## LeakCanary工作原理 ### 核心机制 LeakCanary通过以下步骤检测内存泄漏: 1. **对象引用监控**: 监控Activity/Fragment等关键对象的生命周期 2. **弱引用检测**: 使用WeakReference + ReferenceQueue检测对象是否被回收 3. **堆转储分析**: 当检测到泄漏时,dump堆内存进行分析 4. **泄漏路径分析**: 使用Shark库分析GC Root到泄漏对象的引用链 ### 工作流程 ``` Activity创建 ↓ LeakCanary.watch() 监控 ↓ Activity销毁 ↓ 等待5秒(GC时间) ↓ 检查对象是否被回收 ↓ 未回收 → 触发堆转储 ↓ 分析引用链 ↓ 生成泄漏报告 ``` ### 源码关键点 #### 1. ObjectWatcher ```java // 监控对象是否被回收 public class ObjectWatcher { private final ReferenceQueue queue = new ReferenceQueue<>(); private final Map watchedObjects = new HashMap<>(); public void watch(Object watchedObject, String referenceName) { KeyedWeakReference reference = new KeyedWeakReference( watchedObject, referenceName, queue, clock); watchedObjects.put(key, reference); } } ``` #### 2. HeapDumper ```java // 堆转储 public interface HeapDumper { File dumpHeap(); } ``` #### 3. HeapAnalyzer ```java // 分析堆转储文件 public class HeapAnalyzer { public AnalysisResult checkForLeaks(File heapDumpFile) { // 使用Shark库分析hprof文件 // 查找GC Root到泄漏对象的路径 } } ``` ## LeakCanary集成 ### 添加依赖 ```gradle dependencies { // LeakCanary debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.12' } ``` ### 自动安装(无需代码) LeakCanary 2.0+ 使用ContentProvider自动初始化,无需手动配置。 ### 手动监控 ```java // 监控自定义对象 LeakCanary.INSTANCE.getObjectWatcher().watch( myObject, "MyObject被监控" ); ``` ## 常见内存泄漏案例 ### 1. Handler泄漏 **问题代码**: ```java public class MainActivity extends Activity { private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { // Handler持有MainActivity的隐式引用 } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 发送延迟消息 mHandler.sendEmptyMessageDelayed(0, 60000); } } ``` **解决方案**: ```java public class MainActivity extends Activity { private static class MyHandler extends Handler { private WeakReference mActivityRef; MyHandler(MainActivity activity) { mActivityRef = new WeakReference<>(activity); } @Override public void handleMessage(Message msg) { MainActivity activity = mActivityRef.get(); if (activity == null) { return; } // 处理消息 } } private MyHandler mHandler = new MyHandler(this); @Override protected void onDestroy() { super.onDestroy(); mHandler.removeCallbacksAndMessages(null); } } ``` ### 2. 单例持有Context **问题代码**: ```java public class AppManager { private static AppManager sInstance; private Context mContext; private AppManager(Context context) { mContext = context; // 可能持有Activity } public static AppManager getInstance(Context context) { if (sInstance == null) { sInstance = new AppManager(context); } return sInstance; } } ``` **解决方案**: ```java public class AppManager { private static AppManager sInstance; private Context mContext; private AppManager(Context context) { // 使用Application Context mContext = context.getApplicationContext(); } public static AppManager getInstance(Context context) { if (sInstance == null) { sInstance = new AppManager(context); } return sInstance; } } ``` ### 3. 匿名内部类 **问题代码**: ```java public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 匿名内部类持有外部类引用 new Thread(new Runnable() { @Override public void run() { // 长时间运行的任务 while (true) { // ... } } }).start(); } } ``` **解决方案**: ```java public class MainActivity extends Activity { private static class MyRunnable implements Runnable { private WeakReference mActivityRef; MyRunnable(MainActivity activity) { mActivityRef = new WeakReference<>(activity); } @Override public void run() { MainActivity activity = mActivityRef.get(); if (activity == null) { return; } // 执行任务 } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); new Thread(new MyRunnable(this)).start(); } } ``` ## 内存优化最佳实践 ### 1. 使用Application Context ```java // 优先使用Application Context Context appContext = context.getApplicationContext(); ``` ### 2. 及时释放资源 ```java @Override protected void onDestroy() { super.onDestroy(); // 注销监听器 // 取消网络请求 // 停止动画 // 释放资源 } ``` ### 3. 使用WeakReference ```java // 对于可能持有Activity引用的对象,使用WeakReference private WeakReference mActivityRef; ``` ### 4. 避免静态变量持有View/Context ```java // 错误 private static TextView sTextView; // 正确:使用WeakReference或避免静态变量 ``` ### 5. 使用对象池 ```java // 复用对象,减少GC压力 ObjectPool pool = new ObjectPool<>(10); MyObject obj = pool.acquire(); // 使用后归还 pool.release(obj); ``` ## 内存分析工具 ### 1. Android Profiler - 实时内存监控 - 堆转储分析 - 内存分配追踪 ### 2. MAT (Memory Analyzer Tool) - 分析hprof文件 - 查找内存泄漏 - 查看对象引用关系 ### 3. LeakCanary - 自动检测泄漏 - 可视化引用链 - 开发阶段使用 ## 内存优化指标 - **峰值内存**: 根据设备配置设定上限(如512MB) - **内存泄漏**: 0个 - **GC频率**: 尽量减少Full GC - **内存抖动**: 避免频繁分配/释放 ## 相关链接 - [[README]] - [[09-调试与工具链]] - [[05-进程与线程通信]]