ANR问题的定位与分析

2020-03-19 15:57:33 浏览数 (1)

【前言】

ANR问题,相信是日常应用测试中,各位小伙伴都会遇到的问题。本篇对ANR的类型、原因及出现场景、以及ANR定位与分析思路进行了总结!

【一. ANR定义与分类】

ANR定义

ANR全称Application Not Responding,意思就是程序未响应。如果应用程序在UI线程被阻塞太长时间,就会出现ANR。出现ANR,通常系统会弹出一个提示提示框。

ANR类型

(1). 广播ANR

(2). ServiceANR

(3). ContentProviderANR

(4). InputANR

(5). 系统WatchDog

ANR超时阈值

不同组件的超时阈值不同,Service、Broadcast、ContentProvider 、Input的超时阈值如下

(1)BroadcastTimeout

前台Broadcast:onReceiver在10S内没有处理完成发生ANR。

后台Broadcast:onReceiver在60s内没有处理完成发生ANR。

(2)ServiceTimeout

前台Service:onCreate,onStart,onBind等生命周期在20s内没有处理完成发生ANR。

后台Service:onCreate,onStart,onBind等生命周期在200s内没有处理完成发生ANR (3)ContentProviderTimeout ContentProvider 在10S内没有处理完成发生ANR。

(4)KeyDispatchTimeout

input事件在5S内没有处理完成(如按键或者触摸)发生了ANR。

【二. ANR产生原因与出现场景】

1. 产生ANR原因

a. 耗时操作

b. 自身服务阻塞

c. 系统阻塞

d. 内存紧张

e. CPU资源抢占

2. 典型场景

a. 主线程频繁进行耗时的IO操作:如数据库读写

b. 多线程操作的死锁,主线程被block;

c. 主线程被Binder 对端block;

d. SystemServer中WatchDog出现ANR;

e. servicebinder的连接达到上限无法和和System Server通信

f. 系统资源已耗尽(管道、CPU、IO)

【三. ANR定位与分析】

1. ANR分析思路——traces

通常发生ANR时,首先去查找对应Trace(重要进程的各个线程调用栈trace信息)日志,看看主线程是否在处理该广播或被阻塞。

trace路径:/data/anr/traces.txt

trace导出:adb pull/data/anr/traces.txt

最新的ANR信息在最开始部分,我们从stacktrace中即可找到出问题的具体行数。

在文件中使用ctrl F 查找包名可以快速定位相关代码。特别注意:产生新的ANR,原来的 traces.txt 文件会被覆盖。

2. ANR其他分析思路与相关日志

如果发现堆栈完全处于空闲状态,那就需要结合log日志进行分析,包括logcat、kernel日志、cpuinfo以及meminfo等,参考顺序从前向后。

分析logcat思路

首先在日志中搜索(“anrin”,“low_memory”,“slow_operation”)等关键字,通过该类关键字主要是查看系统CPU负载,如果是发现应用进程CPU明显过高,那么很有可能是该进程抢占CPU过多导致,系统调度不及时,误认为应用发生了超时行为。

分析kernel思路

在此类日志中直接搜索lowmemorykiller,如果存在则查看发生时间和ANR时间是否大致对应,相差无几的话,可以从该日志中看到操作系统层面当前内存情况,Free Memory说明的是空闲物理内存,File Free说明的则是文件Cache,也就是应用或系统从硬盘读取文件,使用结束后,kernel并没有这正释放这类内存,加以缓存,目的是为了下次读写过程加快速度。当然,发现Free和Other整体数值都偏低时,Kernel会进行一定程度的内存交换,导致整个系统卡顿。同时这类现象也会体现在log日志“slow_operation”中,即系统进程的调度也会收到影响。

分析cpuinfo思路

这类日志,可以清晰的看到哪类进程CPU偏高,如果存在明显偏高进程,那么ANR和此进程抢占CPU有一定关系。当然,如发现Kswapd,emmc进程在top中,则说明遇到系统内存压力或文件IO开销。

分析meminfo思路

分析该类日志,主要是看哪类应用或系统占用内存偏高,如果应用内存占用比较正常,系统也没有发生过度内存使用,那么则说明系统中缓存了大量进程,并没有及时释放导致系统整体内存偏低。

3. traces.txt重要字段

main:main标识主线程,如果是线程,那么命名成“Thread-X”的格式,x表示线程id,逐步递增。

prio:线程优先级,默认是5

tid:tid不是线程的id,是线程唯一标识ID

group:是线程组名称

sCount:该线程被挂起的次数

dsCount:是线程被调试器挂起的次数

obj:对象地址

self:该线程Native的地址

sysTid:是线程号(主线程的线程号和进程号相同)

nice:是线程的调度优先级

sched:分别标志了线程的调度策略和优先级

cgrp:调度归属组

handle:线程处理函数的地址。

state:是调度状态

schedstat:从 /proc/[pid]/task/[tid]/schedstat读出,三个值分别表示线程在cpu上执行的时间、线程的等待时间和线程执行的时间片长度,不支持这项信息的三个值都是0;

utm:是线程用户态下使用的时间值(单位是jiffies)

stm:是内核态下的调度时间值

core:是最后执行这个线程的cpu核的序号。

找到java的堆栈信息定位代码位置,定位到问题。

【四. ANR分析案例】

分析案例一:Input ANR

分析案例二:在系统的方法上的锁没释放

WindowManagerGlobal.dumpGfxInfo

Blocked就一定有被持有的对象,这个有时候是发生在binder,就需要分析binder相关的log

分析案例三 内存问题

分析案例四 GC问题

观察Trace主线程堆栈,发现主线程在申请内存过程中被block,等待GC结束,

再看看其它线程状态,进一步查找发现,下面任务正在执行GC

Tid=8线程执行GC,导致主线程申请内存被Block,应用进程内存使用不当,导致GC时间过程,产生ANR。

【参考】

https://juejin.im/post/5be698d4e51d452acb74ea4c

https://www.jianshu.com/p/862ce91c1abf

https://droidyue.com/blog/2015/07/18/anr-in-android/

https://www.jianshu.com/p/388166988cef

0 人点赞