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