美文网首页
崩溃优化笔记

崩溃优化笔记

作者: 952625a28d0d | 来源:发表于2019-01-16 15:12 被阅读10次

Java崩溃

  • Java 崩溃就是在 Java 代码中,出现了未捕获异常,导致程序异常退出

Native崩溃

  • 代码中访问非法地址,也可能是地址出现了问题,或者发生了程序主动abort,这些都会产生响应的signal信号,导致程序异常退出。

  • 程序捕获崩溃流程


    image.png

Native崩溃捕获的难点

  • 文件句柄泄露,导致日志创建失败。
    • 提前申请文件句柄fd预留以防止出现这种情况
  • 栈溢出导致日志创建失败
    • 为了防止栈溢出导致进程没有空间创建调用栈执行处理函数,我们通常会使用常见的 signalstack。在一些特殊情况,我们可能还需要直接替换当前栈,所以这里也需要在堆中预留部分空间。
  • 内存耗尽,导致日志生成失败
    • 这个时候我们无法安全地分配内存,也不敢使用 stl 或者 libc的函数,因为他们内部实现会分配堆内存。这个时候如果继续分配内存,很有可能导致二次崩溃的情况。Breckpad做的比较彻底,重新封装了Linux Syscall Support,来避免直接调用libc

选择合适的崩溃服务

  • 从产品化和社区维护服务来讲,腾讯的Bugly国内做的不错
  • 从捕获深度来讲,阿里UC团队打造的啄木鸟平台不错
  • 从国际化的角度来看,Fabric做的不错

客观衡量崩溃

UV 崩溃率 = 发生崩溃的 UV / 登录 UV

常见崩溃类型

处理崩溃的误区

  • 使用try catch 消化掉了java崩溃
  • 不采集所有的Native崩溃

其他影响App稳定性的东西

  • ANR(Application Not Responding)程序没有响应
  • 国内微信利用Hardcoder框架
  • https://mp.weixin.qq.com/s/9Z8j3Dv_5jgf7LDQHKA0NQ?
  • 监控消息队列的运行时间
    • 异常退出的情形
      • 主动自杀。Process.killProcess()、exit()
      • 崩溃。出现了Java和Native崩溃
      • 系统重启;系统出现异常、断电、用户主动重启等。可以通过比较系统开机运行时间是不是比之前记录的值更小。
      • 被系统杀死,low memory killer、从系统的任务管理器中抹掉
      • ANR

异常率

UV 异常率 = 发生异常退出或崩溃的 UV / 登录 UV

崩溃分析

  • 进程名、线程名、崩溃是发生在前台进程还是后台进程?是不是发生在UI线程?
  • 崩溃的堆栈类型,是属于Java崩溃?Native崩溃还是ANR?
Process Name: 'com.sample.crash'
Thread Name: 'MyThread'

java.lang.NullPointerException
    at ...TestsActivity.crashInJava(TestsActivity.java:275)
  • logcat 记录在/system/etc/event-log-tags中
system logcat:
10-25 17:13:47.788 21430 21430 D dalvikvm: Trying to load lib ... 
event logcat:
10-25 17:13:47.788 21430 21430 I am_on_resume_called: 生命周期
10-25 17:13:47.788 21430 21430 I am_low_memory: 系统内存不足
10-25 17:13:47.788 21430 21430 I am_destroy_activity: 销毁 Activty
10-25 17:13:47.888 21430 21430 I am_anr: ANR 以及原因
10-25 17:13:47.888 21430 21430 I am_kill: APP 被杀以及原因
  • 机型、系统、厂商、CPU、ABI、Linux 版本

  • 设备状态:是否 root、是否是模拟器

  • 内存信息

  • OOM ANR 虚拟内存耗尽等都跟内存有直接关系。

  • 2GB以上和2GB以下崩溃率差别很大,2GB以下的崩溃率是2GB以上的崩溃率的几倍

  • 系统剩余内存

    • 关于系统剩余内存,可以直接读取文件/proc/meminfo,当系统内存低于10%,OOM、大量GC、系统频繁自杀的问题很容易出现
    • 应用使用内存
      • 可通过 /proc/self/smap 来计算
    • 虚拟使用内存
      • 可以通过/proc/self/status得到,但是很多OOM、tgkill等问题都是虚拟内存不足导致的
Name:     com.sample.name   // 进程名
FDSize:   800               // 当前进程申请的文件句柄个数
VmPeak:   3004628 kB        // 当前进程的虚拟内存峰值大小
VmSize:   2997032 kB        // 当前进程的虚拟内存大小
Threads:  600               // 当前进程包含的线程个数

资源信息

  • 文件句柄fd
    一般可通过 /proc/self/limits 得到,一般单个进程允许打开的最大句柄个数为1024个。但如果超过800就会非常危险。
opened files count 812:
0 -> /dev/null
1 -> /dev/log/main4 
2 -> /dev/binder
3 -> /data/data/com.crash.sample/files/test.config
  • 线程数
    一般线程数超过400非常危险。
 threads count 412:               
 1820 com.sample.crashsdk                         
 1844 ReferenceQueueD                                             
 1869 FinalizerDaemon   
 ...  
  • JNI 如果使用不注意,就会出现引用失效、引用爆表等问题。可以使用DumpReferenceTables来统计JNI的引用表。进一步分析是否出现了JNI泄露等问题。

应用信息

  • 崩溃场景
  • 关键操作路径
  • 其他自定义信息

崩溃分析

java.util.concurrent.TimeoutException: 
         android.os.BinderProxy.finalize() timed out after 10 seconds
at android.os.BinderProxy.destroy(Native Method)
at android.os.BinderProxy.finalize(Binder.java:459)
  • Logcat

  • 查找共性

  • 机型、系统、ROM、厂商、ABI、X86等等

  • 第三步

    • 尝试复现
    • 系统崩溃
      • 查找可能的原因:系统版本、厂商修改ROM
      • 尝试规避 是否使用了不恰当的API
    • HOOK解决
      • Java HOOK 和 Native HOOK
      • Toast显示的时候窗口已经失效了,例子:
android.view.WindowManager$BadTokenException: 
   at android.view.ViewRootImpl.setView(ViewRootImpl.java)
   at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java)
   at android.view.WindowManagerImpl.addView(WindowManagerImpl.java4)
   at android.widget.Toast$TN.handleShow(Toast.java)
  • 为什么8.0的系统不会出这个问题?是因为8.0的源码中已经catch住了
try {
  mWM.addView(mView, mParams);
  trySendAccessibilityEvent();
} catch (WindowManager.BadTokenException e) {
  /* ignore */
}
  • 那么就可以采用8.0的做法,直接Catch住这个异常

相关文章

网友评论

      本文标题:崩溃优化笔记

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