字节三面: 给你一个Demo 你如何快速定位ANR?

本文介绍了Android ANR错误的定义、原因,并详细阐述了如何定位和优化ANR问题,包括使用traces.txt文件、AsyncTask、线程优先级调整以及性能分析工具如Traceview和Systrace的运用。提供了避免ANR的关键实践建议,帮助开发者提升应用响应性能。

在这里插入图片描述

金九银十面试季又要到来了,这里给大家不定期更新大厂面试真题,希望可以帮助到即将面试的小伙伴们,祝面试顺利~

一、知识储备

1.ANR错误定义

在Android上,如果你的应用程序有一段时间响应不够灵敏,系统会向用户显示一个对话框,这个对话框称作“应用程序无响应”(ANR:Application Not Responding)对话框。用户可以选择“等待”而让程序继续运行,也可以选择“强制关闭”。因此,在程序里对响应性能的设计很重要,这样,系统不会显示ANR给用户。

默认情况下,在Android中Activity的最长执行时间是5秒(主要类型),BroadcastReceiver的最长执行时间的则是10秒,ServiceTimeout的最长执行时间是20秒(少数类型)。超出就会提示应用程序无响应(ANR错误)。
在这里插入图片描述

2.ANR错误出现原因

只有当应用程序的UI线程响应超时才会引起ANR 超时产生的原因包括:

①当前事件没有机会处理,例如UI线程正在响应另外的事件,当前事件被某个事件给阻塞掉了;
②当前事件正在处理 但是由于耗时太长没有能及时的完成。其他原因:
③在BroadcastReceiver里做耗时的操作或计算;
④CPU使用过高;
⑤发生了死锁;
⑥耗时操作的动画需要大量的计算工作,可能导致CPU负载过重。

二、ANR定位方式及优化

1.ANR错误定位

如果开发机器上出现ANR问题时,系统会生成一个traces.txt的文件放在/data/anr下,最新的ANR信息在最开始部分。通过adb命令将其导出到本地,输入以下字符:

$adb pull data/anr/traces.txt .

2.供选的优化ANR问题的方式:

1)为了执行一个长时间的耗时操作而创建一个工作线程最方便高效的方式是使用AsyncTask,只需要继承AsyncTask并实现doInBackground()方法

