ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Android main thread主线程Choreographer doFrame发生FullSuspendCheck

Android main thread主线程Choreographer doFrame发生FullSuspendCheck Android main thread主线程Choreographer doFrame发生FullSuspendCheck1. FullSuspendCheck 是什么ART 的 Java/Kotlin 线程并不是任意时刻都能立刻安全暂停。通常线程需要执行到某些 ART 已知安全的位置称为safepoint suspend check point在这些位置ART 会检查当前线程是否被要求暂停。通常情况下这类检查很快类似是否有 suspend request 没有。 继续执行。但如果发现当前线程存在 suspend request就会进入较重的路径FullSuspendCheck可大致理解为ART 发现当前线程需要响应挂起请求。 当前线程进入可被安全检查、暂停或协调 runtime 操作的流程。因此FullSuspendCheck不常见且耗时长往往意味着当时不是普通代码执行而是 ART runtime 正在进行某种需要线程配合的全局或半全局操作。2. 为什么会发生在 Choreographer#doFrame 中因为主线程正在运行doFrame而 ART 的 suspend check 可以插入或出现在 Java/Kotlin 执行过程中的多个安全点例如方法调用边界 循环回边 滑动、View traversal、绘制等逻辑 ↓ 运行到 ART safepoint ↓ ART 发现存在 pending suspend request ↓ 进入 FullSuspendCheck ↓ 主线程被暂停/等待/协助 runtime 操作 ↓ 返回后继续执行 doFrame所以它出现在 doFrame 内只是说明主线程刚好在该帧执行期间响应了 ART 的挂起请求。并不表示Choreographer 或大图滑动代码直接调用了 FullSuspendCheck。3. FullSuspendCheck 常见触发原因从常见程度和关联度看优先排查以下几类。原因一GC 相关的线程暂停这是最需要优先确认的一类。ART 在执行某些 GC 阶段时需要让 Java 线程进入 safepoint或者等待线程到达可安全扫描/处理的状态。典型链路大图左右滑动期间产生较多对象、Bitmap、Drawable、临时集合或图片解码相关对象 ↓ Java heap / native heap 压力上升 ↓ ART 发起 GC ↓ GC 某阶段要求 mutator 线程响应 suspend request ↓ 图库主线程执行到 safepoint ↓ 进入 FullSuspendCheck ↓ 主线程暂停或等待 GC 相关操作 ↓ doFrame 被拉长注意并发 GC 不代表对 UI Thread 完全没有影响。即使 GC 的主体工作在后台线程上进行仍可能有需要应用线程配合的阶段或者存在短暂停顿。应在同一时间窗口检查HeapTaskDaemon GC Thread Concurrent GC Marking Sweep Compact Young GC Explicit GC WaitForGcToComplete不同 Android 版本的 trace 名称会有区别。原因二线程创建、线程退出、Attach/Detach 等 ART ThreadList 操作ART 维护一份 runtime 线程列表。当线程创建、销毁或 native 线程执行 JNI attach/detach 时可能涉及 thread list、线程状态切换及相关同步。如果应用频繁创建线程例如临时图片解码线程 协程调度器工作线程扩容 自建线程池临时建线程 JNI/native 图片处理线程 attach/detach 媒体/相机/相关 native 工作线程就可能增加 ART runtime 线程管理操作。这类路径不一定每次都导致全局暂停但如果 trace 同期出现Thread.start ThreadList AttachCurrentThread DetachCurrentThread CreateNativeThread DestroyJavaVM就需要重点关联。典型链路某个调试、采样或监控组件请求线程栈 ↓ ART 发起线程 suspend / checkpoint 操作 ↓ 主线程在 safepoint 响应 ↓ 出现 FullSuspendCheck如果这是在debug 包 开启 Android Studio Profiler 开启 method trace 开启 heap tracking 接入性能监控或崩溃监控实验功能环境下复现需要优先排除这类因素。原因四JIT、类加载、运行时内部。4. 为什么会出现 FullSuspendCheck可能有几种情况情况一同一个 runtime 操作的多阶段协作例如某次 GC 或 ART runtime 操作不是一次主线程暂停就完全结束而是在不同阶段需要 mutator 线程再次配合。主线程第一次到达 safepoint ↓ 第一次 FullSuspendCheck ↓ 恢复执行 ↓ GC/runtime 操作进入下一阶段 ↓ 主线程再次到达 safepoint ↓ 第二次 FullSuspendCheck情况二短时间内连续出现两次独立 suspend 请求例如一次 GC 相关请求 ↓ 主线程恢复 ↓ 随后又发生线程管理、调试采样或另一轮 GC 相关请求 ↓ 主线程再次进入 FullSuspendCheck情况三主线程第一次没有立即完成所需协调后续再次检查这需要看具体 ART 版本及 trace 周边事件确认。但总体含义仍然是主线程在同一帧中多次被 runtime suspend 存在 pending suspend/checkpoint 请求 ↓ 主线程第一次运行到 safepoint ↓ 进入 FullSuspendCheck耗时约 20ms 的一部分 ↓ 主线程恢复后继续执行 doFrame ↓ 再次运行到 safepoint ↓ 再次进入 FullSuspendCheck ↓ doFrame 总耗时被拉长至 80ms ↓ 90Hz 下错过约 7 帧 ↓ 大图左右滑动明显卡顿90Hz 帧预算1000 / 90 ≈ 11.11ms因此80ms / 11.11ms ≈ 7.2 帧而单次20ms的FullSuspendCheck本身已经超过一帧预算。6. 在 Perfetto/Trace 中如何继续定位以FullSuspendCheck的起止时间为中心前后各扩 50ms200ms 看完整时间线。7.1 优先找 ART / GC 轨道搜索这些关键词GC HeapTaskDaemon HeapTask Concurrent MarkSweep Sweep Compaction Marking WaitForGcToComplete SuspendAll ResumeAll Checkpoint ThreadList如果它们与FullSuspendCheck重叠尤其是有SuspendAll GC HeapTaskDaemon那么 GC 相关性很高。7.2 看进程中的线程状态重点看这些线程在该时段是否活跃HeapTaskDaemon Jit thread pool Signal Catcher ReferenceQueueDaemon ↓ ART GC 或系统内存回收 ↓ 主线程 FullSuspendCheck ↓ 滑动卡顿7.4 排除调试和监控影响确认复现环境是否有Android Studio Profiler CPU method trace heap profiling debugger attached StrictMode 自研 ANR watchdog 自研线程栈采样 Crash/性能 SDK 的高频堆栈采集建议对比debug 包 profiler 开启 debug 包 profiler 关闭 release 包如果只在 profiler/debug 环境下频繁出现优先考虑调试采样造成的 runtime 挂起干扰。8. 最终重点最需要确认的是这两次 FullSuspendCheck 同期是否存在 GC / SuspendAll / HeapTaskDaemon 活动如果有优先沿着Bitmap/对象分配 ↓ Java/native 内存压力 ↓ GC/runtime suspend ↓ FullSuspendCheck ↓ UI Thread doFrame 卡顿去定位。如果没有 GC 痕迹则下一优先级是是否发生了协程/线程创建或 native Attach/Detach 以及是否有 profiler、ANR/Crash 栈采集等线程挂起操作。
返回列表