美文网首页Android
如何分析ANR问题?

如何分析ANR问题?

作者: 代码改变人生 | 来源:发表于2018-09-06 23:20 被阅读0次

    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的根源。

    2.3 内存原因

    内存原因有可能会导致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
    

    可以看到free的内存已所剩无几.

    3 ANR的处理

    三种不同的情况, 一般的处理情况如下
    1.主线程阻塞

    开辟单独的子线程来处理耗时阻塞事务.

    2.CPU满负荷, I/O阻塞

    I/O阻塞一般来说就是文件读写或数据库操作执行在主线程了, 也可以通过开辟子线程的方式异步执行.

    3.内存不够用

    增大VM内存, 使用largeHeap属性, 排查内存泄露等.

    感谢
    1.ANR分析思路简析-https://blog.csdn.net/sinat_34157462/article/details/78651870
    2.Android应用ANR分析-https://www.jianshu.com/p/30c1a5ad63a3

    相关文章

      网友评论

        本文标题:如何分析ANR问题?

        本文链接:https://www.haomeiwen.com/subject/owwfgftx.html