<think>我们正在分析如何利用`/proc/pressure/cpu`输出的指标(avg10, avg60, avg300)来定位ANR的根本原因。这些指标来自PSI(Pressure Stall Information)机制,用于量化CPU资源紧张程度。 根据引用内容,我们知道: - ANR发生的原因是消息执行慢了(主线程被阻塞或系统资源不足)[^1] - 系统在发生ANR时会生成traces.txt文件,记录线程状态和堆栈[^2] - 我们可以通过分析ANR发生前后的CPU压力指标来辅助定位问题 ### 分析步骤 #### 1. **理解PSI指标含义** - `some`行:表示至少一个任务因等待CPU而受阻的时间比例 - `full`行:表示所有非空闲任务同时因等待CPU而受阻的时间比例(更严重的资源短缺) - 指标格式:`avg10=36.09` 表示最近10秒内,36.09%的时间存在CPU资源竞争压力 - 关键值解读: - `avg10>30%`:高压力(需警惕) - `avg60>20%`:持续压力(可能引发ANR) - `full>0%`:严重资源枯竭(必然导致性能问题)[^4] #### 2. **关联ANR时间点** 在ANR日志中查找PSI输出时间戳: ``` 08-19 12:11:33.496 ... E ActivityManager: ----- Output from /proc/pressure/cpu ----- 08-19 12:11:33.496 ... E ActivityManager: some avg10=36.09 avg60=31.91 avg300=9.91 total=33355394 ``` 这表明在`12:11:33`时刻系统记录了CPU压力状态,若ANR发生在此时间点附近: - `avg10=36.09%`:**最近10秒超过1/3时间存在CPU竞争**,说明短时负载极高 - `avg60=31.91%`:**1分钟内持续高压**,系统长期过载 - `avg300=9.91%`:5分钟压力相对较低,表明高负载是近期突发的 #### 3. **压力阈值与ANR关联** - **临界点判断**: - `some.avg10 > 30%`:可能触发ANR(主线程调度延迟) - `full.avg10 > 0%`:极可能导致ANR(完全资源枯竭)[^4] - 示例日志中`full=0%`,说明尚未达到最严重级别,但`some`指标已超30%警戒线 #### 4. **结合其他日志定位根因** - **检查系统负载**: - 执行`adb shell cat /proc/loadavg`获取完整负载: ``` 4.82 3.75 2.01 5/1286 20139 ``` 含义:`1分钟负载4.82, 5分钟3.75, 15分钟2.01`(4核设备负载>4即过载) - **分析CPU使用率**: 在ANR日志中查找类似输出: ``` CPU usage from 0ms to 1005ms ago: 50% 1234/com.app: 40% user + 10% kernel 30% system_server: 20% user + 10% kernel ``` 关注: - **用户态高占用**:应用自身代码问题(如引用[3]的主线程休眠)[^3] - **内核态高占用**:系统服务或驱动问题 #### 5. **根因分类与解决方案** | 压力指标模式 | 可能根因 | 解决方案 | |------------------------|------------------------------|------------------------------| | `avg10突增 >40%` | 突发高负载(如广播风暴) | 检查广播接收器和后台服务[^1] | | `avg60持续 >25%` | 资源泄漏(线程/定时器失控) | 分析traces.txt定位泄漏点[^2] | | `full >0%` | 极端资源枯竭 | 排查内存或I/O瓶颈 | | `avg300高但avg10正常` | 历史负载影响 | 忽略(非当前问题) | #### 6. **实战案例** 假设日志显示: ``` // ANR发生时间:12:11:30 ... E ActivityManager: ANR in com.app (com.app/.MainActivity) // 3秒后记录PSI 08-19 12:11:33.496 ... E ActivityManager: some avg10=36.09 avg60=31.91 avg300=9.91 ``` 分析: - ANR前10秒CPU压力达36.09%,证明**系统过载是主因** - 进一步检查`traces.txt`: - 若主线程处于`RUNNABLE`状态:说明在等待CPU资源 - 若其他进程(如`system_server`)占用高:系统服务拖累整体性能[^4] ### 总结流程 ```mermaid graph TD A[发现ANR日志] --> B{检查/proc/pressure/cpu} B -->|avg10>30%| C[系统过载导致] B -->|avg10<20%| D[应用自身问题] C --> E[分析系统负载/proc/loadavg] D --> F[检查traces.txt主线程状态] E -->|负载>核心数| G[排查高CPU进程] G --> H[优化后台服务或限制资源] F -->|主线程阻塞| I[优化耗时操作] ``` 通过PSI指标可快速区分: - **系统级问题**:压力指标持续高位 → 需系统级优化[^1] - **应用级问题**:压力指标正常但ANR → 聚焦应用代码[^3] §§相关问题§§ 1. 如何区分ANR是由CPU压力还是I/O压力导致的? 2. PSI指标中的`full`行为什么对诊断ANR更关键? 3. 除了`/proc/pressure/cpu`,还有哪些Linux内核文件可用于分析系统资源压力?</think>### 分析CPU压力指标定位ANR根本原因 通过`/proc/pressure/cpu`的`avg10/avg60/avg300`指标定位ANR问题,需结合系统负载特征和ANR触发机制进行分层诊断。以下是具体分析流程: --- #### 一、**指标诊断框架** | **指标组合** | **压力特征** | **ANR关联场景** | |----------------------------|-------------------------------|---------------------------------------------------------------------------------| | **avg10 > avg60 > avg300** | 负载**持续下降** | ANR可能是**瞬时突发流量**导致(如广播风暴)[^1] | | **avg10 < avg60 < avg300** | 负载**持续上升** | 存在**资源泄漏**(失控线程/定时器)或**后台服务堆积**[^1] | | **avg10突增 >40%** | 短期高压 | **主线程阻塞**(如点击事件中`SystemClock.sleep()`)或**锁竞争**[^3] | | **avg60 >25%持续5分钟** | 中长期高压 | **系统级资源枯竭**(CPU过载、内存抖动、I/O等待)[^4] | | **full >0%** | 完全资源枯竭(所有任务受阻) | 必然发生ANR,需检查**系统服务死锁**或**内核资源耗尽**[^4] | --- #### 二、**实战分析步骤** 以日志为例: ```log 08-19 12:11:33.496 E ActivityManager: some avg10=36.09 avg60=31.91 avg300=9.91 08-19 12:11:33.496 E ActivityManager: full avg10=0.00 avg60=0.00 avg300=0.00 ``` 1. **压力模式识别** - `avg10(36.09%) > avg60(31.91%) > avg300(9.91%)` → 负载从长期低位**突然飙升** - `full=0%` → 未达到最严重资源枯竭 2. **时间关联分析** - 若ANR发生在`12:11:33`附近: - `avg10=36.09%` 表示**ANR前10秒内,系统36%时间存在CPU竞争** - `avg60=31.91%` 表明**高压已持续1分钟**(非瞬时现象) 3. **根因定位流程** ```mermaid graph TD A[avg10>30%且avg60>25%] --> B{检查主线程状态} B -->|主线程RUNNABLE| C[CPU资源不足] B -->|主线程BLOCKED| D[锁竞争/IO阻塞] C --> E[分析top进程] E -->|system_server高占用| F[系统服务瓶颈] E -->|目标应用高占用| G[应用代码优化] D --> H[检查binder调用栈] ``` 4. **关键证据链构建** - **证据1**:高压期间抓取`traces.txt` ```bash adb pull /data/anr/traces.txt ``` 检查主线程状态: - 若为`RUNNABLE` → 等待CPU调度(资源不足) - 若为`SLEEPING`/`MONITOR` → 应用自身阻塞[^3] - **证据2**:对比CPU使用率 在ANR日志中查找: ``` CPU usage from XXXms to YYYms ago: 50% com.app: 30% user + 20% kernel 40% system_server: 25% user + 15% kernel ``` - `system_server`占比高 → 系统服务过载[^4] - 目标应用`kernel`占比高 → 频繁系统调用(如低效I/O) --- #### 三、**典型场景解决方案** | **压力模式** | **根因** | **修复方案** | |----------------------------|-------------------------------|-----------------------------------------------------------------------------| | 突发avg10>40% | 主线程同步耗时操作 | 改用`Handler`异步或`Coroutine`调度[^3] | | avg60>30%持续 | 后台服务线程泄漏 | 使用`StrictMode`检测线程创建/销毁 | | full>0% | 系统服务死锁 | 分析`system_server`的`binder`调用链(`traces.txt`中的`Binder`栈)[^2] | | 伴随高`iowait` | I/O阻塞拖累CPU | 优化文件读写/数据库查询(批量操作+索引) | --- #### 四、**工具链辅助验证** 1. **实时监控PSI** ```bash adb shell "cat /proc/pressure/cpu && echo '---' && top -n 1 -b" ``` 2. **生成负载火焰图** ```bash adb shell perfetto -t 20s -o /data/local/tmp/trace.pb \ --config cpu:sampling=1000 ``` > **关键结论**:当`avg10>30%`时,系统已处于**ANR高风险状态**,需结合`traces.txt`中的线程堆栈和CPU使用率,区分应用代码问题(主线程阻塞)或系统问题(资源枯竭)[^1][^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值