iOS 内存管理
1. 内存布局
20200324142841981.png- stack:方法调用
- heap:通过alloc等分配的对象
- bss:未初始化的全局变量
- data:已初始化的全局变量
- text:程序代码段
2. 内存管理方案
通过不同的场景入手:
截屏2020-09-12 下午7.36.39.png
ISA
不同的架构对应不同的isa结构:
# if __arm64__
# define ISA_MASK 0x0000000ffffffff8ULL
# define ISA_MAGIC_MASK 0x000003f000000001ULL
# define ISA_MAGIC_VALUE 0x000001a000000001ULL
# define ISA_BITFIELD \
uintptr_t nonpointer : 1; \
uintptr_t has_assoc : 1; \
uintptr_t has_cxx_dtor : 1; \
uintptr_t shiftcls : 33; /*MACH_VM_MAX_ADDRESS 0x1000000000*/ \
uintptr_t magic : 6; \
uintptr_t weakly_referenced : 1; \
uintptr_t deallocating : 1; \
uintptr_t has_sidetable_rc : 1; \
uintptr_t extra_rc : 19
# define RC_ONE (1ULL<<45)
# define RC_HALF (1ULL<<18)
# elif __x86_64__
# define ISA_MASK 0x00007ffffffffff8ULL
# define ISA_MAGIC_MASK 0x001f800000000001ULL
# define ISA_MAGIC_VALUE 0x001d800000000001ULL
# define ISA_BITFIELD \
uintptr_t nonpointer : 1; \
uintptr_t has_assoc : 1; \
uintptr_t has_cxx_dtor : 1; \
uintptr_t shiftcls : 44; /*MACH_VM_MAX_ADDRESS 0x7fffffe00000*/ \
uintptr_t magic : 6; \
uintptr_t weakly_referenced : 1; \
uintptr_t deallocating : 1; \
uintptr_t has_sidetable_rc : 1; \
uintptr_t extra_rc : 8
# define RC_ONE (1ULL<<56)
# define RC_HALF (1ULL<<7)
nonpointer
: 1; 0表示是isa指针,1表示是isa结构体
has_assoc
: 1; 表示对象是否含有关联引用(associatedObject)
has_cxx_dtor
: 1; 表示当前对象是否有C++的析构函数(destructor),如果没有,释放时会快速的释放内存。
shiftcls
: 33; 当前对象类的指针
magic
: 6; magic的值调试器会用到,调试器根据magci的值判断当前对象已经初始过了,还是尚未初始化的空间。
weakly_referenced
: 1; 表示对象是否含有弱引用对象
deallocating
: 1; 表示对象是否正在释放
has_sidetable_rc
: 1; 表示对象的引用计数是否太大,如果太大,则需要用其他的数据结构来存
extra_rc
: 19; 对象的引用计数大于1,则会将引用计数的个数存到extra_rc里面。比如对象的引用计数为5,则extra_rc的值为4
extra_rc和has_sidetable_c 可以一起理解。extra_rc用于存放引用计数的个数,extra_rc占8位,也就是最大表示255,当对象的引用计数个数超过257时,has_sidetable_rc的值应该为1。
散列表
SideTables.png SideTable.pngSideTable的定义很清晰,有三个成员:
- spinlock_t slock : 自旋锁,用于上锁/解锁 SideTable。
- RefcountMap refcnts :以DisguisedPtr<objc_object>为key的hash表,用来存储OC对象的引用计数(仅在未开启isa优化 或 在isa优化情况下isa_t的引用计数溢出时才会用到)。
- weak_table_t weak_table : 存储对象弱引用指针的hash表。是OC weak功能实现的核心数据结构。
SideTables.SideTable.weak_table.weak_entry_t 优秀博文
20180920175152461.pngstruct SideTable {
spinlock_t slock; // 自旋锁,防止多线程访问冲突
RefcountMap refcnts; // 对象引用计数map
weak_table_t weak_table; // 对象弱引用map
SideTable() {
memset(&weak_table, 0, sizeof(weak_table));
}
~SideTable() {
_objc_fatal("Do not delete SideTable.");
}
void lock() { slock.lock(); }
void unlock() { slock.unlock(); }
void forceReset() { slock.forceReset(); }
// Address-ordered lock discipline for a pair of side tables.
template<HaveOld, HaveNew>
static void lockTwo(SideTable *lock1, SideTable *lock2);
template<HaveOld, HaveNew>
static void unlockTwo(SideTable *lock1, SideTable *lock2);
};
spinlock_t 自旋锁
Spinlock_t 是忙等
的锁。
适用于轻量级访问。(如:refCount +1 / -1)
RefcountMap 引用计数表
截屏2020-09-12 下午9.03.59.pngsize_t : (unsign long) 引用计数的值
size_t.pngweak_table_t 对象弱引用表
weak_table_t.jpg3. 引用计数原理
MRC
MRC.pngARC
ARC.png-
alloc
-
retain
retain的实现.png -
release
- retainCount
- dealloc
- (void)dealloc {
_objc_rootDealloc(self);
}
_objc_rootDealloc(id obj)
{
ASSERT(obj);
obj->rootDealloc();
}
objc_object::rootDealloc()
{
if (isTaggedPointer()) return;
object_dispose((id)this);
}
object_dispose.png
object_dispose(id obj)
{
if (!obj) return nil;
objc_destructInstance(obj);
free(obj);
return nil;
}
objc_destructInstance.png
void *objc_destructInstance(id obj)
{
if (obj) {
Class isa = obj->getIsa();
if (isa->hasCxxDtor()) {
object_cxxDestruct(obj);
}
if (isa->instancesHaveAssociatedObjects()) {
_object_remove_assocations(obj);
}
objc_clear_deallocating(obj);
}
return obj;
}
clearDeallocating.png
objc_object::clearDeallocating()
{
if (slowpath(!isa.nonpointer)) {
// Slow path for raw pointer isa.
sidetable_clearDeallocating();
}
else if (slowpath(isa.weakly_referenced || isa.has_sidetable_rc)) {
// Slow path for non-pointer isa with weak refs and/or side table data.
clearDeallocating_slow();
}
assert(!sidetable_present());
}
objc_object::sidetable_clearDeallocating()
{
SideTable& table = SideTables()[this];
// clear any weak table items
// clear extra retain count and deallocating bit
// (fixme warn or abort if extra retain count == 0 ?)
table.lock();
RefcountMap::iterator it = table.refcnts.find(this);
if (it != table.refcnts.end()) {
if (it->second & SIDE_TABLE_WEAKLY_REFERENCED) {
weak_clear_no_lock(&table.weak_table, (id)this);
}
table.refcnts.erase(it);
}
table.unlock();
}
清理顺序:
1、C++ 析构函数
2、移除关联对象
3、weak reference 置nil
4、RefcountMap[this] (引用计数值)擦除
weak 弱引用
弱引用编译.png 添加weak变量.png 清除weak变量.png👆请移步源码查看详细过程 👆
4. 自动释放池
IMG_4086.JPG黑幕背后的Autorelease • sunnyxx的技术博客
总结:
- 是以栈为结点通过双向链表的形式组合而成。
- 是和线程一一对应的。
- 在当次Runloop将要结束的时候调用AutoReleasePoolPage::pop()。
- 多层嵌套就是多次插入哨兵对象(0 / nil)。
- 在for循环中alloc图片数据等内存消耗较大的场景手动插入AutoReleasePool。
5. 循环引用
- Delegate
- NSTimer
NSTimer定时器进阶——详细介绍,循环引用分析与解决