技术复核 · 2026-10-13。 本文案例与截图来自 2019 年。此次按 Android 8.1/9/16 的相关源码、Linux v6.6 文档及现行 Android 文档核查,保留原始图片和作者经验;修正将长 slice、核号、内存回收或输入节奏直接等同根因的结论。没有重新运行原设备或回放完整 Trace,缺少的现场条件不作补造。
在应用篇中,我们列举了应用侧的卡顿问题。这一篇沿系统服务、显示、调度和内存路径整理案例。各设备的硬件、内核与厂商实现不同,定位时应保留系统版本和具体配置。
手机厂商和 App 开发者通常会通过优化组、Matrix 或自有监控方案收集线索。系统权限有助于获取更多数据,但采集范围仍受构建类型、内核配置和数据源开关影响。
流畅度不能仅等同于平均 FPS。60 Hz 下的一秒平均 55 FPS,无法说明延迟集中在哪几帧,也不能直接代表所有人的感知;应检查目标节奏、实际呈现间隔和输入响应。静止界面不需要每个刷新周期提交新内容。本系列四篇如下:
- 0. Android 中的卡顿丢帧原因概述 - 方法论
- 1. Android 中的卡顿丢帧原因概述 - 系统篇
- 2. Android 中的卡顿丢帧原因概述 - 应用篇
- 3. Android 中的卡顿丢帧原因概述 - 低内存篇
Android 平台性能导致的性能案例
下面这些是我在 2019 年整理的实际案例。有些在开发阶段就暴露了,发给用户前还能解决;有些要长时间使用才出现,这一类往往更难查;还有一些只在特殊场景或硬件上出现。
部分能从图中确认等待、合成或回收活动;设备私有实现和最终根因,则需要原始 Trace、日志与源码共同验证。截图没有提供完整系统版本,不把案例归因推广为所有 Android 设备的固定行为。
这些图使用旧 Systrace,可结合 Systrace 系列 阅读。新设备优先采集 Perfetto;Android 12(API 31)起,在采到相应数据时结合 FrameTimeline 检查 expected/actual frame、deadline 与呈现关系。旧图颜色、长 slice、缺少 slice 都只是分析入口。
1.SurfaceFlinger 主线程耗时
SurfaceFlinger 协调图层与合成。如果关键工作未赶上对应帧的 deadline,可能延后呈现;不是某个主线程调用超过固定时长就必然丢一帧。HWC、GPU 和显示驱动可以异步工作,还要看 fence 与实际呈现关系。
dequeueBuffer 可能等待空闲 buffer、分配或同步;queueBuffer 是生产者提交队列的接口,不能把二者一律称为直接阻塞在 SurfaceFlinger 上的 Binder 调用。旧 BufferQueue 可能跨进程,Android 16 的 BLASTBufferQueue 则能建立本地队列并通过 transaction 提交 buffer。具体阻塞位置要看调用链。
前两图展示合成和队列节奏;第三图选区约 28.02 ms,除较长 dequeueBuffer 外还有 allocateBuffers。第四图显示等待后产帧节奏推迟,但不能仅靠这些截图确认是哪一层造成,也不能把整个选区当作 GPU 耗时。




2.屏下光感截图导致 SurfaceFlinger 渲染不及时
原文描述的是某款设备屏下环境光传感器方案,通过获取屏幕内容辅助估计环境光;原文把“屏下光感”写成“屏下指纹”的地方应更正。不能推广为所有屏下光感都频繁截图,也不能与指纹识别流程混为一谈。
如果这类厂商功能同步占用合成线程、锁或 GPU 资源,可能干扰正常帧。下图 211.867 ms 是多帧观察区间,没有显示明确的截图调用或传感器请求;要确认该归因,还需关联请求频率、capture 路径及开启/关闭该功能的受控对照。

3.HWC Service 执行耗时
这里应为 HWC(Hardware Composer)。图中是历史 android.hardware.graphics.composer@2.2-service 的 HIDL 路径,可见 PresentDisplay、DRM Commit/AtomicCommit 等区间。长调用可能包含驱动或 fence 等待,不等于 HWC 一直消耗 CPU。
合成、提交和扫描输出相互依赖,延迟可能向应用队列传播,但不能写成“HWC 一慢,SurfaceFlinger 下一帧必定不合成”。第三图的 CRTC 工作与 HWC 调用重叠,重叠本身也不能证明“CRTC 在等 HWC”;需查看实际等待点和完成信号。第四图是另一组进程记录,不当作同一次测量。




