内存管理-自动释放池(autoreleasepool)

一、自动释放池

在MRC环境下对象的内存管理是程序员自己管理的,当对象需要释放的时候,调用一下release,但是这样这样带来了很大的不便,代码必须要写在对象调用release的前面,这样很是不方便。为了解决这个问题,可以在创建出对象的时候自动调用一下autorelease,这样就不需要我们自己调用对象release方法了

1、autorelease

  • RevanPerson
#import 

@interface RevanPerson : NSObject

@end

#import "RevanPerson.h"

@implementation RevanPerson

- (void)dealloc {
    NSLog(@"%s", __func__);
    [super dealloc];
}
@end
  • 测试代码
#import 
#import "RevanPerson.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        RevanPerson *person = [[[RevanPerson alloc] init] autorelease];
    }
    return 0;
}
  • 打印输出
2018-07-28 23:35:42.745168+0800 09-自动释放池[61827:3974115] -[RevanPerson dealloc]
Program ended with exit code: 0
  • 使用autorelease可以解决手动调用release

2、查看源码

xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc [OC文件] -o [.cpp文件]
  • main方法
int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
        RevanPerson *person = ((RevanPerson *(*)(id, SEL))(void *)objc_msgSend)((id)((RevanPerson *(*)(id, SEL))(void *)objc_msgSend)((id)((RevanPerson *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("RevanPerson"), sel_registerName("alloc")), sel_registerName("init")), sel_registerName("autorelease"));
    }
    return 0;
}
  • 简化后的main方法
int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ {
         __AtAutoreleasePool __autoreleasepool; 
         RevanPerson *person = [[[RevanPerson alloc] init] autorelease];
    }
    return 0;
}

3、__AtAutoreleasePool结构体

struct __AtAutoreleasePool {
    //构造函数,在创建结构体的时候调用
  __AtAutoreleasePool() {
      //构造函数会调用objc_autoreleasePoolPush函数,并且会返回 atautoreleasepoolobj指针
      atautoreleasepoolobj = objc_autoreleasePoolPush();
      
  }
    //析构函数,在结构体销毁的时候调用
  ~__AtAutoreleasePool() {
      //析构函数,会调用objc_autoreleasePoolPop函数并且会把 atautoreleasepoolobj指针传入
      objc_autoreleasePoolPop(atautoreleasepoolobj);
  }
  void * atautoreleasepoolobj;
}

在执行@autoreleasepool自动释放池时,首先会生成一个__AtAutoreleasePool类型的__autoreleasepool变量,在创建这个结构体的时候,会调用objc_autoreleasePoolPush函数,并且会返回一个地址,当这个结构体将要销毁时又会调用objc_autoreleasePoolPop函数

二、AutoreleasePoolPage类结构

每个AutoreleasePoolPage对象占用4096字节内存,除了用来存放它内部的成员变量,剩下的空间用来存放autorelease对象的地址。所有的AutoreleasePoolPage对象通过双向链表的形式连接在一起

内存管理-自动释放池(autoreleasepool)_第1张图片
AutoreleasePoolPage.png

三、objc_autoreleasePoolPush函数

  • 1、objc_autoreleasePoolPush函数
void *objc_autoreleasePoolPush(void) {
    return AutoreleasePoolPage::push();
}
  • 2、AutoreleasePoolPage类简单结构
class AutoreleasePoolPage  {
magic_t const magic;
    id *next;
    pthread_t const thread;
    AutoreleasePoolPage * const parent;
    AutoreleasePoolPage *child;
    uint32_t const depth;
    uint32_t hiwat;
      .
      .
      .
}
  • 3、push函数
static inline void *push() {
        id *dest;
        if (DebugPoolAllocation) {
            // Each autorelease pool starts on a new pool page.
            // 传入POOL_BOUNDARY来新建一个AutoreleasePoolPage对象
            dest = autoreleaseNewPage(POOL_BOUNDARY);
        } else {
            // 已经有了AutoreleasePoolPage对象就直接添加
            dest = autoreleaseFast(POOL_BOUNDARY);
        }
        assert(dest == EMPTY_POOL_PLACEHOLDER || *dest == POOL_BOUNDARY);
        return dest;
    }
  • 4、autoreleaseNewPage函数
static __attribute__((noinline))
    id *autoreleaseNewPage(id obj) {
        AutoreleasePoolPage *page = hotPage();
        if (page) return autoreleaseFullPage(obj, page);
        else return autoreleaseNoPage(obj);
    }
  • 5、autoreleaseFullPage函数,会把POOL_BOUNDARY加入到page中
