一刊殉、官網(wǎng)關(guān)于自動釋放池的說明截取
NSAutoreleasePool
- NSAutoreleasePool 類被用來支持自動引用計數(shù)內(nèi)存管理系統(tǒng)。一個自動釋放池存儲的對象當(dāng)自己被銷毀的時會向其中的對象發(fā)送 release 消息手负。
Overview
在一個自動引用計數(shù)的環(huán)境中(并不是垃圾回收機(jī)制),一個包含了多個對象的 NSAutoreleasePool 對象能夠接收 autorelease 消息并且當(dāng)銷毀它的時候會對每一個池子中的對象發(fā)送 release 消息。因此,發(fā)送 autorelease 而不是 release 消息延長了對象的生命周期直到 pool 被清空的時候(當(dāng)對象被保留的時候會更久)净神。一個對象能夠被放到同一個池子中許多次,在這種情況下每放一次都會收到一個 release 消息耙厚。
在引用計數(shù)的環(huán)境中强挫,Cocoa 期望有一個自動釋放池能夠保持有效岔霸。如果一個池子沒有用了薛躬,需要自動釋放的對象沒有被釋放從而會造成內(nèi)存泄漏。在這種情況下呆细,你的程序?qū)箦e型宝。
Application Kit 在事件循環(huán)開始的時候在主線程創(chuàng)建了一個自動釋放池,并且在結(jié)束的時候去清空它絮爷,從而釋放所有進(jìn)程事件中生成的自動釋放的對象趴酣。如果使用了 Application Kit ,就沒必要再去創(chuàng)建自己的自動釋放池坑夯。然而岖寞,如果你的應(yīng)用在事件循環(huán)中創(chuàng)建了很多臨時的自動釋放的對象,創(chuàng)建臨時的自動釋放池會將有助于削減你內(nèi)存峰值的占用柜蜈。
你創(chuàng)建了一個 NSAutoreleasePool 對象根據(jù) alloc 和 init 消息并且用 drain 來清空它仗谆。因為你不能夠保留一個自動釋放池(或者自動釋放它。)淑履, 清空一個池子最終會影響它的銷毀隶垮。你應(yīng)該在創(chuàng)建它的同一個上下文來進(jìn)行銷毀的工作。
每一個線程(包括主線程)包含一個它自己的自動釋放池對象的堆棧秘噪。作為一個新的被創(chuàng)建的池子狸吞,它們被添加到堆棧的頂部。當(dāng)池子被釋放的時候指煎,它們從棧中被移除蹋偏。自動釋放的對象被放在當(dāng)前線程的自動釋放池的頂部。當(dāng)一個線程終止的時候至壤,它自動清空與它關(guān)聯(lián)的所有的自動釋放池暖侨。
線程
- 如果你想要 Cocoa 在 ApplicationKit 的主線程之外調(diào)用,比如你創(chuàng)建了一個 Foundation 的 應(yīng)用或者你創(chuàng)建了一個線程崇渗,你需要創(chuàng)建你自己的自動釋放池字逗。
- 如果應(yīng)用或線程是長久保存的并且潛在的生成了很多自動釋放的對象京郑,這時應(yīng)該定期的清空并且創(chuàng)建自動釋放池(就像 Application Kit 在主線程中做的那樣);否則葫掉,對象的積累會增加內(nèi)存的占用些举。如果,獨(dú)立的線程并沒有使用 Cocoa 的調(diào)用俭厚,你沒有必要去創(chuàng)建一個自動釋放池户魏。
注意
如果使用了 POSIX 線程 APIS 而不是 NSThread 對象來創(chuàng)建線程,你不能使用 Cocoa挪挤,包括 NSautoreleasePool叼丑,除非 Cocoa 是在多線程模式下,Cocoa 進(jìn)入了多線程模式只有在首次創(chuàng)建 NSThread 對象的時候扛门,為了在第二個 POSIX 線程中使用 Cocoa 鸠信,你的應(yīng)用必須首先至少創(chuàng)建了一個獨(dú)立的 NSThread 對象,這個對象可以立即退出论寨。你可以通過 NSThread 類方法 isMultiTheraded 來測試 Cocoa 是否在多線程模式下星立。
垃圾回收
- 在垃圾回收的環(huán)境下,是不需要自動釋放池的葬凳。你可能寫了一個 framework 绰垂,它被設(shè)計用來在垃圾回收環(huán)境和引用計數(shù)環(huán)境下都可工作。在這種情況下火焰,你可以使用自動釋放池去提示回收器回收可能是合適的劲装。在垃圾回收環(huán)境中,如果必要會發(fā)送一個 drain 消息到池子中去觸發(fā)垃圾回收機(jī)制昌简;然而占业,release,是一個空操作江场。在引用計數(shù)的環(huán)境中纺酸,drain 和 release 的效果是一樣的。通常址否,你應(yīng)該使用 drain 而不是 release餐蔬。
二、什么時候是用 @autoreleasepool
- 寫基于命令行的的程序時佑附,就是沒有UI框架樊诺,如 AppKit 等 Cocoa 框架時。
- 當(dāng)我們的應(yīng)用有需要創(chuàng)建大量的臨時變量的時候音同,可以是用 @autoreleasepool 來減少內(nèi)存峰值词爬。
- 為什么?
自動釋放池可以延長對象的聲明周期权均,如果一個事件周期很長顿膨,比如有一個很長的循環(huán)邏輯锅锨,那么一個臨時變量可能很長時間都不會被釋放,一直在內(nèi)存中保留恋沃,那么內(nèi)存的峰值就會一直增加必搞,但是其實(shí)這個臨時變量是我們不再需要的。這個時候就通過創(chuàng)建新的自動釋放池來縮短臨時變量的生命周期來降低內(nèi)存的峰值囊咏。 - 這是一個說明這個問題的很好的例子恕洲。
- YYKit 中的使用
for (int i = 0; i < count; i++) {
@autoreleasepool {
id imageSrc = _images[i];
NSDictionary *frameProperty = NULL;
if (_type == YYImageTypeGIF && count > 1) {
frameProperty = @{(NSString *)kCGImagePropertyGIFDictionary : @{(NSString *) kCGImagePropertyGIFDelayTime:_durations[i]}};
} else {
frameProperty = @{(id)kCGImageDestinationLossyCompressionQuality : @(_quality)};
}
if ([imageSrc isKindOfClass:[UIImage class]]) {
UIImage *image = imageSrc;
if (image.imageOrientation != UIImageOrientationUp && image.CGImage) {
CGBitmapInfo info = CGImageGetBitmapInfo(image.CGImage) | CGImageGetAlphaInfo(image.CGImage);
CGImageRef rotated = YYCGImageCreateCopyWithOrientation(image.CGImage, image.imageOrientation, info);
if (rotated) {
image = [UIImage imageWithCGImage:rotated];
CFRelease(rotated);
}
}
if (image.CGImage) CGImageDestinationAddImage(destination, ((UIImage *)imageSrc).CGImage, (CFDictionaryRef)frameProperty);
} else if ([imageSrc isKindOfClass:[NSURL class]]) {
CGImageSourceRef source = CGImageSourceCreateWithURL((CFURLRef)imageSrc, NULL);
if (source) {
CGImageDestinationAddImageFromSource(destination, source, i, (CFDictionaryRef)frameProperty);
CFRelease(source);
}
} else if ([imageSrc isKindOfClass:[NSData class]]) {
CGImageSourceRef source = CGImageSourceCreateWithData((CFDataRef)imageSrc, NULL);
if (source) {
CGImageDestinationAddImageFromSource(destination, source, i, (CFDictionaryRef)frameProperty);
CFRelease(source);
}
}
}
}
三、release 和 drain的區(qū)別
- 當(dāng)我們向自動釋放池 pool 發(fā)送 release 消息梅割,將會向池中臨時對象發(fā)送一條 release 消息霜第,并且自身也會被銷毀。
- 向它發(fā)送drain消息時户辞,將會向池中臨時對象發(fā)送一條release消息泌类。
- 官方解釋
- release:
釋放并且出棧接收者。(ARC) - drain:
- 在引用計數(shù)環(huán)境中咆课,會釋放并且出棧接受者末誓。
- 在垃圾回收環(huán)境中扯俱,會觸發(fā)垃圾回收機(jī)制如果上次分配的內(nèi)存集合大于當(dāng)前的閾值书蚪。
四、Autoreleasepool 底層實(shí)現(xiàn)
- 首先我們?nèi)タ梢圆榭匆幌卵刚ぃ琧lang 轉(zhuǎn)成 c++ 的 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;
};
可以發(fā)現(xiàn)objc_autoreleasePoolPush() 和 objc_autoreleasePoolPop() 這兩個方法殊校。
-
再看一下runtime 中 Autoreleasepool 的結(jié)構(gòu)司光,通過閱讀源碼可以看出 Autoreleasepool 是一個由 AutoreleasepoolPage 雙向鏈表的結(jié)構(gòu)臀防,其中 child 指向它的子 page,parent 指向它的父 page磺陡。
雙向鏈表結(jié)構(gòu)圖 - 并且每個 AutoreleasepoolPage 對象的大小都是 4096 個字節(jié)让簿。
#define PAGE_MAX_SIZE PAGE_SIZE
#define PAGE_SIZE I386_PGBYTES
#define I386_PGBYTES 4096 /* bytes per 80386 page */
- AutoreleasepoolPage 通過壓棧的方式來存儲每個需要自動釋放的對象敬察。
//入棧方法
static inline void *push()
{
id *dest;
if (DebugPoolAllocation) {
// Each autorelease pool starts on a new pool page.
//在 Debug 情況下每一個自動釋放池 都以一個新的 poolPage 開始
dest = autoreleaseNewPage(POOL_BOUNDARY);
} else {
//正常情況下,調(diào)用 push 方法會先插入一個 POOL_BOUNDARY 標(biāo)志位
dest = autoreleaseFast(POOL_BOUNDARY);
}
assert(dest == EMPTY_POOL_PLACEHOLDER || *dest == POOL_BOUNDARY);
return dest;
}
- 然后我們來看看 runtime 中的源碼
/***********************************************************************
自動釋放池的實(shí)現(xiàn):
一個線程的自動釋放池是一個指針堆棧
每一個指針或者指向被釋放的對象尔当,或者是自動釋放池的 POOL_BOUNDARY莲祸,POOL_BOUNDARY 是自動釋放池的邊界。
一個池子的 token 是指向池子 POOL_BOUNDARY 的指針椭迎。當(dāng)池子被出棧的時候锐帜,每一個高于標(biāo)準(zhǔn)的對象都會被釋放掉。
堆棧被分成一個頁面的雙向鏈表畜号。頁面按照需要添加或者刪除缴阎。
本地線程存放著指向當(dāng)前頁的指針,在這里存放著新創(chuàng)建的自動釋放的對象简软。
**********************************************************************/
// Set this to 1 to mprotect() autorelease pool contents
//將這個設(shè)為 1 可以通過mprotect修改映射存儲區(qū)的權(quán)限來更改自動釋放池的內(nèi)容
#define PROTECT_AUTORELEASEPOOL 0
#define CHECK_AUTORELEASEPOOL (DEBUG)
BREAKPOINT_FUNCTION(void objc_autoreleaseNoPool(id obj));
BREAKPOINT_FUNCTION(void objc_autoreleasePoolInvalid(const void *token));
namespace {
//對AutoreleasePoolPage進(jìn)行完整性校驗
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
};
//自動釋放頁
class AutoreleasePoolPage
{
//EMPTY_POOL_PLACEHOLDER 被存放在本地線程存儲中當(dāng)一個池入棧并且沒有存放任何對象的時候蛮拔。這樣在棧頂入棧出棧并且沒有使用它們的時候會節(jié)省內(nèi)存述暂。
# 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
//對象數(shù)量
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
//創(chuàng)建
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);
}
//設(shè)置當(dāng)前內(nèi)存可讀
inline void protect() {
#if PROTECT_AUTORELEASEPOOL
mprotect(this, SIZE, PROT_READ);
check();
#endif
}
//設(shè)置當(dāng)前內(nèi)存可讀可寫
inline void unprotect() {
#if PROTECT_AUTORELEASEPOOL
check();
mprotect(this, SIZE, PROT_READ | PROT_WRITE);
#endif
}
//初始化
AutoreleasePoolPage(AutoreleasePoolPage *newParent)
: magic(), next(begin()), thread(pthread_self()),
parent(newParent), child(nil),
depth(parent ? 1+parent->depth : 0),
hiwat(parent ? parent->hiwat : 0)
{
if (parent) {
parent->check();
assert(!parent->child);
parent->unprotect();
parent->child = this;
parent->protect();
}
protect();
}
//析構(gòu)
~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());
}
//校驗
void check(bool die = true)
{
if (!magic.check() || !pthread_equal(thread, pthread_self())) {
busted(die);
}
}
//快速校驗
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));
}
//頁的結(jié)束位置
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);
}
//添加對象
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
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;
//將頁索引內(nèi)容置為 SCRIBBLE 表示已經(jīng)被釋放
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
}
//殺死
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);
}
//獲取 AutoreleasePoolPage
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);
}
//設(shè)置空池占位符
static inline id* setEmptyPoolPlaceholder()
{
assert(tls_get_direct(key) == nil);
tls_set_direct(key, (void *)EMPTY_POOL_PLACEHOLDER);
return EMPTY_POOL_PLACEHOLDER;
}
//獲取當(dāng)前頁
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;
}
//設(shè)置當(dāng)前頁
static inline void setHotPage(AutoreleasePoolPage *page)
{
if (page) page->fastcheck();
tls_set_direct(key, (void *)page);
}
//獲取 coldPage
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()) {
return page->add(obj);
} else if (page) {
return autoreleaseFullPage(obj, page);
} else {
return autoreleaseNoPage(obj);
}
}
//添加自動釋放對象,當(dāng)頁滿的時候調(diào)用這個方法
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);
}
//添加自動釋放對象建炫,當(dāng)沒頁的時候使用這個方法
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) {
// 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.
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;
}
//入棧
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;
}
//兼容老的 SDK 出棧方法
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)) {
// 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);
}
//出棧
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);
}
}
//打印 hiwat
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();
}
}
}
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("##############");
}
//打印 hiwat
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
};