游戏应用主线程无长耗时任务却触发ANR的原因是什么?
残留空闲态ANR问题解答
首先明确:你观察到的「主线程、GL线程都处于等待状态」和ANR触发并不冲突,核心原因有以下几类:
- ANR Trace采样滞后
系统是在检测到输入超时(你贴的日志中已经提示等待队列头年龄达到5302.9ms,远超500ms阈值)后才会抓取进程调用栈,抓取时触发阻塞的逻辑已经执行完成,主线程已经回到Looper等待新任务的空闲状态,所以你看到的栈信息不是ANR发生时刻的真实状态,这是这类非典型ANR最常见的原因。 - 线程调度抢占确实可能发生
即使主线程优先级高于普通子线程,以下场景仍会导致主线程拿不到CPU时间片:- 进程内部存在多个设置了实时调度策略(SCHED_FIFO/SCHED_RR)的子线程,占满所有CPU核心,主线程无法被调度
- 整机CPU负载长时间100%,系统级高优先级进程(如通话、核心服务)抢占了所有核心资源,你的应用主线程分配不到时间片
- 部分低端设备的CPU调度策略存在缺陷,大核调度不及时,高优先级线程也会出现数秒级的调度延迟
- 隐藏的跨线程同步阻塞
标准GLSurfaceView的主线程和GL线程本身存在多处默认同步点:生命周期回调(onSurfaceCreated/Changed/Destroyed)、requestRender触发等逻辑内部都会持有公共锁,两个线程会互相等待。如果NativeTick中曾经持有全局锁,刚好主线程需要竞争同一把锁,就会出现主线程阻塞;锁释放后主线程恢复空闲,Trace抓取时就会呈现两个线程都处于等待状态的假象。
排查建议
- 在主线程Looper中设置自定义Printer,记录每个Message的执行耗时,超过200ms就输出日志,定位阻塞主线程的具体任务
- 排查所有Native层全局锁的持有逻辑,避免主线程和GL线程长时间竞争同一把锁
- 检查进程内所有线程的优先级配置,不要随意给子线程设置高于主线程的优先级
- 排查是否存在主线程同步调用跨进程binder的逻辑,binder阻塞恢复后也会在Trace中呈现为空闲状态
内容的提问来源于stack exchange,提问作者Steven Haggerty
相关产品推荐
相关产品推荐