static __attribute__((noinline))
    id *autoreleaseFullPage(id obj, AutoreleasePoolPage *page) {
        // The hot page is full. 
        // Step to the next non-full page, adding a new page if necessary.
        // Then add the object to that page.
        assert(page == hotPage());
        assert(page->full()  ||  DebugPoolAllocation);

        do {
            //page如果child有值,那么就取page中child中存储的那个page
            if (page->child) page = page->child;
            else page = new AutoreleasePoolPage(page);//新创建一个page
        } while (page->full());//判断这个page是否已经满了

        setHotPage(page);//page空间没有满,设置为hot标志
        return page->add(obj);//把obj加入到page中
    }
  • 执行objc_autoreleasePoolPush函数后
    内存管理-自动释放池(autoreleasepool)_第2张图片
    执行objc_autoreleasePoolPush后.png

四、autorelease

对象调用release后会直接被释放,那么调用autorelease会怎么

  • 1、autorelease方法
- (id)autorelease {
    return ((id)self)->rootAutorelease();
}
  • 2、rootAutorelease函数
inline id 
objc_object::rootAutorelease()
{
    if (isTaggedPointer()) return (id)this;
    if (prepareOptimizedReturn(ReturnAtPlus1)) return (id)this;

    return rootAutorelease2();
}
  • 3、rootAutorelease2函数
__attribute__((noinline,used))
id 
objc_object::rootAutorelease2()
{
    assert(!isTaggedPointer());
    return AutoreleasePoolPage::autorelease((id)this);
}
  • 4、AutoreleasePoolPage的autorelease函数并且把对象传入
    static inline id autorelease(id obj)
    {
        assert(obj);
        assert(!obj->isTaggedPointer());
        id *dest __unused = autoreleaseFast(obj);
        assert(!dest  ||  dest == EMPTY_POOL_PLACEHOLDER  ||  *dest == obj);
        return obj;
    }
  • 5、autoreleaseFast函数
static inline id *autoreleaseFast(id obj)
    {
        AutoreleasePoolPage *page = hotPage();
        if (page && !page->full()) {//page空间没有满
            return page->add(obj);//把obj对象加入到page空间中
        } else if (page) {
            return autoreleaseFullPage(obj, page);
        } else {
            return autoreleaseNoPage(obj);
        }
    }
  • 对象调用了autorelease方法后都会被加入到自动释放池创建的page对象中
    内存管理-自动释放池(autoreleasepool)_第3张图片
    对象调用autorelease.png

五、objc_autoreleasePoolPop函数

当__AtAutoreleasePool结构销毁时,会调用结构体的析构函数objc_autoreleasePoolPop函数

  • 1、objc_autoreleasePoolPop函数
void
objc_autoreleasePoolPop(void *ctxt)
{
    AutoreleasePoolPage::pop(ctxt);
}
  • 2、调用AutoreleasePoolPage类中的pop函数,ctxt参数就是构造函数返回的地址
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;//token中存储的内容就是 POOL_BOUNDARY
        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();
        //开始释放对象直到stop
        page->releaseUntil(stop);
        //下面是处理有多个 page 的情况
        // memory: delete empty children
        if (DebugPoolAllocation  &&  page->empty()) {//如果当前的page为空
            // special case: delete everything during page-per-pool debugging
            AutoreleasePoolPage *parent = page->parent;//找到上一个page
            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();
            }
        }
    }
  • 3、releaseUntil函数
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
    }
  • 4、对象移除objc_release
__attribute__((aligned(16)))
void 
objc_release(id obj)
{
    if (!obj) return;
    if (obj->isTaggedPointer()) return;
    return obj->release();
}
  • 5、调用对象的release函数
inline void
objc_object::release()
{
    assert(!isTaggedPointer());

    if (fastpath(!ISA()->hasCustomRR())) {
        rootRelease();
        return;
    }

    ((void(*)(objc_object *, SEL))objc_msgSend)(this, SEL_release);
}
  • 6、给这个对象发送SEL_release消息
  • 7、page销毁page->kill()

六、实践

可以通过以下私有函数来查看自动释放池的情况

extern void _objc_autoreleasePoolPrint(void);

-1、测试代码

#import 
#import "RevanPerson.h"

