iOS内存(方案3) 散列表

290 阅读2分钟

在runtime内存空间中,SideTables是一个hash数组,里面存储了SideTable。SideTables的hash键值就是一个对象obj的address。
因此可以说,一个obj,对应了一个SideTable。但是一个SideTable,会对应多个obj。因为SideTable的数量有限,所以会有很多obj共用同一个SideTable。

<  
1.  复杂的数据结构,包括了引用计数表和弱引用表 通过 SideTables()结构来实现的,SideTables()结构下,有很多 SideTable 的数据结构。 2. 而 sideTable 当中包含了自旋锁,引用计数表,弱引用表。 SideTables()实际上是一个哈希表,通过对象的地址来计算该对象的引用计数在哪个 sideTable 中。 >

我们来看一下SideTable的结构

struct SideTable {
    spinlock_t slock;      // 自旋锁
    RefcountMap refcnts;    //引用计数的Map表 key-value
    weak_table_t weak_table;  //弱引用表

来抛出一个问题,为什么不直接用一张SideTable,而是用SideTables去管理多个SideTable? SideTable里有一个自旋锁,如果把所有的类都放在同一个SideTable,有任何一个类有改动都会对整个table做操作,并且在操作一个类的同时,操作别的类会被锁住等待,这样会导致操作效率和查询效率都很低。而有多个SideTable的话,操作的都是单个Table,并不会影响其他的table,这就是分离锁。

继续SideTables,来看一下散列表的数据结构(数组+链表),举个例子,我们需要把小于100的放到第1个Table,大于900的放到第6个Table:

![image.png](https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2019/7/9/16bd46d0dbb85954~tplv-t2oaga2asx-image.image)

如何从sideTables里找到特定的sideTable呢,这就用到了散列函数。查看源码,runtime是通过这么一个函数来获取到相应的sideTable:

table = &SideTables()[obj];

点进去:
static StripedMap<SideTable>& SideTables() {
    return *reinterpret_cast<StripedMap<SideTable>*>(SideTableBuf);
}
复制代码

如果看不懂没关系,来看  **StripedMap  **的定义:

template<typename T>
class StripedMap {
#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
    enum { StripeCount = 8 }; // iPhone时这个值为8
#else
    enum { StripeCount = 64 }; //否则为64
#endif

    struct PaddedT {
        T value alignas(CacheLineSize);
    };

    PaddedT array[StripeCount];

    static unsigned int indexForPointer(const void *p) {
        //这里是做类型转换
        uintptr_t addr = reinterpret_cast<uintptr_t>(p);

        //这就是哈希算法了
        return ((addr >> 4) ^ (addr >> 9)) % StripeCount;
    }
public:
    T& operator[] (const void *p) { 
        //返回sideTable
        return array[indexForPointer(p)].value; 
    }
复制代码

可以看到,在对StripeCount取余后,所得到的值根据机器不同,会在0-7或者0-63之间,这就是通过哈希函数来获取到了sideTable的下标,然后再根据value取到所需的sideTable。

我们来验证一下: 编译objc源码,打上断点看一下具体调用:

![image.png](https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2019/7/9/16bd46d0dba7cdff~tplv-t2oaga2asx-image.image)
![image.png](https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2019/7/9/16bd46d0dc6c0119~tplv-t2oaga2asx-image.image)

执行table = &SideTables()[obj];之后,执行到了array[indexForPointer(p)].value;,然后进行哈希算法获取到下标,再返回所需的sideTable

![image.png](https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2019/7/9/16bd46d0dc8bc44c~tplv-t2oaga2asx-image.image)

作者:聪莞
链接:juejin.cn/post/684490…
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。