autoreleasepool原理

声明:本文仅供记忆使用,并不适合新手小白观看

autorealesepool 原理

  • 自动释放池(即所有的AutoreleasePoolPage对象)是以栈为结点通过双向链表的形式组合而成;
  • 自动释放池与线程一一对应;
  • 每个AutoreleasePoolPage对象占用4096字节内存,除了56字节用来存放它内部的成员变量,剩下4040字节的空间用来存放autorelease对象的地址
  • @autoreleasepool {}就相当于在代码前添加一个__AtAutoreleasePool结构体,所有局部变量通过push方法添加到AutoreleasePoolPage,若不够开辟新的AutoreleasePoolPage,作用域结束后通过pop方法释放对象
  • 调用push方法会将一个POOL_BOUNDARY(哨兵对象)入栈,并且返回其存放的内存地址
  • 调用pop方法时传入一个POOL_BOUNDARY(哨兵对象)的内存地址,会从最后一个入栈的对象开始发送release消息,直到遇到这个POOL_BOUNDARY(哨兵对象)
  • next指向了下一个能存放autorelease对象地址的区域

autorealese相关问题

  • ARC 环境下,autorelease对象在什么时候释放?

    1. 手动干预释放,手动添加的@autoreleasepool{}

      在当前作用域大括号结束时释放。

    2. 系统干预释放有runloop,例如主线程添加runloop的子线程

      在事件循环(RunLoop)的每次循环开始时,在当前线程创建一个自动释放池,并在每次循环结束时销毁它,在销毁时释放自动释放池中的所有autorelease对象。

      iOS在主线程的Runloop中注册了2个Observer
      第1个Observer --------
      监听了kCFRunLoopEntry(即将进入Loop)事件,会调用objc_autoreleasePoolPush()
      第2个Observer --------
      监听了kCFRunLoopBeforeWaiting(即将进入休眠)事件,会调用objc_autoreleasePoolPop()objc_autoreleasePoolPush()
      监听了kCFRunLoopBeforeExi(即将退出Loop)t事件,会调用objc_autoreleasePoolPop()

    3. 系统干预释放无runloop,例如没有添加runloop的子线程

      每一个线程创建的时候就会有一个autoreleasepool的创建,并且在线程退出的时候,清空整个autoreleasepool。(ps:如果在子线程中设置一个循环,autorelease对象确实无法释放)

  • ARC环境下,需不需要手动添加@autoreleasepool?

    如果我们需要在循环中创建了很多临时的autorelease对象,则手动添加@autoreleasepool来管理这些对象可以很大程度地减少内存峰值。比如在for循环中alloc图片数据等内存消耗较大的场景,需要手动添加@autoreleasepool

  • 如果对 NSAutoreleasePool 对象调用 autorelease 方法会发生什么情况?

    1
    2
     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    [pool autorelease];

    抛出异常NSInvalidArgumentException并导致程序Crash,异常原因:不能对NSAutoreleasePool对象调用autorelease。

