</>FUNNYGUYZ

Android 性能优化与分析工具

#Profiler#Perfetto#LeakCanary#Coroutine
← 学习资料优先级:核心

核心原理讲解

Android Profiler 怎么用?

在 AS 里,Profiler 主要分 CPU、Memory、Network 三大面板。

  • CPU Profiler:用来找卡顿和方法耗时。一般用 Sampled(抽样)模式 抓一段 Trace,生成火焰图(Flame Chart)。火焰图里越宽的方块代表这个方法执行时间越长,我们可以顺着它一层层往下看,找出最底层的耗时热点。

图片 02-1:CPU Profiler Flame Chart 火焰图与方法耗时比例示意图

  • Memory Profiler:用来排查内存抖动和泄漏。在里面可以直接看实时分配的对象。如果发现内存曲线呈“锯齿状”高频起伏,说明有内存抖动(短时间内高频创建并销毁大对象,比如在 onDraw 里 new 对象或频繁拼接 String),这会引发高频 GC 导致卡顿。

Perfetto / Systrace 的读图思路

它们是系统级的性能分析工具,基于 Linux 内核的 ftrace 机制。

  • 怎么读图:抓出来的 Trace 是一个多轨时间线视图。首先看 UI ThreadRenderThread。如果发现某一帧超过了 16.6ms(60Hz)或 11.1ms(90Hz),对应的 Frame 轨道就会标红。
  • 然后看主线程这一帧里在干什么。如果阻塞在 monitor contention(锁竞争)或进程间的 Binder 调用(binder transaction),那主线程就会被挂起(处于 Blocked 状态),这就是导致卡顿的真凶。如果是 Choreographer#doFrame 下面的 performTraversals(绘制三大流程)耗时过长,说明是布局太深或自定义 View 绘制太重。

图片 02-2:Systrace 渲染时间线、主线程 Blocked 锁竞争及 Binder 调用阻塞原理图

LeakCanary 的检测原理

可以用一句话概括:弱引用关联引用队列 + 主动 GC 校验

图片 02-3:LeakCanary 弱引用队列检测与堆分析堆栈定位流程

  1. 自动检测销毁:LeakCanary 注册了 Activity 和 Fragment 的生命周期监听。当它们执行 onDestroy 时,LeakCanary 会把这个销毁的对象包装成一个 WeakReference(弱引用),并关联一个 ReferenceQueue(引用队列)。
  2. 触发检测:过 5 秒钟后,系统会收到检测通知。它先通过 GC 机制让对象去回收,然后检查 ReferenceQueue
  3. 判断泄漏:如果该对象的弱引用没有出现在 ReferenceQueue 里,说明这个已经被销毁的对象仍然被外界强引用着,判定为内存泄漏。
  4. 生成引用链:判定泄漏后,LeakCanary 会在后台进程进行 Heap Dump 生成 .hprof 文件,利用 Shark 库分析堆内存,计算出泄漏对象到 GC Root 的最短路径引用链。

协程内存泄漏的常见模式

协程是用户态的,但它的挂起点是通过 Continuation(状态机)保存在堆上的。 如果一个协程的 CoroutineScope 生命期太长(比如滥用 GlobalScope 或把协程跑在了一个没有随 Activity/ViewModel 销毁而 cancel 的 Scope 里),那么这个挂起的协程就会一直留在内存中。它所持有的所有局部变量、外部类引用(比如 Activity 里的 UI 控件、ViewModel 实例)都会被这个 Continuation 强引用着,从而导致严重的内存泄漏。


工程注意事项

  • [!IMPORTANT] 网络库配置细节:取消网络请求并不等于立即关闭连接池中的 TCP 连接。对于仍可复用的连接,OkHttp 会将其保留在连接池中,等待后续请求复用;连接最终何时释放,取决于连接状态、服务端响应和连接池配置。

  • [!WARNING] GC 机制防坑:很多人以为调用 System.gc() 对象就会立刻回收。实际上,System.gc() 只是向 JVM 建议进行一次 Full GC,至于系统什么时候真的执行、回不回收,是不可控的。LeakCanary 底层也是先通过多次触发 GC 建议,再等待数秒,才去确认弱引用是否被真正回收。