ANR
ANR(Application Not Responding), 如果 Android 应用的界面线程处于阻塞状态的时间过长,会触发“应用无响应”(ANR) 错误。如果应用位于前台,系统会向用户显示一个对话框
Android系统通过对应用进程的组件(Activity,Service,Receiver,Provider、input)的响应能力进行超时监控,如果超过预定时间应用进程还未完成任务,则会触发系统的ANR警告。
Input dispatching timed out:输入时间分发超过5s,包括按键和触屏事件。
Broadcast of Intent:前台广播需要在10s内完成,后台广播需要在60s内完成。
executing service:前台服务需要在20s内完成,后台则需要在200s内完成。
ContentProvider:几乎非常少见,publish执行未在10s内完成。
Context.startForegroundService() did not then call Service.startForeground():应用调用startForegroundService,然后5s内未调用startForeground出现ANR或者Crash,此问题属于应用未适配Android版本sdk。
ANR的原因
ANR产生的原因可以分为两大类
-
主线程繁忙,来不及处理关键消息:存在耗时消息、或者消息队列拥塞,关键消息得不到调度、或者发生死锁
1、应用在主线程上非常缓慢地执行涉及 I/O 的操作,如有复杂的layout布局、频繁的I/O操作。
2、主线程在对另一个进程进行同步 binder 调用,而后者需要很长时间才能返回。
3、主线程存在耗时操作:主线程阻塞(Blocked)、挂起(suspend)、死锁、死循环、耗时操作等;
4、应用频繁GC:包括应用自身也容易导致前台应用出现anr的现象;
5、应用内存泄露;
-
系统繁忙,主线程得不到调度:系统或应用内部其它线程或资源负载过高(高IO、内存频繁抖动),主线程调度被严重抢占
1、cpu资源被抢占,CPU负载过高:其他进程某一时间点cpu占比高、某一刻系统的cpu占比过高,都会导致这一时间段无法抢到cpu时间片;
2、系统可用内存紧张:系统一直在执行lowmemorykiller操作查杀进程;
分析步骤
1.查看日志
-
首先在android(system)日志中搜索“ANR in”关键字,查看 发生ANR的应用进程、pid、类型,cpu负载、内存压力、cpu使用情况
-
event日志中搜索"am_anr"关键字,可以确定 anr发生的时间 和 进程号,根据上面的时间点往前推(取决于该ANR类型对应的阈值时间),找到案发最初的时间点。
-
查看 ANR Trace日志 ,trace文件中搜索关键字"sysTid=进程号" ,查看线程的运行状态,查看函数的调用堆栈信息
-
查看 main log日志,分析ANR 前5S -10S 秒内的详细的日志信息
查看cpu使用率
首先关注的进程就是发生anr的进程、system_server、kswapd0、kworker和其他占比较高的进程、以及最终统计的整体cpu使用率信息。
如果kswapd0占比较高(前3名),就说明内存存在一定的压力;iowait很高就说明系统在一些I/O耗时操作,就可以结合上下文日志及系统日志辅助分析发生ANR的原因。
如果很多进程的cpu使用率普遍较高,发生anr应用的cpu使用率较低,此时可以怀疑发生anr的应用拿不到cpu时间片导致anr等(系统原因)。
85% TOTAL: 42% user + 33% kernel + 0% iowait + 0% softirq
PS: ANR发生时SystemServer占据稍高可能是正常的,因为Dump Trace时需要获取系统整体及各进程 CPU 使用情况,短时间内会造成SystemServer升高
查看内存使用
在日志中直接搜索关键字“lowmemorykiller”、“iowait”等,查看发生的时间点与发生anr的时间点是否基本对应。
lowmemorykiller
如果发生anr时间点附近,出现大量的lowmemorykiller日志信息,则说明当时内存已经严重紧张,可以较大概率认为是内存不足导致后台一直在查杀进程,同时影响前台应用的操作,导致前台应用操作耗时出现anr问题;搜索"lowmemorykiller"关键词,可以看到问题时间区域会有较多的查杀进程行为,我们通常会关注下"lowmemorykiller"这一行杀的进程adj值,adj值越低,说明当前系统内存越吃紧。
iowait
如果出现iowait,则表示可能出现了I/O卡顿。
kswapd
通常内存紧张的时候,kswapd线程会活跃起来进行回收内存 kswapd的CPU占据排进top3的话,这个问题受系统低内存影响比较大 分析ANR问题时如果遇到kswapd占据很高(top3),则认为该问题受低内存影响很大。
查看函数的调用堆栈
查看Main日志
频繁GC导致的ANR
看系统日志,根据上面的时间点往前推(取决于该ANR类型对应的阈值时间),找到案发最初的时间点。
如果在案发时间点附近,发现大量的GC片段且很多GC耗时都较长
Clamp target GC heap from这行日志是在SetIdealFootprint即调整目标堆上限值时会打印
上面这段日志说明当前应用堆使用已超出上限512M,为了满足新分配的对象内存需求,系统一直持续不断的对该应用进行阻塞GC(GC分为不同力度,阻塞GC说明应用此时内存情况比较糟糕)。
如果应用持续不断的阻塞GC,应用内存依旧没有降下来,这种情况下大概率是出现了应用内存泄漏的情况。
关于应用内存使用不当,通常有如下几种情况
频繁的生成临时对象导致堆内存增长迅速,达到下次堆GC触发阈值后便会触发Bg GC,进而导致回收线程跑大核和前台应用争抢CPU。
另外GC回收阶段会存在一次锁堆,应用的主线程会被pause,这种情况下势必会造成应用使用卡顿甚至ANR。
还有一种比较常见的情况是应用发生了较为严重的内存泄漏,导致GC一直无法回收足够的内存。
应用申请大内存触发阻塞GC以便能够申请到足够的内存,这种情况通常会引起应用界面的黑屏或者明显的卡顿。
我们知道系统低内存时会触发OnTrimMemory回调,如果应用在OnTrimMemory中并且是在主线程中直接调用显式GC接口即System.gc(),也容易引起应用卡顿,对于这个接口的使用需要谨慎。
小结
通常拿到一份完整的ANR日志,先看下是否受系统环境因素影响,比如判断是否陷入严重的低内存,是否存在系统整机负载很高等情况。 如果存在上述的这些情况,说明本次ANR的进程可能只是受害者。
如果整机负载并不高,且kswapd占比很低,排除系统因素的影响,是应用自身内存使用不当导致的ANR
参考
https://mp.weixin.qq.com/s/-yfJoRxHe_J3PNLkGw19Cg
https://mp.weixin.qq.com/s/40T6ITvJNWR8F42530k4DA
https://source.android.com/docs/core/runtime/gc-debug?hl=zh-cn
http://lihaizhou.top/2021/10/27/%E5%AF%B9Android-S-ART-GC%E7%9A%84%E6%BA%90%E7%A0%81%E6%A2%B3%E7%90%86/
https://developer.android.com/topic/performance/vitals/anr?hl=zh-cn
https://developer.android.com/topic/performance/anrs/keep-your-app-responsive?hl=zh-cn
网友评论