内存布局
截屏2025-02-20 11.17.59.png
自动释放池实现结构:
截屏2025-02-20 11.19.22.png
是以栈为结点通过双向链表的形式组合而成。
是和线程一一对应的。
image.png
@autoreleasepool
clang编译器生成代码:
创建自动释放池
双向链表结构
存放哨兵对象(nil)
存放管理的对象指针
当释放池释放时候,将哨兵对象之间的所有创建的对象依次释放。
struct __AtAutoreleasePool {
__AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();}
~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);}
void * atautoreleasepoolobj;
};
自动添加autorelease
2、@autorelease 将创建一个新的AutoreleasePoolPage并且设置好哨兵对象
objc_autoreleasePoolPush(void)
{
return AutoreleasePoolPage::push();
}
static inline void *push()
{
ReturnAutoreleaseInfo info = getReturnAutoreleaseInfo();
moveTLSAutoreleaseToPool(info);
id *dest;
if (slowpath(DebugPoolAllocation)) {
// Each autorelease pool starts on a new pool page.
dest = autoreleaseNewPage(POOL_BOUNDARY);//# define POOL_BOUNDARY nil
} else {
dest = autoreleaseFast(POOL_BOUNDARY);
}
ASSERT(dest == (id *)EMPTY_POOL_PLACEHOLDER || *dest == POOL_BOUNDARY);
// dtrace probe
OBJC_RUNTIME_AUTORELEASE_POOL_PUSH(dest);
return dest;
}
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);
}
}
class AutoreleasePoolPage : private AutoreleasePoolPageData{}
class AutoreleasePoolPage;
struct AutoreleasePoolPageData
{
#if SUPPORT_AUTORELEASEPOOL_DEDUP_PTRS
struct AutoreleasePoolEntry {
uintptr_t ptr: 48;
uintptr_t count: 16;
static const uintptr_t maxCount = 65535; // 2^16 - 1
};
static_assert((AutoreleasePoolEntry){ .ptr = OBJC_VM_MAX_ADDRESS }.ptr == OBJC_VM_MAX_ADDRESS, "OBJC_VM_MAX_ADDRESS doesn't fit into AutoreleasePoolEntry::ptr!");
#endif
magic_t const magic;
__unsafe_unretained id *next;
objc_thread_t const thread;
AutoreleasePoolPage * const parent;
AutoreleasePoolPage *child;
uint32_t const depth;
uint32_t hiwat;
AutoreleasePoolPageData(__unsafe_unretained id* _next, objc_thread_t _thread, AutoreleasePoolPage* _parent, uint32_t _depth, uint32_t _hiwat)
: magic(), next(_next), thread(_thread),
parent(_parent), child(nil),
depth(_depth), hiwat(_hiwat)
{
}
};
2、释放:
1、创建的每个对象都是autorelease的,即会在AutoreleasePoolPage中添加这个对象
id
objc_autoreleaseReturnValue(id obj)
{
if (prepareOptimizedReturn(obj, false, ReturnAtPlus1)) return obj;
return objc_autorelease(obj);
}
__attribute__((aligned(16), flatten, noinline))
id
objc_autorelease(id obj)
{
if (_objc_isTaggedPointerOrNil(obj)) return obj;
return obj->autorelease();
}
ALWAYS_INLINE id
objc_object::autorelease()
{
ASSERT(!isTaggedPointer());
if (fastpath(!ISA()->hasCustomRR())) {
return rootAutorelease();
}
return ((id(*)(objc_object *, SEL))objc_msgSend)(this, @selector(autorelease));
}
// Replaced by ObjectAlloc
- (id)autorelease {
return _objc_rootAutorelease(self);
}
NEVER_INLINE id
_objc_rootAutorelease(id obj)
{
ASSERT(obj);
return obj->rootAutorelease();
}
ALWAYS_INLINE id
objc_object::rootAutorelease()
{
if (isTaggedPointer()) return (id)this;
bool nonpointerIsa = false;
#if ISA_HAS_INLINE_RC
nonpointerIsa = isa().nonpointer;
// When we can cheaply determine if the object is deallocating, avoid
// putting it in the pool. Refcounting doesn't work on a deallocating object
// so it's pointless to put it in the pool, and potentially dangerous.
if (nonpointerIsa && isa().isDeallocating()) return (id)this;
#endif
// If the class has custom dealloc initiation, we also want to avoid putting
// deallocating instances in the pool even if it's expensive to check. (UIView
// and UIViewController need this. rdar://97186669)
if (!nonpointerIsa && ISA()->hasCustomDeallocInitiation() && rootIsDeallocating())
return (id)this;
if (prepareOptimizedReturn((id)this, true, ReturnAtPlus1)) return (id)this;
if (slowpath(isClass())) return (id)this;
return rootAutorelease2();
}
__attribute__((noinline,used))
id
objc_object::rootAutorelease2()
{
ASSERT(!isTaggedPointer());
return AutoreleasePoolPage::autorelease((id)this);
}
static inline id autorelease(id obj)
{
ASSERT(!_objc_isTaggedPointerOrNil(obj));
id *dest __unused = autoreleaseFast(obj);
#if SUPPORT_AUTORELEASEPOOL_DEDUP_PTRS
ASSERT(!dest || dest == (id *)EMPTY_POOL_PLACEHOLDER || (id)((AutoreleasePoolEntry *)dest)->ptr == obj);
#else
ASSERT(!dest || dest == (id *)EMPTY_POOL_PLACEHOLDER || *dest == obj);
#endif
return obj;
}
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);
}
}
2、当__AtAutoreleasePool作用域结束,__AtAutoreleasePool结构体的析构方法调用,继而调用objc_autoreleasePoolPop(atautoreleasepoolobj);释放池中的所有对象。
static inline void
pop(void *token)
{
// dtrace probe
OBJC_RUNTIME_AUTORELEASE_POOL_POP(token);
// We may have an object in the ReturnAutorelease TLS when the pool is
// otherwise empty. Release that first before checking for an empty pool
// so we don't return prematurely. Loop in case the release placed a new
// object in the TLS.
while (releaseReturnAutoreleaseTLS())
;
AutoreleasePoolPage *page;
id *stop;
if (token == (void*)EMPTY_POOL_PLACEHOLDER) {
// Popping the top-level placeholder pool.
page = hotPage();
if (!page) {
// Pool was never used. Clear the placeholder.
return setHotPage(nil);
}
// Pool was used. Pop its contents normally.
// Pool pages remain allocated for re-use as usual.
page = coldPage();
token = page->begin();
} else {
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);
}
}
if (slowpath(PrintPoolHiwat || DebugPoolAllocation || DebugMissingPools)) {
return popPageDebug(token, page, stop);
}
return popPage<false>(token, page, stop);
}
template<bool allowDebug>
static void
popPage(void *token, AutoreleasePoolPage *page, id *stop)
{
if (allowDebug && PrintPoolHiwat) printHiwat();
page->releaseUntil(stop);
// memory: delete empty children
if (allowDebug && DebugPoolAllocation && page->empty()) {
// special case: delete everything during page-per-pool debugging
AutoreleasePoolPage *parent = page->parent;
page->kill();
setHotPage(parent);
} else if (allowDebug && 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
do {
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();
#if SUPPORT_AUTORELEASEPOOL_DEDUP_PTRS
AutoreleasePoolEntry* entry = (AutoreleasePoolEntry*) --page->next;
// create an obj with the zeroed out top byte and release that
id obj = (id)entry->ptr;
int count = (int)entry->count; // grab these before memset
#else
id obj = *--page->next;
#endif
memset((void*)page->next, SCRIBBLE, sizeof(*page->next));
page->protect();
if (obj != POOL_BOUNDARY) {
#if SUPPORT_AUTORELEASEPOOL_DEDUP_PTRS
// release count+1 times since it is count of the additional
// autoreleases beyond the first one
for (int i = 0; i < count + 1; i++) {
objc_release(obj);
}
#else
objc_release(obj);
#endif
}
}
// Stale return autorelease info is conceptually autoreleased. If
// there is any, release the object in the info. If stale info is
// present, we have to loop in case it autoreleased more objects
// when it was released.
} while (releaseReturnAutoreleaseTLS());
setHotPage(this);
#if DEBUG
// we expect any children to be completely empty
for (AutoreleasePoolPage *page = child; page; page = page->child) {
ASSERT(page->empty());
}
#endif
}
OBJC_EXPORT void
objc_release(id _Nullable obj)
__asm__("_objc_release")
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0, 2.0);
结论:
自动释放池时是一个以栈为结点通过双向链表的形式组合而成的结构,且是和线程一一对应的。
- 1、当我们编写@autoreleasepool代码时,编译器将添加一个创建释放池对象的代码,运行时,则创建自动释放池AutoreleasePoolPage
- 2、如果嵌套@autoreleasepool,则在池page中添加哨兵,并将新的释放池放在next位置
- 3、创建对象时,则会调用对象的autorelease,将对象添加到自动释放池
- 4、当作用域结束和手动调用时释放池时,调用objc_autoreleasePoolPop,将对当前page中所有的对象依次调用objc_release。
测试代码
{
@autoreleasepool {
{
CFPerson *obj2 = [[CFPerson alloc]initWithname:@"111"];
}
NSLog(@"-----------1-------");
{
__autoreleasing id obj3=[[CFPerson alloc]initWithname:@"222"];
CFPerson *obj4 = [[CFPerson alloc]initWithname:@"333"];
}
NSLog(@"-----------2-------");
}
}
NSLog(@"main结束");
clang编译后:
// @implementation GGObject
// @end
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
{
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
{
CFPerson *obj2 = ((CFPerson *(*)(id, SEL, NSString *))(void *)objc_msgSend)((id)((CFPerson *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("CFPerson"), sel_registerName("alloc")), sel_registerName("initWithname:"), (NSString *)&__NSConstantStringImpl__var_folders_mj_xqk5tcl13nb42n09nqty6_vc0000gn_T_main_ef2fa6_mi_0);
}
NSLog((NSString *)&__NSConstantStringImpl__var_folders_mj_xqk5tcl13nb42n09nqty6_vc0000gn_T_main_ef2fa6_mi_1);
{
__attribute__((objc_ownership(autoreleasing))) id obj3=((CFPerson *(*)(id, SEL, NSString *))(void *)objc_msgSend)((id)((CFPerson *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("CFPerson"), sel_registerName("alloc")), sel_registerName("initWithname:"), (NSString *)&__NSConstantStringImpl__var_folders_mj_xqk5tcl13nb42n09nqty6_vc0000gn_T_main_ef2fa6_mi_2);
CFPerson *obj4 = ((CFPerson *(*)(id, SEL, NSString *))(void *)objc_msgSend)((id)((CFPerson *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("CFPerson"), sel_registerName("alloc")), sel_registerName("initWithname:"), (NSString *)&__NSConstantStringImpl__var_folders_mj_xqk5tcl13nb42n09nqty6_vc0000gn_T_main_ef2fa6_mi_3);
}
NSLog((NSString *)&__NSConstantStringImpl__var_folders_mj_xqk5tcl13nb42n09nqty6_vc0000gn_T_main_ef2fa6_mi_4);
}
}
NSLog((NSString *)&__NSConstantStringImpl__var_folders_mj_xqk5tcl13nb42n09nqty6_vc0000gn_T_main_ef2fa6_mi_5);
}
return 0;
}
运行结果:
person dealloc:111
-----------1-------
person dealloc:333
-----------2-------
person dealloc:222
main结束
Program ended with exit code: 0
__autoreleasing 的对象在 @autoreleasepool 块结束时会被释放,而不是当前作用域结束时。
普通的局部对象(如 obj2 和 obj4)在当前作用域结束时会立刻释放。







网友评论