objc_release Crash 分析(EXC_BAD_ACCESS)

2,326 阅读3分钟

前因

在日常巡查崩溃日志发现偶现日志,基本无法定位到项目代码。

image.png

偶现联调

偶然然我碰到了,查看堆栈并对比:

image.png

汇编分析

第一行是x0寄存器中调用objc_release
第二行,第三行相当月if 0x = null {return}
第四行,x0是个指针,把指针指向的对象复制给x8
第五行,和 0xffffffff8作与运算,相当于该对象的后三位抹0⃣️
第六行,是x9 偏移 0x20 对应的指针放入到x10中

image.png 其中0xffffffff8与运算这个显然是一个寻找isa指针的操作。

但是无法以对象输出(po)x8的“对象”。也无法打印x9所指向的地址。

image.png

堆栈分析

(lldb) bt
* thread #19, stop reason = EXC_BAD_ACCESS (code=1, address=0x12e6d6f80)
  * frame #0: 0x00000001a2488dd0 libobjc.A.dylib`objc_release + 16
    frame #1: 0x00000001a248a57c libobjc.A.dylib`AutoreleasePoolPage::releaseUntil(objc_object**) + 204
    frame #2: 0x00000001a248a41c libobjc.A.dylib`objc_autoreleasePoolPop + 212
    frame #3: 0x00000001a248a878 libobjc.A.dylib`AutoreleasePoolPage::tls_dealloc(void*) + 140
    frame #4: 0x00000001d95fce4c libsystem_pthread.dylib`_pthread_tsd_cleanup + 524
    frame #5: 0x00000001d95f4c5c libsystem_pthread.dylib`_pthread_exit + 84
    frame #6: 0x00000001d95f4100 libsystem_pthread.dylib`pthread_exit + 92
    frame #7: 0x000000018ebc47b8 Foundation`+[NSThread exit] + 20
    frame #8: 0x000000018ed04320 Foundation`__NSThread__start__ + 884
    frame #9: 0x00000001d95f4bfc libsystem_pthread.dylib`_pthread_start + 320
(lldb) 

摄取有用信息:
开辟线程导致的异常
线程退出的时候释放tls(线程本地缓存)导致的

在obj4源码中寻找:

image.png

image.png

暂时在源码中没有斩获有用信息。

综合分析

这个crash发生的时机是在应用启动的时候,崩溃的堆栈也都是系统的调用。在启动的时候会有大量的开辟线程的操作,内存也会大规模的上涨。KKThreadMonitor来观察线程的开辟情况。

image.png

image.png

image.png

image.png 可以看到,线程的使用情况已经非常夸张了~所以针对于启动阶段线程使用的优化势在必行。等到优化完成后可以再观测崩溃情况(如能优化成功,一定另起一篇吹响牛皮)。

番外篇

就在快完成本文的时候,又在调试的时候遇到了另一处的EXC_BAD_ACCESS,看似两个crash毫不相干,但仔细分析两者崩溃的根本原因其实是同一原因。

image.png

汇编分析

第一行,x0寄存器中调用objc_msgSend 第二行,第三行相当月if 0x = null {return}
第四行,x0是个指针,把指针指向的对象复制给x13
第五行到第八行,是通过ISA指针获取取对象对应的类对象地址,并放到x16寄存器中
第九行,暂存类对象的地址放入到x15寄存器中
第十行,类对象结构体中偏移 0x10获取方法缓存地址

小结

两个crash都是因为访问类对象相关信息而造成的EXC_BAD_ACCESS,还有都是在启动的时候出现的问题。在启动的过程中会有使用大量的内存,有可能是内存不够用系统自动回收加载过类对象所在内存所导致的问题。