AutoReleasePool

602 阅读9分钟

问题

  • 什么是AutoReleasePool ?
  • AutoReleasePool 在OC中有哪几种表现形式 ?
  • ARC 下AutoReleasePool 规则?
  • autoreleasepool 调用流程(释放流程,插入流程)
  • Autoreleasepool 什么时候释放
  • __autoreleasing 作用

什么是AutoReleasePool

简称自动释放池,拿来进行内存管理优化的,解决内存的峰值 陡增的一种机制。在autoreleasepool 当中创建对象,将对象加到autoreleasepool 当中,当自动释放池销毁的时候(啥时候销毁?)会对所有对象做release 操作。

autoreleasepool 有哪几种表现形式

1. NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; //MRC
   id objc = [[NSObject alloc] init];
   [objc autorelease];
   [pool drain];
 2. @autoreleasepool {}  // ARC MRC

ARC下的规则

以alloc ,new, copy 的是不会加入到autoreleasepool当中的。释放是以编译器在适当的时候释放的。

autoreleasepool 解析

在我们看原理之前我想请大家思考一个问题:obj1,obj2,obj3什么时候释放?

@autoreleasepool {
  NSObject *obj1 = [person person];
  @autoreleasepool {
    NSObject *obj2 = [person person];
    @autoreleasepool {
      NSObject *obj3 = [person person];
    }
  }
}

解析:

image-20191120112859974

Clang :

image-20191120113119612

c++ 源码:

image-20191120113217912

__AtAutoreleasePool :

struct __AtAutoreleasePool {
  __AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();}
  ~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);}
  void * atautoreleasepoolobj;
};

Objc-runtime 源码:

void *
objc_autoreleasePoolPush(void)
{
    return AutoreleasePoolPage::push();
}

void
objc_autoreleasePoolPop(void *ctxt)
{
    AutoreleasePoolPage::pop(ctxt);
}

AutoreleasePoolPage: (双向链表)

class AutoreleasePoolPage 
{
#   define EMPTY_POOL_PLACEHOLDER ((id*)1)

#   define POOL_BOUNDARY nil
    static pthread_key_t const key = AUTORELEASE_POOL_KEY;
    static uint8_t const SCRIBBLE = 0xA3;  // 0xA3A3A3A3 after releasing
    static size_t const SIZE = 
#if PROTECT_AUTORELEASEPOOL
        PAGE_MAX_SIZE;  // must be multiple of vm page size
#else
        PAGE_MAX_SIZE;  // size and alignment, power of 2
#endif
    static size_t const COUNT = SIZE / sizeof(id);

    magic_t const magic;
    id *next;
    pthread_t const thread;
    AutoreleasePoolPage * const parent;
    AutoreleasePoolPage *child;
    uint32_t const depth;
    uint32_t hiwat;
}

可以看出autoreleasepool 是双向链表

同时每一个page 又是一个栈结构

调用流程

插入流程

  • __ AtAutoreleasePool __autoreleasepool 调用构造函数 objc_autoreleasePoolPush

    struct __AtAutoreleasePool {
      __AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();}
      ~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);}
      void * atautoreleasepoolobj;
    };
    
  • objc_autoreleasePoolPush

    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;
    }

哨兵对象:

image-20191120175605206

  • autoreleasefast

        static inline id *autoreleaseFast(id obj)
        {
            AutoreleasePoolPage *page = hotPage();
            if (page && !page->full()) { // 没有满
                return page->add(obj);
            } else if (page) { // 满了
                return autoreleaseFullPage(obj, page);
            } else { // page == nil 的时候,子线程的时候 autoreleasepool 是nil,没有初始化,这句代码防止崩溃
                return autoreleaseNoPage(obj);
            }
        }
    
  • 添加对象

    id *add(id obj) { // 代码功能移动指针,并且返回插入的对象的指针地址
            assert(!full());
            unprotect();
            id *ret = next;  // faster than `return next-1` because of aliasing
            *next++ = obj;
            protect();
            return ret;
        }
    

