15、Logcat 与性能分析:GC 日志、CPU 负载、内存抖动的识别
性能分析,说白了就是找茬。找系统运行时的各种不顺畅。
Logcat 在这块儿是个宝。很多开发者觉得它只是看报错信息的,其实不然。我个人习惯,在性能调优阶段,Logcat 是我第一个打开的工具。它能直接告诉你系统在「喘气」还是「卡壳」。
15.1 GC 日志:读懂系统的「呼吸」
GC,垃圾回收。Java 和 Kotlin 的世界里,内存回收是自动的。但自动不代表免费。GC 发生时,所有线程都可能被暂停——这就是传说中的「STW」(Stop The World)。
怎么在 Logcat 里看 GC?很简单。过滤关键字 GC 或者 art。你会看到类似这样的输出:
I/art: Explicit concurrent mark sweep GC freed 2047(64KB) AllocSpace objects, 0(0B) LOS objects, 49% free, 2MB/4MB, paused 1.230ms total 35.789ms
这一行信息量很大。我来拆解一下:
- Explicit concurrent:显式触发的并发 GC。说白了就是有人调用了
System.gc()。我在项目中遇到过,某些第三方 SDK 特别喜欢干这事儿,结果导致界面掉帧。 - freed 2047(64KB):回收了 2047 个对象,释放了 64KB 内存。嗯,这里要注意,如果释放量很小,说明内存管理还不错。
- 49% free, 2MB/4MB:堆内存使用情况。49% 空闲,总共 4MB,用了 2MB。这个比例如果低于 10%,说明内存快爆了。
- paused 1.230ms total 35.789ms:暂停时间 1.23 毫秒,总耗时 35.789 毫秒。暂停时间越短越好,超过 5ms 就可能掉帧。
关键指标:重点关注 paused 时间和 % free。如果 paused 经常超过 3ms,你的 UI 线程大概率在受苦。
我曾经接手过一个项目,首页列表滑动卡成 PPT。打开 Logcat 一看,GC 日志每两秒就刷一次,paused 时间平均 8ms。嗯,找到根儿了——内存泄漏导致 GC 频繁触发。
15.2 CPU 负载:别让系统「满负荷」
CPU 负载高,手机发烫,电量狂掉。怎么用 Logcat 看?
过滤关键字 CPU 或者 Load。系统会定期输出 CPU 使用情况:
I/ActivityManager: CPU: 12% user, 8% kernel, 80% idle
这个输出告诉你三件事:
- user:用户态 CPU 占用。你的 App 代码跑在这里。
- kernel:内核态 CPU 占用。系统调用、驱动等。
- idle:空闲率。低于 30% 就要警惕了。
你想想看,如果 idle 只有 10%,说明 CPU 快被榨干了。这时候任何操作都会卡。
我的小技巧:在 Logcat 里加一个过滤器 package:com.your.app + CPU,只看自己进程的 CPU 负载。这样能排除其他 App 的干扰。
还有一种情况,某个线程在死循环。你会看到 CPU 负载突然飙到 90% 以上,而且持续不降。这时候用 ps -T 配合 Logcat 看线程名,能快速定位问题线程。
15.3 内存抖动:看不见的「锯齿」
内存抖动,英文叫 Memory Churn。说白了就是内存频繁分配和释放,导致 GC 不断触发。
怎么在 Logcat 里识别?看 GC 日志的频率。如果每秒钟出现多次 GC 日志,那就是抖动。
举个例子,你写了个循环:
for (i in 0..1000) {
val str = "Item $i" // 每次循环都创建新字符串
list.add(str)
}
这段代码在循环里频繁创建对象。如果循环次数多,GC 就会被反复触发。Logcat 里会看到:
I/art: Background partial concurrent mark sweep GC freed 1000 objects
I/art: Background partial concurrent mark sweep GC freed 1000 objects
I/art: Background partial concurrent mark sweep GC freed 1000 objects
嗯,这就是典型的抖动。我见过最夸张的一次,某个页面在 5 秒内触发了 30 次 GC,界面直接卡死。
避坑指南:我曾经在列表的 onBindViewHolder 里直接拼接字符串,导致每滚动一屏就触发多次 GC。后来改用 StringBuilder,GC 频率直接降了 80%。
15.4 知识体系总览
下面这张图,是我自己总结的 Logcat 性能分析框架。你可以把它当成一个检查清单:
15.5 实战:一次完整的性能排查
我记得有一次,用户反馈某个页面「点不动」。我打开 Logcat,做了三步:
- 看 GC 频率:过滤
GC,发现每 500ms 就触发一次。不正常。 - 看 CPU 负载:过滤
CPU,发现 idle 只有 15%。CPU 被吃满了。 - 定位代码:用
ps -T找到 CPU 占用最高的线程,发现是RenderThread在疯狂重绘。
最终原因是什么?一个自定义 View 在 onDraw 里 new 了 Paint 对象。每次绘制都创建新对象,导致 GC 不断触发,CPU 忙于回收,UI 线程被频繁暂停。
我的建议:把 Logcat 当成你的「听诊器」。性能问题出现时,先听 GC 的「心跳」,再看 CPU 的「呼吸」,最后查内存的「脉搏」。三步走,基本能定位 90% 的问题。
好了,这一章的内容就到这里。Logcat 在性能分析上的能力远不止这些,但掌握了 GC、CPU 和内存抖动这三个维度,你已经能解决大部分日常问题了。