Java崩溃
- Java 崩溃就是在 Java 代码中,出现了未捕获异常,导致程序异常退出
Native崩溃
-
代码中访问非法地址,也可能是地址出现了问题,或者发生了程序主动abort,这些都会产生响应的signal信号,导致程序异常退出。
-
程序捕获崩溃流程
image.png
Native崩溃捕获的难点
- 文件句柄泄露,导致日志创建失败。
- 提前申请文件句柄fd预留以防止出现这种情况
- 栈溢出导致日志创建失败
- 为了防止栈溢出导致进程没有空间创建调用栈执行处理函数,我们通常会使用常见的 signalstack。在一些特殊情况,我们可能还需要直接替换当前栈,所以这里也需要在堆中预留部分空间。
- 内存耗尽,导致日志生成失败
- 这个时候我们无法安全地分配内存,也不敢使用 stl 或者 libc的函数,因为他们内部实现会分配堆内存。这个时候如果继续分配内存,很有可能导致二次崩溃的情况。Breckpad做的比较彻底,重新封装了Linux Syscall Support,来避免直接调用libc
选择合适的崩溃服务
- 从产品化和社区维护服务来讲,腾讯的Bugly国内做的不错
- 从捕获深度来讲,阿里UC团队打造的啄木鸟平台不错
- 从国际化的角度来看,Fabric做的不错
客观衡量崩溃
UV 崩溃率 = 发生崩溃的 UV / 登录 UV
常见崩溃类型
- 启动崩溃
阿里有个安全模式
https://mp.weixin.qq.com/s?__biz=MzUxMzcxMzE5Ng==&mid=2247488429&idx=1&sn=448b414a0424d06855359b3eb2ba8569&source=41#wechat_redirect - PV崩溃
- 重复崩溃
处理崩溃的误区
- 使用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崩溃 一般是NullPointerException 空指针的问题
- Native崩溃 需要观察signal、code
http://www.mkssoftware.com/docs/man5/siginfo_t.5.asp
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住这个异常
网友评论