异常捕获
异常类型
- Mach 异常:EXC_CRASH
- UNIX 信号:SIGABRT
- NSException 异常:应用层,通过 NSUncaughtExceptionHandler 捕获
异常捕获
- 利用 Mach API 捕获 Mach 异常
- 通过 POSIX(可移植性操作系统接口) API 注册 signal(SIGSEGV,signalHandler) 来捕获 UNIX 异常 信号
- 注册 NSUncaughtExceptionHandler 来捕获应用级异常
所有 Mach 异常都在 host 层被ux_exception转换为相应的 Unix 信号,并通过threadsignal将信号投递到出错的线程。iOS 中的 POSIX API 就是通过 Mach 之上的 BSD 层实现的。
EXC_BAD_ACCESS (SIGSEGV)表示的意思是:Mach 层的EXC_BAD_ACCESS异常,在 host 层被转换成 SIGSEGV 信号投递到出错的线程。既然最终以信号的方式投递到出错的线程,那么就可以通过注册 signalHandler 来捕获信号:
signal(SIGSEGV,signalHandler);
Mach 异常 +Unix 信号方式: Github 上多数开源项目都采用的这种方式,即使在优选捕获 Mach 异常的情况下,也放弃捕获EXC_CRASH异常,而选择捕获与之对应的 SIGABRT 信号 为什么需要两种方式:(内核发送一个EXC_CRASH mach 异常来表示 SIGABRT 终止。在这种情况下,捕获进程中的 Mach 异常会导致进程死锁在不间断的等待中。因此,我们回退 SIGABRT 的 BSD 信号处理程序,并且不注册EXC_CRASH。)
### Crash 收集
### crash类型 SIGILL 执行了非法指令,一般是可执行文件出现了错误 SIGTRAP 断点指令或者其他trap指令产生 SIGABRT 调用abort产生 SIGBUS 非法地址。比如错误的内存类型访问、内存地址对齐等 SIGSEGV 非法地址。访问未分配内存、写入没有写权限的内存等 SIGFPE 致命的算术运算。比如数值溢出、NaN数值等
### 野指针 访问野指针是不会Crash的,只有野指针指向的地址被写上了有问题的数据才会引发Crash
野指针放大iOS监控-野指针定位 如何定位Obj-C野指针随机Crash(一):先提高野指针Crash率
- 对象释放后在内存上填上不可访问的数据,其实这种技术其实一直都有,xcode的Enable Scribble就是这个作用。
- 要重写对象释放的接口,重写哪个呢?NSObject的dealloc、runtime的 object_dispose,C的free应该都是可以,但是各有优点,我选择的是覆盖面最广的free,free是C的函数,重写了它之后还可以顺带解决一部分C的野指针问题。
- 填充的不可访问的数据的长度怎么确定?获取内存长度的接口不在标准库中,好在在Mac和iOS中可以用malloc_size就可以。
- 填什么?和xcode一样,填0x55。
如何定位Obj-C野指针随机Crash(二):让非必现Crash变成必现
- 当free被调用的时候我们不真的调用free,而是自己保留着内存,这样系统不知道这片内存已经不需要用了,自然就不会被再次写上别的数据
- 为了防止系统内存过快耗尽,还需要额外多做几件事:
1.自己保留的内存大于一定值的时候就释放一部分,防止被系统杀死。
2.系统内存警告的时候,也要释放一部分内存。
- 最多存100M, 最多存1010241024个指针,每次释放多少个指针
- 具体代码看博客
如何定位Obj-C野指针随机Crash(三):加点黑科技让Crash自报家门 我们需要自己写一个类,用它的isa来替换已经释放的对象的isa。如果不出我们所料,我们用自己的类覆盖之后,之前调用的sel就换成了调用我们自己的类的某个sel
性能优化可以分为:崩溃治理、内存优化、cpu优化、启动优化、卡顿优化(耗时优化)、APM相关(fps、卡顿、cpu监控) 主线程占1M,子线程占用512KB