4.CRTC 执行耗时
图中的 crtc_commit 是显示驱动提交相关的内核线程;它的时间片不是面板扫描输出耗时的直接测量。图里同时出现较长 dequeueBuffer、setTransactionState 和 CRTC 活动,可以沿 Binder、锁、fence、驱动完成事件追踪依赖。
显示提交延迟可能让上游等待,但仅凭并行轨道不能确认所有阻塞都是 CRTC 导致,更不能保证“下一帧一定不会合成”。具体调用链随厂商驱动和 Android 版本变化。

5.CPU 调度问题
调度分析要同时看线程状态、CPU 拓扑、允许运行的 CPU、调度策略/优先级、频率与帧 deadline。核号不是核型,小核、低频或迁核本身都不是错误;能否及时完成关键工作才是判断依据。
关键任务的 CPU 容量与频率
当关键任务获得的算力不足时,确实可能错过 deadline。但下图 22.558 ms 是选中的 RenderingResponse 区间,截图未给出完整拓扑和线程状态,不能直接读作“小核上的单帧 CPU 耗时”。

第二图原注释标出 RenderThread 约 1.66 ms / 2.4 GHz 与 8.6 ms / 0.7 GHz。它提示检查频率和调度,却不是控制了相同工作量、缓存、核型与等待时间的单因素实验。可结合 Linux v6.6 EAS 文档 理解容量与能耗取舍;设备是否使用该策略仍需查内核。

优先级低未能及时获取 cpu 时间片导致卡顿
用户关键路径上的线程不一定具有较高调度优先级。若它长时间 Runnable,应检查允许 CPU 上的竞争、策略/优先级、cpuset/affinity 和资源限制;也要检查它是否在等另一个更低优先级的线程。不能只看到某个 nice 值或线程名称就确认优先级反转。
被 RT 线程抢占
这里 RT 指实时调度线程,例如 SCHED_FIFO/SCHED_RR,不是 RenderThread 的缩写,也不是按整个进程一概分类。实时线程占用同一可用 CPU 时,可能延迟普通线程;要用实际策略、抢占与运行区间确认。
Android 9 ActivityManagerService 中确有默认关闭的 sys.use_fifo_ui 路径,为 TOP_APP 的 UI/RenderThread 调整调度。但源码不能支持“因为启动更慢,所以所有版本一直不开”的结论,也不构成把应用线程统一改为实时调度的建议。
大小核调度导致
不合适的 affinity/cpuset 或任务放置可能限制可用算力。在这次 CTS 问题里,我当时的判断是:线程受限于 CPU 7,竞争导致测试超时。但截图没有显示 affinity mask、完整调度策略或 CTS 报告,不能单独验证该根因。
“只有一个核,所以 RenderThread 不做完 UI 线程就只能等”也过于绝对:同核普通线程仍可被抢占、分时运行。应检查实际可用 CPU、谁在运行、等待多久,以及测试期限。

6.触发 Thermal 导致限频
热管理可能限制 CPU/GPU 频率、功耗或持续性能,从而影响复杂场景;不是任何发热或降频都必然造成卡顿。要把 thermal 状态、频率上限和对应帧时间关联起来,不能只凭手感温度或一张频率图判断。
Android 10(API 29)提供 thermal status 接口,Android 11(API 30)增加 getThermalHeadroom。Headroom 是估计值而非温度计,设备支持与有效性需要确认;工程优化应减少持续负载,并在相近温度条件下复测。单纯清后台或重启不能证明问题已修复。
7.后台活动进程太多导致系统繁忙
后台工作可能与前台竞争 CPU、I/O、内存或共享锁,但进程数量本身不是负载指标。应检查实际活跃工作及其与前台关键路径的关系,不能把所有后台进程都当成异常。
CPU 繁忙
dumpsys cpuinfo 是一段时间窗内的统计,不是单帧调度图。下图多个进程超过 100% 可来自多核累计,不能直接当整机占比;还需知道窗口和分母。图中 lowmemorykiller 日志写有 no selected tsk,不能把它当作该次扫描已经杀进程的证据。

线程调度等待:Runnable 状态
Runnable 表示线程可运行但尚未获得 CPU,关键路径上的长等待可能错过 deadline。原因还可能是允许 CPU 范围、优先级或配额,而非所有 CPU 都忙。下图显示的是 UnityGfxDeviceW,不是已确认的 App 主线程;需要选中状态详情与 sched 记录进一步看。

无关进程活跃耗时
判断“无关”要看当前操作的依赖关系,不能按进程名或是否后台直接划分。图中表头为 Wall Duration、Average Wall Duration、Occurrences,汇总了选中区间的墙钟时长与次数,不是 CPU 占用比例;UnityMain、RenderThread 也可能就是前台工作。应回到时间线看这些任务是否在关键线程可用的 CPU 上竞争。

