美文网首页
iOS 内存管理 (2):引用计数

iOS 内存管理 (2):引用计数

作者: 知礼账本 | 来源:发表于2020-03-17 11:33 被阅读0次

引用计数:

引用计数是计算机编程语言中的一种内存管理技术,是指将资源(可以是对象、内存或磁盘空间等等)的被引用次数保存起来,当被引用次数变为零时就将其释放的过程。使用引用计数技术可以实现自动资源管理的目的。同时引用计数还可以指使用引用计数技术回收未使用资源的垃圾回收算法。
每个OC对象都有自己的引用计数器,是一个整数表示对象被引用的次数,即现在有多少东西在使用这个对象。对象刚被创建时,默认计数器值为1,当计数器的值变为0时,则对象销毁。 在每个OC对象内部,都专门有4个字节的存储空间来存储引用计数器。判断对象要不要回收的唯一依据就是计数器是否为0,若不为0则存在。

存储

Tagged Pointer

  从64bit开始,iOS引入了Tagged Pointer技术,用于优化NSNumber、NSDate、NSString等小对象的存储。

  在没有使用Tagged Pointer之前, NSNumber等对象需要动态分配内存、维护引用计数等,NSNumber指针存储的是堆中NSNumber对象的地址值。

  使用Tagged Pointer之后,NSNumber指针里面存储的数据变成了:Tag + Data,也就是将数据直接存储在了指针中。

  当指针不够存储数据时,才会使用动态分配内存的方式来存储数据。

  objc_msgSend能识别Tagged Pointer,比如NSNumber的intValue方法,直接从指针提取数据,节省了以前的调用开销。

(深入理解 Tagged Pointer)https://www.infoq.cn/article/deep-understanding-of-tagged-pointer/

SideTables

为了管理所有对象的引用计数和weak指针,苹果创建了一个全局的64个元素长度SideTables,虽然名字后面有个"s"不过他其实是一个全局的Hash表,里面的内容装的都是SideTable结构体而已。它使用对象的内存地址当它的key。管理引用计数和weak指针。

SideTable

truct SideTable {
    // 保证原子操作的自旋锁
    spinlock_t slock;
    // 引用计数的 hash 表
    RefcountMap refcnts;
    // weak 引用全局 hash 表
    weak_table_t weak_table;
};
spinlock_t  slock 自旋锁:

自旋锁比较适用于锁使用者保持锁时间比较短的情况。正是由于自旋锁使用者一般保持锁时间非常短,因此选择自旋而不是睡眠是非常必要的,自旋锁的效率远高于互斥锁。信号量和读写信号量适合于保持时间较长的情况,它们会导致调用者睡眠,因此只能在进程上下文使用,而自旋锁适合于保持时间非常短的情况,它可以在任何上下文使用。

它的作用是在操作引用技术的时候对SideTable加锁,避免数据错误。

RefcountMap  refcnts 引用计数器:

对象具体的引用计数数量是记录在这里的。
RefcountMap其实是个C++的Map
内存中对象的数量实在是太庞大了我们通过第一个Hash表只是过滤了第一次,然后我们还需要再通过这个Map才能精确的定位到我们要找的对象的引用计数器。

weak_table_t weak_table 维护weak指针的结构体
struct weak_table_t {
    weak_entry_t *weak_entries; //连续地址空间的头指针, 数组
    size_t    num_entries; //数组中已占用位置的个数
    uintptr_t mask; //数组下标最大值(即数组大小 -1)
    uintptr_t max_hash_displacement; //最大哈希偏移值
};

weak_table 是一个哈希表的结构, 根据 weak 指针指向的对象的地址计算哈希值, 哈希值相同的对象按照下标 +1 的形式向后查找可用位置, 是典型的闭散列算法. 最大哈希偏移值即是所有对象中计算出的哈希值和实际插入位置的最大偏移量, 在查找时可以作为循环的上限.

weak_entry_t
struct weak_entry_t {
    DisguisedPtr<objc_object> referent; //对象地址
    union {  //这里又是一个联合体, 苹果设计的数据结构的确很棒
        struct {
            // 因为这里要存储的又是一个 weak 指针数组, 所以苹果继续选择采用哈希算法
            weak_referrer_t *referrers; //指向 referent 对象的 weak 指针数组
            uintptr_t        out_of_line_ness : 2; //这里标记是否超过内联边界, 下面会提到
            uintptr_t        num_refs : PTR_MINUS_2; //数组中已占用的大小
            uintptr_t        mask; //数组下标最大值(数组大小 - 1)
            uintptr_t        max_hash_displacement; //最大哈希偏移值
        };
        struct {
            //这是一个取名叫内联引用的数组
            weak_referrer_t  inline_referrers[WEAK_INLINE_COUNT]; //宏定义的值是 4
        };
    };
    ...
}

(weak_table_t 详细说明引用)https://www.jianshu.com/p/7eb4d291d6d6

相关文章

网友评论

      本文标题:iOS 内存管理 (2):引用计数

      本文链接:https://www.haomeiwen.com/subject/ekezehtx.html