autoreleasepool自动释放池

3,518 阅读4分钟

objc下层c++代码

生成c++代码查看

  • clang -rewrite-objc main.m -o main.cpp
  • 脚本直接直接配置到测试工程里

image.png 脚本直接生成c++代码 并打开 image.png 选择脚本 build,xcode直接弹窗打开c++代码 image.png

查找autorelease相关c++代码

image.png

  • 全局查找 __AtAutoreleasePool 是个结构体

image.png

其实说白了就是结构体(自动释放池)构造,析构

  • 自动释放池作用域调整下代码

image.png

构造 objc_autoreleasePoolPush() 压栈

析沟 objc_autoreleasePoolPop(void *) 弹出栈

根据c++代码分析大致了解到,自动释放池其实就是一块作用域时机,作用域开始时构造,作用域结束时析构回收 主要是个栈结构

汇编查看

image.png

image.png

通过汇编,结果更直观,压栈,执行逻辑, 出栈

汇编 进入符号 objc_autoreleasePoolPush 最终得到符号所在的dyld

image.png

发现 objc_autoreleasePoolPush 符号存在于objc源码中,所以我们进入源码

没有源码环境?快速配置一个出来

打开源码调试工程

查找符号 objc_autoreleasePoolPush

image.png

image.png

  • 如果就这样分析,还是一股脑儿就是代码,没什么直观意识,能查看到具体结构信息最好

    关闭arc

image.png

既然提到了打印信息,那么就看源码里有没有相关的调试打印函数,既然有调试,应该会有,拿来用就可以了

前后多看些范围代码,发现了打印函数

image.png

既然C函数,我们可以声明extern来使用

image.png

  • 编译运行

image.png

image.png

image.png image.png

非arc下,autorelease - 对象才会放入池子

  • 如果加个循环

image.png image.png image.png image.png

这时候 其实可以看出关键点了 根据打印逻辑回看源码

AutoreleasePoolPage: AutoreleasePoolPageData

image.png

可以看出是个双向链表 (nil <- page <-> page <-> page <-> page -> nil)

image.png

AutoreleasePoolPage: push()

image.png

image.png

image.png

image.png

释放池push操作 分析一下

  • 释放池是分页设计,就像内存分页概念一样,如果不分页,一下子要面对池子里的一坨很大的数据,人是无法处理超级多的复杂的事情的,这儿自动释放池也一样

  • 不分页的设计会造成池子里对象么次操作 都需要检索整个池子,而池子对象可能很多,效率不高

  • 只有分页对于栈式结构来说,每次操作只在有限长度栈结构内,超过就一页一页pop,之前被压在hot页下面的不活动页变成hot,每次操作只需要检索一小块数据对象

  • 对于嵌套释放池,不分页设计就没有了渐进式内存处理,池子要么整个内存,要么整个释放,但其实从应用逻辑业务角度来看,用户的操作是一个一个访问具体功能,分页才更合理

  • 自动释放池整体可以理解为两层栈结构

    • 外层把一个个page看作一个个单元,page压栈出栈的过程,page大小是固定的,顶层的page为活动的page,也就是当前操作的page

    • page内部也是一个栈结构,object压栈出栈,object可能是常规对象,也可能是边界,用于区分是不是嵌套了池子

    • 池子可能跨越了多个page,也可能就在一个page里,不重要,看边界在哪儿

分析page大小

根据上面的1024次循环 构建1024个对象,打印结果出现两次 page full标识,也就是有3页,两个满页

hot page未满,有15个对象

也就是说每个page ((1024-15) + 1) / 2 = 505 个对象(每个对象指针8字节)【+1是因为还有一个边界8字节】

505 * 8 = 4040字节 ---- 4k == 4096字节 ----- 差56字节? 差在了哪儿?

image.png

image.png

可以理解为 每个page有一个magic_t 结构的头,占用16字节 page大小4k得证

自动释放池嵌套

image.png

image.png

image.png

image.png

自动总释放池扩展

image.png

没有autorelease,对象并没有进入释放池 当前是在非ARC下

现在切回ARC

image.png

其实 alloc new copy multablecopy 等相关修饰前缀 在llvm编译期间不会加入到释放池

自动释放池 pop操作与push相反,简单来讲就是 从hotpage开始遍历release对象,然后page -> parent 找到code page设置为hot page,当前hotpage 执行kill