extern void _objc_autoreleasePoolPrint(void);

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        RevanPerson *person1 = [[[RevanPerson alloc] init] autorelease];
        RevanPerson *person2 = [[[RevanPerson alloc] init] autorelease];
        RevanPerson *person3 = [[[RevanPerson alloc] init] autorelease];
        
        
        _objc_autoreleasePoolPrint();
        
        
        @autoreleasepool {
            for (int i = 0; i < 600; i++) {
                RevanPerson *person4 = [[[RevanPerson alloc] init] autorelease];
            }
            
            @autoreleasepool {
                RevanPerson *person5 = [[[RevanPerson alloc] init] autorelease];
                RevanPerson *person6 = [[[RevanPerson alloc] init] autorelease];
            }
        }
    }
    return 0;
}
  • 打印输出
objc[62998]: ##############
objc[62998]: AUTORELEASE POOLS for thread 0x100392380
objc[62998]: 4 releases pending.
objc[62998]: [0x103002000]  ................  PAGE  (hot) (cold)//这是一个page
objc[62998]: [0x103002038]  ################  POOL 0x103002038 //这个push时加入的POOL_BOUNDARY
objc[62998]: [0x103002040]       0x102804200  RevanPerson
objc[62998]: [0x103002048]       0x102804060  RevanPerson
objc[62998]: [0x103002050]       0x102804390  RevanPerson
objc[62998]: ##############
  • 2、测试代码
#import 
#import "RevanPerson.h"

extern void _objc_autoreleasePoolPrint(void);

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        RevanPerson *person1 = [[[RevanPerson alloc] init] autorelease];
        RevanPerson *person2 = [[[RevanPerson alloc] init] autorelease];
        RevanPerson *person3 = [[[RevanPerson alloc] init] autorelease];
        
        @autoreleasepool {
            for (int i = 0; i < 600; i++) {
                RevanPerson *person4 = [[[RevanPerson alloc] init] autorelease];
            }
            
            _objc_autoreleasePoolPrint();
            
            @autoreleasepool {
                RevanPerson *person5 = [[[RevanPerson alloc] init] autorelease];
                RevanPerson *person6 = [[[RevanPerson alloc] init] autorelease];
            }
        }
    }
    return 0;
}
  • 打印输出
objc[63030]: ##############
objc[63030]: AUTORELEASE POOLS for thread 0x100392380
objc[63030]: 605 releases pending.
objc[63030]: [0x101006000]  ................  PAGE (full)  (cold)//第一个page对象的存储空间已经满了
objc[63030]: [0x101006038]  ################  POOL 0x101006038 //第一个自动释放池push时加入的POOL_BOUNDARY
objc[63030]: [0x101006040]       0x100533660  RevanPerson
objc[63030]: [0x101006048]       0x1005334c0  RevanPerson
objc[63030]: [0x101006050]       0x100525040  RevanPerson
objc[63030]: [0x101006058]  ################  POOL 0x101006058 //第二个自动释放池push时加入的POOL_BOUNDARY
objc[63030]: [0x101006060]       0x100526ec0  RevanPerson
objc[63030]: [0x101006068]       0x100525a00  RevanPerson
objc[63030]: [0x101006070]       0x100522ee0  RevanPerson
objc[63030]: [0x101006078]       0x100522380  RevanPerson
objc[63030]: [0x101006080]       0x1005218f0  RevanPerson
.
.
.
.

objc[63030]: [0x101006ff0]       0x100535f10  RevanPerson
objc[63030]: [0x101006ff8]       0x100535f20  RevanPerson
objc[63030]: [0x101008000]  ................  PAGE  (hot) //第一个page对象的存储空间已经满了,第二个page开始
objc[63030]: [0x101008038]       0x100535f30  RevanPerson
objc[63030]: [0x101008040]       0x100535f40  RevanPerson

七、小结

  • 自动释放池

    • __AtAutoreleasePool结构体调用push函数时,会将一个POOL_BOUNDARY入栈,并且返回其存放的地址
    • __AtAutoreleasePool结构体调用pop函数时,传入一个POOL_BOUNDARY的内存地址,会从最后一个入栈的对象开始发送release消息,直到遇到这个POOL_BOUNDARY
  • autorelease

    • 将对象自己加入到page中
  • AutoreleasePoolPage

    • id *next指向了下一个能存放autorelease对象地址的区域
      内存管理-自动释放池(autoreleasepool)_第4张图片
      AutoreleasePoolPage.png

你可能感兴趣的:(内存管理-自动释放池(autoreleasepool))