分析ANR问题

https://www.jianshu.com/p/4eabede8c8af
1 为什么会产生ANR
在Android里, App的响应能力是由Activity Manager和Window Manager系统服务来监控的. 通常在如下三种情况下会弹出ANR对话框:

1:5s内无法响应用户输入事件(例如键盘输入, 触摸屏幕等)
2:BroadcastReceiver在10s内无法结束
3:ServiceTimeout(20s) --小概率类型,Service在特定的时间内无法处理完成

造成以上两种情况的首要原因就是在主线程(UI线程)里面做了太多的阻塞耗时操作, 例如文件读写, 数据库读写, 网络查询等.

2 ANR分析
2.1 获取ANR产生的trace文件
ANR产生时, 系统会生成一个traces.txt的文件放在/data/anr/下. 可以通过[adb]命令将其导出到本地:

$adb pull data/anr/traces.txt .

本例中问题出现在MainActivity.java 27行,因为这里调用了Thread.sleep方法。

----- pid 30307 at 2018-09-06 14:51:14 -----
 Cmd line: com.example.android
 JNI: CheckJNI is off; workarounds are off; pins=0; globals=272
 DALVIK THREADS: 
(mutexes: tll=0 tsl=0 tscl=0 ghl=0)
 "main" prio=5 tid=1 TIMED_WAIT | group="main" sCount=1 dsCount=0 obj=0x416eaf18 self=0x416d8650 
| sysTid=30307 nice=0 sched=0/0 cgrp=apps handle=1074565528 
| state=S schedstat=( 0 0 0 ) utm=5 stm=4 core=3 
| stack=0x7fdc4ca000-0x7fdc4cc000 stackSize=8MB 
| held mutexes= 
at java.lang.VMThread.sleep(Native Method) 
at java.lang.Thread.sleep(Thread.java:1044) 
at java.lang.Thread.sleep(Thread.java:1026) at com.example.android.MainActivity$1.run(MainActivity.java:27) 
at android.app.Activity.runOnUiThread(Activity.java:4794) 
at com.example.android.MainActivity.onResume(MainActivity.java:33) 
at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1282) 
at android.app.Activity.performResume(Activity.java:5405)

从第一行开始我们来按每一行解析其所代表的含义。

1,代表 PID at time
2,Cmd line: process name
3,固定头,指明下面都是当前运行的dvm thread ,“DALVIK THREADS:” 及所包含的线程数
4,"main" prio=5 tid=1 Native
分别代表thread name, java thread Priority, DVM thread id, DVM thread status
"main" :main thread -> activity thread
prio :java thread priority default is 5, (正常区域是1-10)
tid:是DVM thread id, 不是 linux thread id(下一行的sysTid才是)
Native:DVM thread Status 正常有这些状态(ZOMBIE, RUNNABLE, TIMED_WAIT, MONITOR, WAIT, INITALIZING,STARTING, NATIVE, VMWAIT, SUSPENDED,UNKNOWN)
5,group="main" sCount=1 dsCount=0 obj=0x416eaf18 self=0x416d8650
代表 DVM thread status。
group:是线程所处的线程组 default is “main”
sCount: 线程被正常挂起的次数 1 (thread suspend count)
dsCount: 线程因调试而挂起次数 0 (thread dbg suspend count)
obj: 当前线程所关联的java线程对象 0x75720fb8 (thread obj address)
Sef: 该线程本身的地址 0x7f7e8af800 (thread point address)
6,sysTid=30307 nice=0 sched=0/0 cgrp=apps handle=1074565528
代表Linux thread status显示线程调度信息
sysTId: linux系统下得本地线程id linux thread tid
Nice:线程的调度有优先级 linux thread nice value
cgrp: 优先组属 c group
sched: 调度策略 cgroup policy/gourp id
handle: 处理函数地址 handle address
7,state=S schedstat=( 0 0 0 ) utm=5 stm=4 core=3
代表CPU Sched stat 显示更多该线程当前上下文
State:调度状态 process/thread state (正常有 "R (running)", "S (sleeping)", "D (disk sleep)", "T (stopped)", "t (tracing stop)", "Z (zombie)", "X (dead)", "x (dead)", "K (wakekill)", "W (waking)",),通常一般的Process 处于的状态都是S (sleeping), 而如果一旦发现处于如D (disk sleep), T (stopped), Z (zombie) 等就要认真审查.
Schedstat (Run CPU Clock/ns, Wait CPU Clock/ns, Slice times) 该线程运行信息
utm: utime, user space time 线程用户态下使用的时间值(单位是jiffies)
stm: stime, kernel space time 内核态下得调度时间值
core: now running in cpu. 最后运行改线程的cup标识
8,stack=0x7f7dc93000-0x7f7dc95000 stackSize=1020KB
代表堆栈地址区域及size
9,held mutexes=
代表是否被锁住,正常有四个属性(mutexes: tll=0 tsl=0 tscl=0 ghl=0),0表示unlock,其它值都代表被lock,
tll: thread List Lock,
tsl: thread Suspend Lock,
tscl: thread Suspend Count Lock
ghl: gc Heap Lock
10,剩余的就是一些 Call Stack