autorealesepool详细过程

  • autoreleasepool生成c++文件

    xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp

    1
    2
    3
    4
    5
    6
    7
    8
    9
    //原始OC代码
    @autoreleasepool {
    NSObject *obj = [[NSObject alloc] init];
    }
    //c++代码
    {
    __AtAutoreleasePool __autoreleasepool;
    NSObject *obj = [[NSObject alloc] init];
    }
  • 一起就是在开始的时候调用 objc_autoreleasePoolPush()结束时候调用objc_autoreleasePoolPop(atautoreleasepoolobj)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
      struct __AtAutoreleasePool {
    //构造函数,在创建结构体的时候调用
    __AtAutoreleasePool() {
    atautoreleasepoolobj = objc_autoreleasePoolPush();
    }
    // 写在autoreleasepool内的代码
    NSObject *obj = [[NSObject alloc] init];

    //析构函数,在结构体销毁的时候调用
    ~__AtAutoreleasePool(){
    objc_autoreleasePoolPop(atautoreleasepoolobj);
    }
    void * atautoreleasepoolobj;
    };
  • 具体源码可以再Runtime源码中查看,push相关源码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    void * objc_autoreleasePoolPush(void) {
    return AutoreleasePoolPage::push();
    }
    static inline void *push() {
    id *dest;
    if (DebugPoolAllocation) {
    // Each autorelease pool starts on a new pool page.
    dest = autoreleaseNewPage(POOL_BOUNDARY);
    } else {
    dest = autoreleaseFast(POOL_BOUNDARY);
    }
    assert(dest == EMPTY_POOL_PLACEHOLDER || *dest == POOL_BOUNDARY);
    return dest;
    }
    static inline id *autoreleaseFast(id obj) {
    AutoreleasePoolPage *page = hotPage();
    if (page && !page->full()) {//page没有满,就把obj对象加到page
    return page->add(obj);
    } else if (page) {//page满了 创建新的page
    return autoreleaseFullPage(obj, page);
    } else {
    return autoreleaseNoPage(obj);
    }
    }
  • 具体源码可以再Runtime源码中查看,pop相关源码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    static inline void pop(void *token)  {
    AutoreleasePoolPage *page;
    id *stop;
    if (token == (void*)EMPTY_POOL_PLACEHOLDER) {
    // Popping the top-level placeholder pool.
    if (hotPage()) {
    // Pool was used. Pop its contents normally.
    // Pool pages remain allocated for re-use as usual.
    pop(coldPage()->begin());
    } else {
    // Pool was never used. Clear the placeholder.
    setHotPage(nil);
    }
    return;
    }

    page = pageForPointer(token);
    stop = (id *)token;
    if (*stop != POOL_BOUNDARY) {
    if (stop == page->begin() && !page->parent) {
    // Start of coldest page may correctly not be POOL_BOUNDARY:
    // 1. top-level pool is popped, leaving the cold page in place
    // 2. an object is autoreleased with no pool
    } else {
    // Error. For bincompat purposes this is not
    // fatal in executables built with old SDKs.
    return badPop(token);
    }
    }

    if (PrintPoolHiwat) printHiwat();

    page->releaseUntil(stop);

    // memory: delete empty children
    if (DebugPoolAllocation && page->empty()) {
    // special case: delete everything during page-per-pool debugging
    AutoreleasePoolPage *parent = page->parent;
    page->kill();
    setHotPage(parent);
    } else if (DebugMissingPools && page->empty() && !page->parent) {
    // special case: delete everything for pop(top)
    // when debugging missing autorelease pools
    page->kill();
    setHotPage(nil);
    }
    else if (page->child) {
    // hysteresis: keep one empty child if page is more than half full
    if (page->lessThanHalfFull()) {
    page->child->kill();
    }
    else if (page->child->child) {
    page->child->child->kill();
    }
    }
    }
  • AutoreleasePoolPage中代码较多,筛选出主要代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
     class AutoreleasePoolPage  {
    magic_t const magic;//用来校验 AutoreleasePoolPage 的结构是否完整
    id *next;//指向最新添加的 autoreleased 对象的下一个位置,初始化时指向 begin() ;
    pthread_t const thread;//指向当前线程;说明了,AutoreleasePoolPage和线程一一对应的。
    AutoreleasePoolPage * const parent;//指向父结点
    AutoreleasePoolPage *child;//指向子结点
    uint32_t const depth;//depth 代表深度,从 0 开始,往后递增 1;
    uint32_t hiwat;//hiwat 代表 high water mark
    id * begin() {
    return (id *) ((uint8_t *)this+sizeof(*this));
    }
    id * end() {
    return (id *) ((uint8_t *)this+SIZE);
    }
    bool empty() {
    return next == begin();
    }
    bool full() {
    return next == end();
    }
    bool lessThanHalfFull() {
    return (next - begin() < (end() - begin()) / 2);
    }
    ...
    }

参考文章

Runtime源码
探索子线程autorelease对象的释放时机
iOS - 聊聊 autorelease 和 @autoreleasepool