LeakCanary

关注
义项:Android内存泄漏检测工具
LeakCanary

LeakCanary是面向Android与Java平台的开源内存泄漏检测库,可在应用开发阶段自动发现被意外保留、无法被垃圾回收的对象,并给出导致保留的引用路径,帮助开发者定位泄漏原因。它通过监听Activity、Fragment等组件的生命周期,在对象应被回收时以弱引用观察其是否仍被持有,达到阈值后转储堆内存并借助自研的Shark引擎在设备上完成分析。该库由Square开源,通常只在调试构建中使用,是Android开发中常用的内存诊断工具之一。

百科 图文
目录
  1. 定义
  2. 原理
  3. 发展历程
  4. 应用
  5. 局限
  6. 参见

定义

LeakCanary是一个用于Android和Java的内存泄漏检测库,其目标是发现应用中不再需要却仍被引用、因而无法被垃圾回收的对象[1]。在Java类运行时中,内存泄漏指程序持续持有已无用途对象的引用,使这部分内存无法被回收,泄漏不断累积最终会引发OutOfMemoryError崩溃[2][3]。

该工具的价值在于把内存泄漏的排查过程自动化。启用后,它无需开发者逐行阅读堆转储文件,就能自动识别可疑对象、生成从垃圾回收根到泄漏对象的引用链,并按引用链特征把同一类泄漏归为一组。

原理

检测的第一步是识别被保留的对象。LeakCanary通过Android生命周期回调得知Activity或Fragment已经销毁,把这些本应被回收的实例交给一个对象观察器统一持有,观察器保存的只是弱引用,不会妨碍垃圾回收。如果等待5秒并主动触发一次垃圾回收后弱引用仍未清除,该对象就被判定为保留对象,即存在泄漏嫌疑。

LeakCanary会累计保留对象的数量,达到阈值后才开始转储堆内存,以免频繁冻结应用。默认阈值是应用可见时为5个保留对象、不可见时为1个,点击通知可以强制立即转储。转储得到的.hprof文件由Shark解析,Shark在堆图中定位保留对象,并计算出阻止其被回收的最短强引用路径,也就是泄漏轨迹。

得到泄漏轨迹后,LeakCanary借助对Android框架内部结构的了解,判断链路上每个对象当前是否应该留在内存中。如果某个对象应当存在,则位于它上方的对象也应当存在;如果某个对象不应存在,则位于它下方的对象同样不应存在,依据这两条规则可以一次排除多行引用[4]。剩余的可疑引用会以波浪下划线标出,泄漏指纹由这些可疑引用拼接后取哈希生成,指纹相同的泄漏被归为同一组。

最终结果分为应用泄漏与库泄漏两类。库泄漏指由第三方代码或Android框架中的已知缺陷造成的泄漏,应用开发者通常无法直接修复,因此被单独列出,并以模式匹配的方式维护在已知泄漏列表中[1]。

发展历程

LeakCanary出现之前,Square的工程师排查内存问题的做法是随机转储一次堆、把文件从设备导出后用Eclipse MAT打开查看,但多数转储看不出问题,即便看出问题也已经是代码改动之后很久。真正的缺口不是分析手段,而是判断何时值得转储的触发机制[5]。

2015年3月,Square的Android收款应用中出现了该工具的第一个形态:一个仅有154行的调试专用类,它监听已销毁的Activity,等待5秒、强制垃圾回收,若对象仍存在则转储堆并发出通知[5]。2015年4月5日,这段检测代码从应用中抽出成为独立仓库,并开始使用Eclipse MemoryAnalyzer的无头版本进行分析[5]。此后分析能力被迁移到设备端,负责转储堆的代码可以直接给出堆中的内容[5]。

早期版本需要开发者在Application类中手工装配各个组件,后来装配代码被收敛为一行安装调用;2019年11月27日发布的2.0版本进一步取消了这一行代码,改由清单中声明的ContentProvider在应用启动时自动完成初始化[5][1]。2.0版本是一次彻底重写,代码从约6000行Java变为约16000行Kotlin,并引入了自研堆分析引擎Shark取代此前依赖的解析器[6]。

3.0系列以alpha版本推进,增加了堆增长检测能力,用于发现对象本身不泄漏但反复累积的情况,同时把最低支持的Android版本提升到8.0[6]。

应用

LeakCanary主要用在应用的调试构建中,只需在构建配置里加入一个调试依赖,库便会自动开始工作,发布构建中不包含它,也不产生额外方法数[1]。默认监视的对象包括已销毁的Activity、已销毁的Fragment、已销毁的Fragment视图以及已清空的ViewModel。

除了自动监视,开发者也可以手动把任意不再需要的对象交给观察器,例如已分离的视图或已销毁的Presenter,从而把他类对象纳入检测范围。

该库也被集成到自动化测试流程中。在仪器化测试里可以调用断言方法检查是否存在泄漏,一旦发现泄漏就让测试失败,从而在持续集成阶段拦截新引入的问题[7]。对应的JUnit规则可以在测试成功结束后自动执行一次泄漏检查[7]。此外,检测到泄漏时LeakCanary会为应用添加一个启动器图标,点击图标可以查看按泄漏指纹分组的列表和每条泄漏的引用链详情。

局限

LeakCanary定位为调试期工具,不适合作为线上监控方案。每次发现泄漏都要生成并解析hprof文件,期间应用会被短暂冻结,多次触发垃圾回收也可能影响性能,且堆转储文件体积较大,不便于回传上报[1]。

检测范围也有边界。它观察的是Java堆中对象的可达性,无法覆盖那些并非由对象保留引起的内存问题[1]。分析依赖堆转储,而堆转储本身是有成本的,因此该库设置了保留对象数量阈值,达到阈值才开始转储,这也意味着单个孤立的小泄漏可能不会被立即报告。

部分泄漏并非应用代码的问题。Android框架和厂商实现中存在已知的内存泄漏,遇到这类情况应用开发者往往难以修复,LeakCanary将其归入库泄漏并内置匹配列表予以识别[1]。分析本身也可能遇到困难,当对象在堆中已无法从垃圾回收根找到强引用路径时,LeakCanary只能报告其为不可达对象[6]。

使用上还有若干容易造成误判的情形。它会在检测到测试环境(例如类路径中出现JUnit)时自动关闭堆转储与分析,若应用自身在调试类路径中携带了JUnit,会导致检测被意外禁用,此时需要指定用于识别测试环境的类[1][6]。当泄漏轨迹信息不足时,仍需要借助外部工具进一步深入堆转储进行排查[1]。

参见

  • 垃圾回收 —— LeakCanary判断对象是否泄漏的依据,正是垃圾回收后对象能否被回收

  • Android —— LeakCanary检测内存泄漏所针对的运行平台

  • 内存泄漏 —— LeakCanary所要发现和定位的编程错误

  • 堆转储 —— LeakCanary分析泄漏时依赖的内存快照文件

  • Shark —— LeakCanary内置的堆分析引擎,负责解析堆转储并计算引用路径

参考资料

  1. FAQ [引用日期2026-10-14]
  2. Introduction [引用日期2026-10-14]
  3. Design decisions¶ [引用日期2026-10-14]
  4. Fixing a memory leak [引用日期2026-10-14]
  5. History¶ [引用日期2026-10-14]
  6. Change Log¶ [引用日期2026-10-14]
  7. Leak detection in UI tests [引用日期2026-10-14]
词条评价
词条统计

浏览次数:3 次

阅读量:0 次 · 阅读完成量:0 次

最近更新:2026-10-13T21:14:21Z

历史版本

本条目引用的词条
垃圾回收 Android 内存泄漏 堆转储 Shark