iOS 内存管理

内存管理总结.png

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.png

SideTable的定义很清晰,有三个成员:

  • spinlock_t slock : 自旋锁,用于上锁/解锁 SideTable。
  • RefcountMap refcnts :以DisguisedPtr为key的hash表,用来存储OC对象的引用计数(仅在未开启isa优化 或 在isa优化情况下isa_t的引用计数溢出时才会用到)。
  • weak_table_t weak_table : 存储对象弱引用指针的hash表。是OC weak功能实现的核心数据结构。
截屏2020-09-12 下午8.15.18.png
截屏2020-09-12 下午8.15.44.png

SideTables.SideTable.weak_table.weak_entry_t 优秀博文

20180920175152461.png
struct 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
    static void lockTwo(SideTable *lock1, SideTable *lock2);
    template
    static void unlockTwo(SideTable *lock1, SideTable *lock2);
};

spinlock_t 自旋锁

Spinlock_t 是忙等的锁。
适用于轻量级访问。(如:refCount +1 / -1)

RefcountMap 引用计数表

截屏2020-09-12 下午9.03.59.png

size_t : (unsign long) 引用计数的值

size_t.png

weak_table_t 对象弱引用表

weak_table_t.jpg

3. 引用计数原理

MRC

MRC.png

ARC

ARC.png

  • alloc

  • retain


    retain的实现.png
  • release

release的实现.png
  • retainCount
retainCount实现.png
  • dealloc
截屏2020-09-12 下午9.46.09.png
- (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定时器进阶——详细介绍,循环引用分析与解决

你可能感兴趣的:(iOS 内存管理)