2.2 CPU负荷和CPU使用率

//CPU前一分钟、五分钟、十五分钟的CPU平均负载, 
//CPU平均负载可以理解为一段时间内正在使用和等待使用CPU的活动进程的平均数量。
    Load: 5.16 / 9.69 / 30.66      
//ago,表示ANR发生之前的一段时间内的CPU使用率,并不是某一时刻的值
    CPU usage from 34388ms to -1ms ago:
    4.1% 32614/com.qihoo.browser: 2.5% user + 1.6% kernel / faults: 465 minor 1 major
    3% 379/adbd: 0.2% user + 2.8% kernel / faults: 1653 minor
    2.6% 743/system_server: 1.8% user + 0.7% kernel / faults: 689 minor
    2.5% 2326/com.qihoo.daemon: 2.2% user + 0.2% kernel / faults: 601 minor
    2.4% 1009/RX_Thread: 0% user + 2.4% kernel
    2% 280/surfaceflinger: 1.2% user + 0.8% kernel / faults: 1361 minor
    1.8% 2675/com.lbe.security:service: 1.7% user + 0.1% kernel / faults: 749 minor
    .......
    +0% 3682/migration/1: 0% user + 0% kernel
    +0% 3683/kworker/1:0: 0% user + 0% kernel
    +0% 3684/ksoftirqd/1: 0% user + 0% kernel
    9.7% TOTAL: 4.8% user + 4.2% kernel + 0.3% iowait + 0.3% softirq
//later,表示ANR发生之后
    CPU usage from 1656ms to 2187ms later:
    8.7% 743/system_server: 0% user + 8.7% kernel / faults: 4 minor
    7% 943/InputDispatcher: 0% user + 7% kernel
    1.7% 1199/Binder_6: 0% user + 1.7% kernel
    5.2% 379/adbd: 0% user + 5.2% kernel / faults: 27 minor
    3.5% 379/adbd: 0% user + 3.5% kernel
    1.7% 1009/RX_Thread: 0% user + 1.7% kernel
    1.7% 1768/mpdecision: 0% user + 1.7% kernel
    1.7% 1784/mpdecision: 0% user + 1.7% kernel
    1.2% 6883/kworker/u:37: 0% user + 1.2% kernel
    1.3% 29165/kworker/0:2: 0% user + 1.3% kernel
    2.3% TOTAL: 0.1% user + 0.3% kernel + 1.8% iowait

如果说某个进程在ANR发生时CPU使用率出现很高的情况,那么就可以知道这个进程在做非常消耗CPU的事情,一般这种情况下这个进程就是ANR进程,而消耗CPU的这个事情往往就是导致ANR的根源。

内存原因有可能会导致ANR, 例如如果由于内存泄露, App可使用内存所剩无几.
// 以下trace信息来自网络, 用来做个示例

Cmdline: android.process.acore DALVIK THREADS: "main"prio=5 tid=3 VMWAIT |group="main" sCount=1 dsCount=0 s=N obj=0x40026240self=0xbda8 | sysTid=1815 nice=0 sched=0/0 cgrp=unknownhandle=-1344001376 atdalvik.system.VMRuntime.trackExternalAllocation(NativeMethod) atandroid.graphics.Bitmap.nativeCreate(Native Method) atandroid.graphics.Bitmap.createBitmap(Bitmap.java:468) atandroid.view.View.buildDrawingCache(View.java:6324) atandroid.view.View.getDrawingCache(View.java:6178) ... MEMINFO in pid 1360 [android.process.acore] ** native dalvik other total size: 17036 23111 N/A 40147 allocated: 16484 20675 N/A 37159 free: 296 2436 N/A 2732

3 ANR的处理
三种不同的情况, 一般的处理情况如下
1.主线程阻塞
开辟单独的子线程来处理耗时阻塞事务.
2.CPU满负荷, I/O阻塞
I/O阻塞一般来说就是文件读写或数据库操作执行在主线程了, 也可以通过开辟子线程的方式异步执行.
3.内存不够用
增大VM内存, 使用largeHeap属性, 排查内存泄露等.

你可能感兴趣的:(分析ANR问题)