cpu 被占用
低内存时可能增加回收工作。kswapd 是内核后台内存回收线程,HeapTaskDaemon 执行 ART 的堆维护任务;它们不是“疯狂向系统申请内存”的线程。仅凭名称也不能断言每个 HeapTaskDaemon 片段都在 GC,需看具体 GC/trim 埋点。
图中能看到这些线程活跃,不能据此说它们占满了所有 CPU。结合回收事件、PSI、页错误、线程运行时长与前台等待,才能判断资源竞争。

System 锁
图中约 93.633 ms 的选区里有多条 monitor contention,涉及 AMS 的 activityIdle、RecentsAnimation.finishAnimation 等,能支持“存在锁竞争”这一观察。要继续跟踪 owner 线程及其嵌套等待,区分持锁计算、被抢占和等待下游。
不是所有 Binder 请求都携带同一把 AMS/WMS 锁,也不能把整段选区等同某一次持锁时长。只有请求依赖被阻塞的临界区,才会沿该路径传播延迟;现代锁结构与动画实现也需按版本核对。

8.Layer过多导致 SurfaceFlinger Layer Compute 耗时
较多需要遍历、更新或参与可见区域计算的 layer 可能增加合成准备工作,但不能只按后台 App 数量判断。图里可见 rebuildLayerStacks 和可见区域计算,没有给出 layer 数量或一组可控对照。
原文“Android P 才改到 SurfaceFlinger 主线程”的说法不准确:Android 8.1 SurfaceFlinger 的 handleMessageRefresh 已调用 rebuildLayerStacks;Android 9 仍有此路径。Android 16 实现 使用更新后的 layer/CompositionEngine 体系,不能把旧函数名当跨版本接口。应查 layer 生命周期、泄漏和事务更新,不能以“多清后台”代替修复。

9.Input 报点不均匀
触摸事件异常间隔可能影响跟手性,但没有输入不等于主线程不能绘制:动画、滚动惯性、定时任务或 invalidate 都可继续触发更新。InputReader/Dispatcher 的 slice 也不是触屏原始报点的逐条副本,批处理、合并、过滤或采集缺失都要区分。
下图的缩略轨道可用来找时间区间,不能独立证明触屏漏报。应关联原始输入时间戳、Reader/Dispatcher/过滤链、应用收到事件与对应呈现帧,区分设备没产生事件、系统晚分发和 App 晚响应。

10.LMK 频繁工作抢占 cpu
旧文中的 LMK 要与现代用户态 lmkd 区分。AOSP lmkd 文档 说明 Android 10 支持基于 PSI 的压力检测、Android 11 更新了结合 thrashing 等信息的策略,仍依赖内核支持与设备配置。看到回收线程活跃不等于 lmkd 本身占满 CPU。
- CPU:反复杀进程与重启可能造成额外开销,但缓存进程被杀后不会保证立即由“主进程”拉起。需用实际 kill、启动记录及 PID/时间匹配确认循环。
- Memory:ART GC、内核回收和 lmkd 杀进程是不同机制;系统低内存不能直接推导所有进程同时 GC。
- I/O:文件页回收后再访问可能读盘;匿名页可能进入 swap。若使用 zram,压缩页主要存放 RAM,不能把所有 swap 都称为磁盘 I/O,配置了 writeback 时另看底层存储活动。
第一图是聚合列表,不能读作 kill/restart 次数。第二图与第 7 节 HeapTaskDaemon/kswapd 图片相同,作为同一历史片段复用,不是独立复现实验。


11.低内存导致 IO 耗时
内存压力可能通过 direct reclaim、文件页重新载入、swap 或回写增加等待,但需要具体证据,不能把所有启动、GC、JIT 或 D 状态都归为“低内存导致磁盘慢”。Linux v6.6 vmscan 描述回收路径;zram 文档 区分内存压缩与可选 writeback。
下面第一图能看到 NativeAlloc concurrent copying GC、资源加载和较长 traversal,不能仅凭它确认磁盘 I/O。后续几个子场景要分别判断。

主线程 IO 导致卡顿
这张图的 SystemUI 主线程状态明确为 Uninterruptible Sleep - Block I/O,阻塞点为 io_schedule,可以支持该片段存在 I/O 相关等待。具体文件、块设备、回收或锁路径仍需调用栈与 I/O 事件;单独一个 D 状态并不总是磁盘 I/O,更不能直接证明低内存。

主线程 IO 导致应用启动速度慢
第一图包含录屏进程 bindApplication、OpenDexFilesFromOat/Open oat 等启动加载;第二图还有 Application.onCreate、VerifyClass 与 JIT。加载可能包含读盘、页错误、验证、CPU 工作或等待,不能把整个启动区间当作纯 I/O。还要区分进程初始化、首帧与用户可交互时间。


