Files
mkdocs/docs/Obsidian笔记体系/Areas/06-性能优化体系/内存优化(LeakCanary原理).md
renjianbo 8a4717277d 测试
2026-01-12 17:14:58 +08:00

9.0 KiB
Raw Blame History

内存优化LeakCanary原理

概述

内存泄漏是Android开发中的常见问题会导致应用内存占用持续增长最终可能引发OOMOut of Memory崩溃。LeakCanary是Square公司开源的内存泄漏检测工具本文档深入解析其工作原理和使用方法。

内存泄漏基础

什么是内存泄漏

内存泄漏是指程序在运行过程中,由于某些原因导致已分配的内存无法被回收,造成内存浪费和潜在的性能问题。

Android中的内存泄漏场景

  1. 静态变量持有Context
// 错误示例
public class MyApplication {
    private static Context sContext; // 持有Activity引用
}

// 正确做法
public class MyApplication {
    private static Context sAppContext; // 使用Application Context
}
  1. 非静态内部类持有外部类引用
// 错误示例
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<MainActivity> mActivityRef;
        
        MyHandler(MainActivity activity) {
            mActivityRef = new WeakReference<>(activity);
        }
        
        @Override
        public void handleMessage(Message msg) {
            MainActivity activity = mActivityRef.get();
            if (activity != null) {
                // 使用activity
            }
        }
    }
}
  1. 监听器未注销
// 错误示例
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);
}
  1. 资源未关闭
// 错误示例
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

// 监控对象是否被回收
public class ObjectWatcher {
    private final ReferenceQueue<Object> queue = new ReferenceQueue<>();
    private final Map<String, KeyedWeakReference> watchedObjects = new HashMap<>();
    
    public void watch(Object watchedObject, String referenceName) {
        KeyedWeakReference reference = new KeyedWeakReference(
            watchedObject, referenceName, queue, clock);
        watchedObjects.put(key, reference);
    }
}

2. HeapDumper

// 堆转储
public interface HeapDumper {
    File dumpHeap();
}

3. HeapAnalyzer

// 分析堆转储文件
public class HeapAnalyzer {
    public AnalysisResult checkForLeaks(File heapDumpFile) {
        // 使用Shark库分析hprof文件
        // 查找GC Root到泄漏对象的路径
    }
}

LeakCanary集成

添加依赖

dependencies {
    // LeakCanary
    debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.12'
}

自动安装(无需代码)

LeakCanary 2.0+ 使用ContentProvider自动初始化无需手动配置。

手动监控

// 监控自定义对象
LeakCanary.INSTANCE.getObjectWatcher().watch(
    myObject, 
    "MyObject被监控"
);

常见内存泄漏案例

1. Handler泄漏

问题代码:

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);
    }
}

解决方案:

public class MainActivity extends Activity {
    private static class MyHandler extends Handler {
        private WeakReference<MainActivity> 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

问题代码:

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;
    }
}

解决方案:

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. 匿名内部类

问题代码:

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();
    }
}

解决方案:

public class MainActivity extends Activity {
    private static class MyRunnable implements Runnable {
        private WeakReference<MainActivity> 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

// 优先使用Application Context
Context appContext = context.getApplicationContext();

2. 及时释放资源

@Override
protected void onDestroy() {
    super.onDestroy();
    // 注销监听器
    // 取消网络请求
    // 停止动画
    // 释放资源
}

3. 使用WeakReference

// 对于可能持有Activity引用的对象使用WeakReference
private WeakReference<Activity> mActivityRef;

4. 避免静态变量持有View/Context

// 错误
private static TextView sTextView;

// 正确使用WeakReference或避免静态变量

5. 使用对象池

// 复用对象减少GC压力
ObjectPool<MyObject> 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
  • 内存抖动: 避免频繁分配/释放

相关链接