释放流程

  • objc_autoreleasePoolPop

    objc_autoreleasePoolPop(void *ctxt)
    {
        AutoreleasePoolPage::pop(ctxt);
    }
    
  • pop

    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);
            // pool boundary 地址
            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();
    
            //链表中的进行release
            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();
                }
            }
        }
    

    进行释放:

    void releaseUntil(id *stop) 
        {
            // Not recursive: we don't want to blow out the stack 
            // if a thread accumulates a stupendous amount of garbage
            
            while (this->next != stop) {
                // Restart from hotPage() every time, in case -release 
                // autoreleased more objects
                AutoreleasePoolPage *page = hotPage();
    
                // fixme I think this `while` can be `if`, but I can't prove it
                while (page->empty()) {
                    page = page->parent;
                    setHotPage(page);
                }
    
                page->unprotect();
                id obj = *--page->next;
                memset((void*)page->next, SCRIBBLE, sizeof(*page->next));
                page->protect();
    
                if (obj != POOL_BOUNDARY) {
                    objc_release(obj);
                }
            }
    
            setHotPage(this);
    
    #if DEBUG
            // we expect any children to be completely empty
            for (AutoreleasePoolPage *page = child; page; page = page->child) {
                assert(page->empty());
            }
    #endif
    

思考

思考1:

   @autoreleasepool {
        // insert code here...
        id  a = [[NSObject alloc] init];
        
        NSLog(@"dsaf");
        
    }

思考2:

   @autoreleasepool {
        // insert code here...
        id __autoreleasing a = [[NSObject alloc] init];
        
        NSLog(@"dsaf");
        
    }

思考3:

 @autoreleasepool {
        // insert code here...
        animal *a1 = [animal animal1];
        
        @autoreleasepool {
            animal *a2 = a1;
            @autoreleasepool {
                animal *a3 = a1;
            }
        }
        
    }

思考4:

   @autoreleasepool {
        // insert code here...
        animal *a1 = [animal animal1];
        @autoreleasepool {
            animal *a2 = [animal animal1];
            @autoreleasepool {
               animal *a3 = [animal animal1];
            }
        }
        
    }

通过: _objc_autoreleasePoolPrint();

答案1:

objc[26458]: ##############
objc[26458]: AUTORELEASE POOLS for thread 0x1000aa5c0
objc[26458]: 1 releases pending.
objc[26458]: [0x103803000]  ................  PAGE  (hot) (cold)
objc[26458]: [0x103803038]  ################  POOL 0x103803038
objc[26458]: ##############

答案2:

objc[26528]: ##############
objc[26528]: AUTORELEASE POOLS for thread 0x1000aa5c0
objc[26528]: 2 releases pending.
objc[26528]: [0x104803000]  ................  PAGE  (hot) (cold)
objc[26528]: [0x104803038]  ################  POOL 0x104803038
objc[26528]: [0x104803040]       0x103306310  NSObject
objc[26528]: ##############

答案3:

objc[25612]: ##############
objc[25612]: AUTORELEASE POOLS for thread 0x1000aa5c0
objc[25612]: 2 releases pending.
objc[25612]: [0x102802000]  ................  PAGE  (hot) (cold)
objc[25612]: [0x102802038]  ################  POOL 0x102802038
objc[25612]: [0x102802040]       0x100514d10  animal
objc[25612]: ##############
objc[25612]: ##############
objc[25612]: AUTORELEASE POOLS for thread 0x1000aa5c0
objc[25612]: 3 releases pending.
objc[25612]: [0x102802000]  ................  PAGE  (hot) (cold)
objc[25612]: [0x102802038]  ################  POOL 0x102802038
objc[25612]: [0x102802040]       0x100514d10  animal
objc[25612]: [0x102802048]  ################  POOL 0x102802048
objc[25612]: ##############
objc[25612]: ##############
objc[25612]: AUTORELEASE POOLS for thread 0x1000aa5c0
objc[25612]: 4 releases pending.
objc[25612]: [0x102802000]  ................  PAGE  (hot) (cold)
objc[25612]: [0x102802038]  ################  POOL 0x102802038
objc[25612]: [0x102802040]       0x100514d10  animal
objc[25612]: [0x102802048]  ################  POOL 0x102802048
objc[25612]: [0x102802050]  ################  POOL 0x102802050
objc[25612]: ##############

思考4:

objc[21124]: ##############
objc[21124]: AUTORELEASE POOLS for thread 0x1000ad5c0
objc[21124]: 3 releases pending.
objc[21124]: [0x104802000]  ................  PAGE  (hot) (cold)
objc[21124]: [0x104802038]  ################  POOL 0x104802038
objc[21124]: [0x104802040]       0x100593500  __NSCFString
objc[21124]: [0x104802048]       0x100593500  __NSCFString
objc[21124]: ##############
objc[21124]: ##############
objc[21124]: AUTORELEASE POOLS for thread 0x1000ad5c0
objc[21124]: 6 releases pending.
objc[21124]: [0x104802000]  ................  PAGE  (hot) (cold)
objc[21124]: [0x104802038]  ################  POOL 0x104802038
objc[21124]: [0x104802040]       0x100593500  __NSCFString
objc[21124]: [0x104802048]       0x100593500  __NSCFString
objc[21124]: [0x104802050]  ################  POOL 0x104802050
objc[21124]: [0x104802058]       0x100602a10  __NSCFString
objc[21124]: [0x104802060]       0x100602a10  __NSCFString
objc[21124]: ##############
objc[21124]: ##############
objc[21124]: AUTORELEASE POOLS for thread 0x1000ad5c0
objc[21124]: 8 releases pending.
objc[21124]: [0x104802000]  ................  PAGE  (hot) (cold)
objc[21124]: [0x104802038]  ################  POOL 0x104802038
objc[21124]: [0x104802040]       0x100593500  __NSCFString
objc[21124]: [0x104802048]       0x100593500  __NSCFString
objc[21124]: [0x104802050]  ################  POOL 0x104802050
objc[21124]: [0x104802058]       0x100602a10  __NSCFString
objc[21124]: [0x104802060]       0x100602a10  __NSCFString
objc[21124]: [0x104802068]  ################  POOL 0x104802068
objc[21124]: [0x104802070]       0x103f000d0  NSTaggedPointerStringCStringContainer
objc[21124]: ##############
objc[21504]: ##############
objc[21504]: AUTORELEASE POOLS for thread 0x1000ad5c0
objc[21504]: 2 releases pending.
objc[21504]: [0x101802000]  ................  PAGE  (hot) (cold)
objc[21504]: [0x101802038]  ################  POOL 0x101802038
objc[21504]: [0x101802040]       0x10079ef20  __NSCFString
objc[21504]: ##############
objc[21504]: ##############
objc[21504]: AUTORELEASE POOLS for thread 0x1000ad5c0
objc[21504]: 3 releases pending.
objc[21504]: [0x101802000]  ................  PAGE  (hot) (cold)
objc[21504]: [0x101802038]  ################  POOL 0x101802038
objc[21504]: [0x101802040]       0x10079ef20  __NSCFString
objc[21504]: [0x101802048]  ################  POOL 0x101802048
objc[21504]: ##############
objc[21504]: ##############
objc[21504]: AUTORELEASE POOLS for thread 0x1000ad5c0
objc[21504]: 4 releases pending.
objc[21504]: [0x101802000]  ................  PAGE  (hot) (cold)
objc[21504]: [0x101802038]  ################  POOL 0x101802038
objc[21504]: [0x101802040]       0x10079ef20  __NSCFString
objc[21504]: [0x101802048]  ################  POOL 0x101802048
objc[21504]: [0x101802050]  ################  POOL 0x101802050
objc[21504]: ##############

这里加入的操作貌似系统做了相应的优化,不知道为何不同的写法,加入与否受系统控制。不过唯一肯定的是,当我们添加__autoreleasing 修饰的时候是肯定会添加到autoreleasepoolpage 当中的。