滑动列表时候 IO 导致卡顿
图中 Launcher 主线程选中区间约 57.994 ms,状态为 Uninterruptible Sleep | WakeKill - Block I/O,等待点为 wait_on_page_bit_killable。这支持存在页相关等待,但不能单独确定哪个文件、是否低内存触发,或底层存储是否故障。需结合页错误、回收与块 I/O 时间线。

12.GPU 合成导致 SurfaceFlinger 耗时
HWC 可把部分 layer 指定为 client composition,由 SurfaceFlinger 通过 GPU 合成。这是正常路径之一;是否影响帧取决于像素工作量、GPU 负载、buffer/fence 和显示 deadline,不能说有 GPU 合成就必然慢。参见 SurfaceFlinger 与 HWC。
图中 hasClientComposition 与 FrameMissed 共现,第二图选区约 22.291 ms,还标注了 HWC 调用问题。这不能单独证明 GPU 是根因,也不能把 SurfaceFlinger 的 CPU 侧 slice 或整个选区当作 GPU 执行时长。


13.kswapd 的 CPU 占用
图中 CPU 7 上有较多 kswapd0 活动。CPU 7 是否属于大核要查该 SoC 拓扑,截图也没有温度或限频证据;不能直接推出“跑大核导致发热限频”。应量化回收线程在关键 CPU 上的运行时间、前台 Runnable 延迟和持续内存压力,再判断是否存在竞争。

14.SurfaceFlinger Vsync 不均匀
旧 Trace 的 VSYNC-sf 是调度信号轨道,不等于面板每次实际呈现时间。图中间隔变化值得检查,但刷新率/显示模式切换、调度相位调整、漏采和实现问题都要区分。不能仅看色块宽度就归因某次 SurfaceFlinger 或 HWC 修改。
现代系统支持 多刷新率切换。应同步看模式、目标周期、应用产帧和实际呈现;在支持时结合 FrameTimeline,判断变化是预期策略还是错过了 deadline。

15.三方应用使用 Accessibility 服务导致系统卡顿
部分无障碍功能会启用输入过滤或变换链,路径可能经过 system_server 的处理再转发。Android 9 AccessibilityManagerService 根据放大、触摸探索、按键过滤、手势注入等功能决定是否启用 InputFilter;不是任意 Accessibility 服务或仅监听可访问性事件就必然走同样路径。Android 16 的 AccessibilityInputFilter 也按功能处理事件。
下图提示 Reader 到 Dispatcher 的节奏变化,但不能据此一概判定“没有立即唤醒就是系统错误”。应关联事件时间、过滤/转发和 Handler/Binder 等等待,并对具体功能做受控对照。

总结
下面是我在 2019 年做这些工作时的一些感受。原文写作时的 Android Q 是 Android 10;Android 原生系统持续演进 , 每个版本都会解决非常多的性能问题 , 同时也会引进一些问题 ; 到了手机厂商这里 , 由于硬件差异和软件定制 , 会在系统中加入大量的自己的代码 , 这无疑也会影响系统的性能 .
上面列出的这些影响流畅性的案例 , 只是 Android 系统开发中遇到的性能问题的冰山一角 , 任何一个问题都会对用户的使用产生影响 , 这也是为什么手机厂商越来越重视系统优化 . 手机厂商非常重视开发过程中和用户使用过程中遇到的性能问题 , 并开发和提出各项优化措施 , 从硬件到软件 , 从用户行为优化到系统策略动态学习 . 这也是为什么现在的手机厂商的系统越做越好 , 质量越来越高的一个原因 , 那些不重视质量只重视设计和产品的手机厂商 , 都渐渐地被消费者淘汰了.
大家可以看看这个问题 : 当手机厂商说安卓手机性能优化的时候,他们到底在做什么
这也是流畅性的一个系列文章中的一篇 , 可以点击下面的链接查看本系列的其他文章.
- 0. Android 中的卡顿丢帧原因概述 - 方法论
- 1. Android 中的卡顿丢帧原因概述 - 系统篇
- 2. Android 中的卡顿丢帧原因概述 - 应用篇
- 3. Android 中的卡顿丢帧原因概述 - 低内存篇
本文知乎地址
由于博客留言交流不方便,点赞或者交流,可以移步本文的知乎界面
知乎 - Android 中的卡顿丢帧原因概述 - 系统篇
关于我 && 博客
下面是个人的介绍和相关的链接,期望与同行的各位多多交流,三人行,则必有我师!
- 博主个人介绍 :里面有个人的微信和微信群链接。
- 本博客内容导航 :个人博客内容的一个导航。
- 个人整理和搜集的优秀博客文章 - Android 性能优化必知必会 :欢迎大家自荐和推荐 (微信私聊即可)
- Android性能优化知识星球:付费社群已于 2026 年停止运营,不再招收新成员;此处保留历史介绍。
一个人可以走的更快 , 一群人可以走的更远
