介绍iOS的自动释放池原理,把底层的方法分析一遍,并给每个方法都添加了注释
我们一般使用自动释放池是直接使用@autoreleasePool方法,如下
底层会帮我们再次翻译成另一种形式
int main(int argc, const char * argv[]) {
@autoreleasePool{
}
return 0;
}
底层会翻译成这样
创建一个局部变量接收push函数的返回值,查看objc_autoreleasePoolPush和objc_autoreleasePoolPop方法,会发现内部都调用了AutoreleasePoolPage,
//这个是翻译后的自动释放池结构体
extern "C" __declspec(dllimport) void * objc_autoreleasePoolPush(void);
extern "C" __declspec(dllimport) void objc_autoreleasePoolPop(void *);
struct __AtAutoreleasePool {
__AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();}
~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj); }
void * atautoreleasepoolobj;
};
{ __AtAutoreleasePool __autoreleasepool;}
//创建释放池
void * objc_autoreleasePoolPush(void) {
return AutoreleasePoolPage::push();
}
//销毁释放池
void objc_autoreleasePoolPop(void *ctxt) {
AutoreleasePoolPage::pop(ctxt);
}
//main方法,翻译后的是这样
int main(int argc, const char * argv[]) {
{
//atautoreleasepoolobj是哨兵对象
void * atautoreleasepoolobj = objc_autoreleasePoolPush();
objc_autoreleasePoolPop(atautoreleasepoolobj);
}
return 0;
}
AutoreleasePoolPage的具体实现是这样的
struct magic_t {
static const uint32_t M0 = 0xA1A1A1A1;
# define M1 "AUTORELEASE!"
static const size_t M1_len = 12;
uint32_t m[4];
magic_t() {
assert(M1_len == strlen(M1));
assert(M1_len == 3 * sizeof(m[1]));
m[0] = M0;
strncpy((char *)&m[1], M1, M1_len);
}
~magic_t() {
m[0] = m[1] = m[2] = m[3] = 0;
}
bool check() const {
return (m[0] == M0 && 0 == strncmp((char *)&m[1], M1, M1_len));
}
bool fastcheck() const {
#if CHECK_AUTORELEASEPOOL
return check();
#else
return (m[0] == M0);
#endif
}
# undef M1
};
/*
@autoreleasepool{}
extern "C" __declspec(dllimport) void * objc_autoreleasePoolPush(void);
extern "C" __declspec(dllimport) void objc_autoreleasePoolPop(void *);
struct __AtAutoreleasePool {
__AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();}
~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj); }
void * atautoreleasepoolobj;
};
{ __AtAutoreleasePool __autoreleasepool;}
void * objc_autoreleasePoolPush(void) {
return AutoreleasePoolPage::push();
}
void objc_autoreleasePoolPop(void *ctxt) {
AutoreleasePoolPage::pop(ctxt);
}
int main(int argc, const char * argv[]) {
{ void * atautoreleasepoolobj = objc_autoreleasePoolPush(); // do whatever you want objc_autoreleasePoolPop(atautoreleasepoolobj);
}
return 0;
}
*/
//自动释放池,每页4096个字节,双向链表结构
//自动释放池的对象成员占用56个字节,所以添加的对象是从第56个字节开始存储到释放池中
class AutoreleasePoolPage
{
// EMPTY_POOL_PLACEHOLDER is stored in TLS when exactly one pool is
// pushed and it has never contained any objects. This saves memory
// when the top level (i.e. libdispatch) pushes and pops pools but
// never uses them.
# define EMPTY_POOL_PLACEHOLDER ((id*)1)
//分界对象 (以前叫POOL_SENTINEL(哨兵对象)总之是一样的作用)
//在每个自动释放池初始化调用objc_autoreleasePoolPush的时候,都会把一个POOL_BOUNDARY push 到自动释放池的栈顶,并且返回这个POOL_BOUNDARY边界对象。
# 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;
// SIZE-sizeof(*this) bytes of contents follow
//创建一块内存,大小4096个字节
static void * operator new(size_t size) {
return malloc_zone_memalign(malloc_default_zone(), SIZE, SIZE);
}
//释放内存
static void operator delete(void * p) {
return free(p);
}
//加锁
inline void protect() {
#if PROTECT_AUTORELEASEPOOL
mprotect(this, SIZE, PROT_READ);
check();
#endif
}
//解锁
inline void unprotect() {
#if PROTECT_AUTORELEASEPOOL
check();
mprotect(this, SIZE, PROT_READ | PROT_WRITE);
#endif
}
//创建一个释放池,参数是上一个已经full满的释放池
AutoreleasePoolPage(AutoreleasePoolPage *newParent)
: magic(), next(begin()), thread(pthread_self()),
parent(newParent), child(nil),
depth(parent ? 1+parent->depth : 0),
hiwat(parent ? parent->hiwat : 0)
{
//如果parent有值,说明上一个释放池已经满了
if (parent) {
parent->check();
assert(!parent->child);
parent->unprotect();
parent->child = this;
parent->protect();
}
protect();
}
~AutoreleasePoolPage()
{
check();
unprotect();
assert(empty());
// Not recursive: we don't want to blow out the stack
// if a thread accumulates a stupendous amount of garbage
assert(!child);
}
//显示异常信息
void busted(bool die = true)
{
magic_t right;
(die ? _objc_fatal : _objc_inform)
("autorelease pool page %p corrupted\n"
" magic 0x%08x 0x%08x 0x%08x 0x%08x\n"
" should be 0x%08x 0x%08x 0x%08x 0x%08x\n"
" pthread %p\n"
" should be %p\n",
this,
magic.m[0], magic.m[1], magic.m[2], magic.m[3],
right.m[0], right.m[1], right.m[2], right.m[3],
this->thread, pthread_self());
}
//调用的方法check()用来检查当前的result是不是一个AutoreleasePoolPage,通过检查magic_t结构体中的某个成员是否为0xA1A1A1A1
void check(bool die = true)
{
if (!magic.check() || !pthread_equal(thread, pthread_self())) {
busted(die);
}
}
//调用的方法fastCheck()用来检查当前的result是不是一个AutoreleasePoolPage,通过检查magic_t结构体中的某个成员是否为0xA1A1A1A1
void fastcheck(bool die = true)
{
#if CHECK_AUTORELEASEPOOL
check(die);
#else
if (! magic.fastcheck()) {
busted(die);
}
#endif
}
//起始位置
id * begin() {
return (id *) ((uint8_t *)this+sizeof(*this));//sizeof(*this) = 56个字节
}
//棧顶位置
id * end() {
return (id *) ((uint8_t *)this+SIZE);
}
//如果next指针等于begin,说明没有要释放的对象
bool empty() {
return next == begin();
}
//如果next指针等于end,说明满了
bool full() {
return next == end();
}
//释放池中的数量是不是小于释放池容量的一半
bool lessThanHalfFull() {
return (next - begin() < (end() - begin()) / 2);
}
//把对象添加到自动释放池中,并返回对象所在的自动释放池中的位置的指针地址
id *add(id obj)
{
assert(!full());
unprotect();
id *ret = next; // faster than `return next-1` because of aliasing
*next++ = obj;
protect();
return ret;
}
//释放所有对象
void releaseAll()
{
releaseUntil(begin());
}
//释放到stop所指定的指针地址这个位置的所有对象
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
//每次都从hotpage中获取对象去释放,防止释放过多
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();
//这句话可以这样看*(--(page->next)),就是先获取游标的地址,减减(--)是获取最后一个添加的对象所在的地址,因为next总是指向下一个空地址,
//*是取地址所在的值
id obj = *--page->next;
//把next的位置设置为0xA3,标记为,以下都是要释放的对象
memset((void*)page->next, SCRIBBLE, sizeof(*page->next));
page->protect();
//如果对象不为nil,就释放对象
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
}
//销毁所有的释放池
void kill()
{
// Not recursive: we don't want to blow out the stack
// if a thread accumulates a stupendous amount of garbage
//通过循环,找到最新的一个释放池
AutoreleasePoolPage *page = this;
while (page->child) page = page->child;
AutoreleasePoolPage *deathptr;
do {
deathptr = page;
page = page->parent;
if (page) {
page->unprotect();
page->child = nil;
page->protect();
}
delete deathptr;//释放内存
} while (deathptr != this);
}
//清空释放池
static void tls_dealloc(void *p)
{
if (p == (void*)EMPTY_POOL_PLACEHOLDER) {
// No objects or pool pages to clean up here.
return;
}
// reinstate TLS value while we work
setHotPage((AutoreleasePoolPage *)p);
if (AutoreleasePoolPage *page = coldPage()) {
if (!page->empty()) pop(page->begin()); // pop all of the pools
if (DebugMissingPools || DebugPoolAllocation) {
// pop() killed the pages already
} else {
page->kill(); // free all of the pages
}
}
// clear TLS value so TLS destruction doesn't loop
setHotPage(nil);
}
//根据哨兵对象的指针地址获取当前页的释放池的首地址,一般传入的参数是哨兵对象
static AutoreleasePoolPage *pageForPointer(const void *p)
{
return pageForPointer((uintptr_t)p);
}
//根据哨兵对象的指针地址获取当前页的释放池的首地址,一般传入的参数是哨兵对象
static AutoreleasePoolPage *pageForPointer(uintptr_t p)
{
AutoreleasePoolPage *result;
uintptr_t offset = p % SIZE;
assert(offset >= sizeof(AutoreleasePoolPage));
result = (AutoreleasePoolPage *)(p - offset);
result->fastcheck();
return result;
}
static inline bool haveEmptyPoolPlaceholder()
{
id *tls = (id *)tls_get_direct(key);
return (tls == EMPTY_POOL_PLACEHOLDER);
}
static inline id* setEmptyPoolPlaceholder()
{
assert(tls_get_direct(key) == nil);
tls_set_direct(key, (void *)EMPTY_POOL_PLACEHOLDER);
return EMPTY_POOL_PLACEHOLDER;
}
//hotPage可以理解为当前正在使用的AutoreleasePoolPage
static inline AutoreleasePoolPage *hotPage()
{
AutoreleasePoolPage *result = (AutoreleasePoolPage *)
tls_get_direct(key);
if ((id *)result == EMPTY_POOL_PLACEHOLDER) return nil;
if (result) result->fastcheck();
return result;
}
//hotPage可以理解为当前正在使用的AutoreleasePoolPage
static inline void setHotPage(AutoreleasePoolPage *page)
{
if (page) page->fastcheck();
tls_set_direct(key, (void *)page);
}
//获取第一个释放池
static inline AutoreleasePoolPage *coldPage()
{
AutoreleasePoolPage *result = hotPage();
if (result) {
while (result->parent) {
result = result->parent;
result->fastcheck();
}
}
return result;
}
//快速添加对象到释放池方法
static inline id *autoreleaseFast(id obj)
{
AutoreleasePoolPage *page = hotPage();
if (page && !page->full()) {
//有hotPage并且当前page不满,调用page->add(obj)方法将对象添加至AutoreleasePoolPage的栈中
return page->add(obj);
} else if (page) {
//有hotPage并且当前page已满,调用autoreleaseFullPage初始化一个新的页,调用page->add(obj)方法将对象添加至AutoreleasePoolPage的栈中
return autoreleaseFullPage(obj, page);
} else {
//无hotPage,调用autoreleaseNoPage创建一个hotPage,调用page->add(obj)方法将对象添加至AutoreleasePoolPage的栈中,最后的都会调用page->add(obj)将对象添加到自动释放池中。
return autoreleaseNoPage(obj);
}
}
//当前的释放池满的时候,会调用这个方法,创建新的释放池,并添加对象
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 {
if (page->child) page = page->child;
else page = new AutoreleasePoolPage(page);
} while (page->full());
setHotPage(page);
return page->add(obj);
}
无hotPage,调用autoreleaseNoPage创建一个hotPage,调用page->add(obj)方法将对象添加至AutoreleasePoolPage的栈中,最后的都会调用page->add(obj)将对象添加到自动释放池中。
static __attribute__((noinline))
id *autoreleaseNoPage(id obj)
{
// "No page" could mean no pool has been pushed
// or an empty placeholder pool has been pushed and has no contents yet
assert(!hotPage());
bool pushExtraBoundary = false;
//判断是否需要插入哨兵
if (haveEmptyPoolPlaceholder()) {
// We are pushing a second pool over the empty placeholder pool
// or pushing the first object into the empty placeholder pool.
// Before doing that, push a pool boundary on behalf of the pool
// that is currently represented by the empty placeholder.
pushExtraBoundary = true;
}
else if (obj != POOL_BOUNDARY && DebugMissingPools) {
//报出错误
// We are pushing an object with no pool in place,
// and no-pool debugging was requested by environment.
// 我们正在推送一个没有池的对象,
// 并且环境请求了无池调试。
_objc_inform("MISSING POOLS: (%p) Object %p of class %s "
"autoreleased with no pool in place - "
"just leaking - break on "
"objc_autoreleaseNoPool() to debug",
pthread_self(), (void*)obj, object_getClassName(obj));
objc_autoreleaseNoPool(obj);
return nil;
}
else if (obj == POOL_BOUNDARY && !DebugPoolAllocation) {
//报出错误
// 我们正在推送一个没有池子的池子,
// 并且没有请求 alloc-per-pool 调试。
// 安装并返回空池占位符。
// We are pushing a pool with no pool in place,
// and alloc-per-pool debugging was not requested.
// Install and return the empty pool placeholder.
//如果obj是个nil对象,就创建一个空释放池
return setEmptyPoolPlaceholder();
}
// We are pushing an object or a non-placeholder'd pool.
// Install the first page.
//创建一个空释放池
AutoreleasePoolPage *page = new AutoreleasePoolPage(nil);
//设置为当前释放池
setHotPage(page);
// Push a boundary on behalf of the previously-placeholder'd pool.
if (pushExtraBoundary) {
// 是否需要添加哨兵
page->add(POOL_BOUNDARY);
}
// Push the requested object or pool.
//添加释放对象
return page->add(obj);
}
static __attribute__((noinline))
id *autoreleaseNewPage(id obj)
{
AutoreleasePoolPage *page = hotPage();
if (page) return autoreleaseFullPage(obj, page);
else return autoreleaseNoPage(obj);
}
public:
//把对象添加到自动释放池中
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;
}
//当我是用@autoreleasepool{},内部会调用这个静态方法去实现一个释放池对象,
static inline void *push()
{
id *dest;
if (DebugPoolAllocation) {
// Each autorelease pool starts on a new pool page.
dest = autoreleaseNewPage(POOL_BOUNDARY);
} else {
//dest这个就创建释放池成功后返回的哨兵对象,
//后面调用pop方法释放的时候,会传入这个dest对象
//这里是快速创建一个释放池,并添加一个哨兵对象
dest = autoreleaseFast(POOL_BOUNDARY);
}
assert(dest == EMPTY_POOL_PLACEHOLDER || *dest == POOL_BOUNDARY);
return dest;//把哨兵对象返回
}
//释放出现异常,调用这个
static void badPop(void *token)
{
// Error. For bincompat purposes this is not
// fatal in executables built with old SDKs.
if (DebugPoolAllocation || sdkIsAtLeast(10_12, 10_0, 10_0, 3_0, 2_0)) {
// OBJC_DEBUG_POOL_ALLOCATION or new SDK. Bad pop is fatal.
_objc_fatal
("Invalid or prematurely-freed autorelease pool %p.", token);
}
// Old SDK. Bad pop is warned once.
static bool complained = false;
if (!complained) {
complained = true;
_objc_inform_now_and_on_crash
("Invalid or prematurely-freed autorelease pool %p. "
"Set a breakpoint on objc_autoreleasePoolInvalid to debug. "
"Proceeding anyway because the app is old "
"(SDK version " SDK_FORMAT "). Memory errors are likely.",
token, FORMAT_SDK(sdkVersion()));
}
objc_autoreleasePoolInvalid(token);
}
//释放对象方法,参数是push方法返回的哨兵对象
static inline void pop(void *token)
{
AutoreleasePoolPage *page;
id *stop;
//这里是如果传入的token不是哨兵
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不是哨兵对象
if (*stop != POOL_BOUNDARY) {
//如果stop等于这个释放页开始位置,并且释放页没有上一页,也就是这个释放页是第一页,不做处理
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
// 如果释放池页是空的直接清空,且上一个释放池页不是空,把上一个释放池页设置成hotpage当前页面
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();
}
}
}
static void init()
{
int r __unused = pthread_key_init_np(AutoreleasePoolPage::key,
AutoreleasePoolPage::tls_dealloc);
assert(r == 0);
}
void print()
{
_objc_inform("[%p] ................ PAGE %s %s %s", this,
full() ? "(full)" : "",
this == hotPage() ? "(hot)" : "",
this == coldPage() ? "(cold)" : "");
check(false);
for (id *p = begin(); p < next; p++) {
if (*p == POOL_BOUNDARY) {
_objc_inform("[%p] ################ POOL %p", p, p);
} else {
_objc_inform("[%p] %#16lx %s",
p, (unsigned long)*p, object_getClassName(*p));
}
}
}
static void printAll()
{
_objc_inform("##############");
_objc_inform("AUTORELEASE POOLS for thread %p", pthread_self());
AutoreleasePoolPage *page;
ptrdiff_t objects = 0;
for (page = coldPage(); page; page = page->child) {
objects += page->next - page->begin();
}
_objc_inform("%llu releases pending.", (unsigned long long)objects);
if (haveEmptyPoolPlaceholder()) {
_objc_inform("[%p] ................ PAGE (placeholder)",
EMPTY_POOL_PLACEHOLDER);
_objc_inform("[%p] ################ POOL (placeholder)",
EMPTY_POOL_PLACEHOLDER);
}
else {
for (page = coldPage(); page; page = page->child) {
page->print();
}
}
_objc_inform("##############");
}
static void printHiwat()
{
// Check and propagate high water mark
// Ignore high water marks under 256 to suppress noise.
AutoreleasePoolPage *p = hotPage();
uint32_t mark = p->depth*COUNT + (uint32_t)(p->next - p->begin());
if (mark > p->hiwat && mark > 256) {
for( ; p; p = p->parent) {
p->unprotect();
p->hiwat = mark;
p->protect();
}
_objc_inform("POOL HIGHWATER: new high water mark of %u "
"pending releases for thread %p:",
mark, pthread_self());
void *stack[128];
int count = backtrace(stack, sizeof(stack)/sizeof(stack[0]));
char **sym = backtrace_symbols(stack, count);
for (int i = 0; i < count; i++) {
_objc_inform("POOL HIGHWATER: %s", sym[i]);
}
free(sym);
}
}
#undef POOL_BOUNDARY
};