Android-ANR问题简析

https://blog.csdn.net/qzh123456/article/details/78737791

 

ANR问题,相信是每位开发日常都会遇到的问题,对于这类问题的分析,按照官方的推荐,或网络博客的总结思路能解决一定的问题,但是多数时候大家的困惑就是应用本逻辑很简单,耗时很短或应用堆栈完全正常,或者或处于空闲状态,可系统为什么就认为接收者发生ANR了呢?下面将之前公司的一次内部分享开放出来,用几个实例从不同角度分析导致ANR产生的Root Case。也希望对大家以后分析该类问题有一定参考,不对的地方欢迎大家拍砖。

一   ANR分类,含如下几种类型:

          广播ANR

          Service ANR

          ContentProvider ANR

          Input ANR

面向系统:WatchDog

二 产生ANR原因,如下几种:

         主线程耗时操作

         主线程被其它线程Block

         系统级响应阻塞

         系统或进程自身可用内存紧张

         CPU资源被抢占

对于这些ANR,先给大家的推荐一下大致分析思路和相关日志,通常发生ANR时,首先去查找对应Trace日志,看看主线程是否在处理该广播或被阻塞,如果发现上述现象,那么恭喜你,已经很接近答案了。但如果发现堆栈完全处于空闲状态,那么很不幸,就需要扩大参考面了,需要结合log日志进行分析,日志包括logcat, kernel日志,cpuinfo以及meminfo等,参考顺序从前向后。

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

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

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

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

5综合分析当时系统环境,例如电量(低电可能会引起手机限频,限核等等),手机温度(温度过高也可能会引起限频),以及操作频率(例如执行monkey测试)等等;

上面说了这么多,下面结合实例进行分析:

 

实例一:主线程进行耗时操作,或被进程内其它线程阻塞

示例:
第一步,观察Trace 主线程堆栈,发现主线程在申请内存过程中被block,等待GC结束,但通过堆栈进一步发现其GC并没有发生在该线程,也就是说在其他线程在执行GC动作,而主线程在申请内存过程中需要等待GC完成,再进一步申请内存。

"main" prio=5 tid=1 WaitingForGcToComplete

  native: #00 pc 0000000000019980  /system/lib64/libc.so (syscall+28)
  native: #01 pc 000000000013a62c  /system/lib64/libart.so (_ZN3art17ConditionVariable4WaitEPNS_6ThreadE+136)
  native: #02 pc 0000000000237f14  /system/lib64/libart.so (_ZN3art2gc4Heap19WaitForGcToCompleteENS0_7GcCauseEPNS_6ThreadE+1376)
  native: #03 pc 000000000024798c  /system/lib64/libart.so (_ZN3art2gc4Heap22AllocateInternalWithGcEPNS_6ThreadENS0_13AllocatorTypeEmPmS5_S5_PPNS_6mirror5ClassE+168)
  native: #04 pc 000000000050394c  /system/lib64/libart.so (artAllocObjectFromCodeRosAlloc+1412)
  native: #05 pc 00000000001215d0  /system/lib64/libart.so (art_quick_alloc_object_rosalloc+64)
  native: #06 pc 00000000018e72f0  /system/framework/arm64/boot.oat (Java_android_widget_TextView__0003cinit_0003e__Landroid_content_Context_2Landroid_util_AttributeSet_2II+1156)
  at android.widget.TextView.<init>(TextView.java:727)
  at android.widget.TextView.<init>(TextView.java:682)
  at android.widget.TextView.<init>(TextView.java:678)
  at java.lang.reflect.Constructor.newInstance!(Native method)

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

"LeuiRunningState:Background" prio=5 tid=28 WaitingPerformingGc

"AsyncTask #6" prio=5 tid=20 WaitingPerformingGc

综上可以得出大致结论,Tid=28,20线程执行GC,导致主线程申请内存被Block.  但是进一步思考,应用GC是常有的事,但是为何这次需要这么长时间呢,带着疑问我们看看进程的内存使用情况:

Total number of allocations 9887486

Total bytes allocated 732MB

Total bytes freed 476MB

Free memory 5KB

Free memory until GC 5KB

Free memory until OOME 5KB

Total memory 256MB

Max memory 256MB

上面发现,应用已使用256Mb, 距离OOM只有5K,内存对象超过998万个,也就是说GC过程需要扫描这些对象的巨大部分,导致耗时很久,另外内存距离OOM只有5kb,说明有内存泄漏,或内存使用不合理。

综上,对于这个问题得出结论,应用进程内存存在泄漏或使用不当,导致GC时间过程,产生ANR.

 

实例二:应用内部线程逻辑依赖关系导致超时,触发ANR

示例:
第一步,观察Trace 主线程堆栈,发现主线程在Binder通信过程被Block.

"main" prio=5 tid=1 Native
  | group="main" sCount=1 dsCount=0 obj=0x75f0eaa8 self=0x7fad046a00
  | sysTid=4298 nice=-6 cgrp=default sched=0/0 handle=0x7fb1d18fe8
  | state=S schedstat=( 79488910537 19985244611 169915 ) utm=6564 stm=1384 core=0 HZ=100
  | stack=0x7fc237c000-0x7fc237e000 stackSize=8MB
  | held mutexes=
  kernel: (couldn't read /proc/self/task/4298/stack)
  native: #00 pc 00000000000683d0  /system/lib64/libc.so (__ioctl+4)
  native: #01 pc 00000000000723f8  /system/lib64/libc.so (ioctl+100)
  native: #02 pc 000000000002d584  /system/lib64/libbinder.so (_ZN7android14IPCThreadState14talkWithDriverEb+164)
  native: #03 pc 000000000002e050  /system/lib64/libbinder.so (_ZN7android14IPCThreadState15waitForResponseEPNS_6ParcelEPi+104)
  native: #04 pc 000000000002e2c4  /system/lib64/libbinder.so (_ZN7android14IPCThreadState8transactEijRKNS_6ParcelEPS1_j+176)
  native: #05 pc 0000000000025654  /system/lib64/libbinder.so (_ZN7android8BpBinder8transactEjRKNS_6ParcelEPS1_j+64)
  native: #06 pc 00000000000e0928  /system/lib64/libandroid_runtime.so (???)
  native: #07 pc 000000000139ba24  /system/framework/arm64/boot.oat (Java_android_os_BinderProxy_transactNative__ILandroid_os_Parcel_2Landroid_os_Parcel_2I+200)
  at android.os.BinderProxy.transactNative(Native method)
  at android.os.BinderProxy.transact(Binder.java:503)
  at android.nfc.INfcAdapter$Stub$Proxy.setAppCallback(INfcAdapter.java:529)
  at android.nfc.NfcActivityManager.requestNfcServiceCallback(NfcActivityManager.java:339)
  at android.nfc.NfcActivityManager.setNdefPushMessageCallback(NfcActivityManager.java:309)

第二步,进一步查找此线程在和哪个进程进行通信,搜索关键字“setAppCallback”(Android命名习惯,客户端和服务端函数命名基本相同),在Nfc的Binder_3线程响应了客户端请求,但在处理过程中被线程1阻塞,顺着再看看线程1状态

"Binder_3" prio=5 tid=17 Blocked

  | group="main" sCount=1 dsCount=0 obj=0x12ddf0a0 self=0x7fa670f000

  | sysTid=3183 nice=-6 cgrp=default sched=0/0 handle=0x7f93c304

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值