散列表方式
在源码当中是通过SideTables()结构来实现的。
在SideTables()结构下,挂有很多SideTable这样的数据结构。这些数据结构,在不同的架构层面有不同的个数。比如说在非嵌入式系统当中,一共有64个SideTable这样的表。
Q:SideTables是什么?
A:是一个哈希表,可以通过一个对象指针来具体找到它对应的引用计数表/弱引用表,在哪一张具体的SideTable当中。
image.png
SideTable 包含了以下3个元素
ac66dccaba6aa8f60553dc9459cb3a0.jpg
spinlock_t: 多线程和资源竞争相关的。
Q:为什么不是一个SideTable, 而是多个SideTable?
A:假如只有一个SideTable,相当于我们在内存当中分配的所有对象的引用计数,弱引用存储都放在一张大表中,如果我们要操作某个对象的引用计数值进行修改,比如+1,-1操作,由于所有对象可能是在不同线程当中去分配,创建的,包括调用release, retain等方法,也可能是在不同的线程当中操作的,那这个时候在对这张表进行操作的时候,要进行加锁处理,才能保证对于数据的访问安全,那在这个过程中就存在效率问题,比如用户的内存空间一共有4GB,我们可能分配出成千上百个内存对象,如果每个对象在对它进行内存引用计数改变的时候,都操作这张表,那很显然就会有效率问题,如果现在有一个对象在操作这张表,那么下一个对象就要等前一个对象操作完了,把锁释放之后,它才能操作这张表,很明显,成百上千个对象都操作这张表的话,自然会存在效率问题。
b3903786f35a61e75b5c1346090fa60.jpg
分离锁(系统为了解决效率问题,引入的技术方案):
概念:
我们可以把内存对象所对应的引用计数表分拆成多个部分,比如说,我们把它分拆成8个,需要对8个这样的表分别加锁,比如说某一个对象在这张表(A)里,另一个对象在另一张表(B)里,那么当A和B同时进行引用计数操作的时候,可以并发操作,但是按照一张表的情况,A和B就必须顺序操作,很明显,引入分离锁的技术方案,可以提高访问效率。
0b94863e53531adb83c3ad0a974f566.jpg
怎样实现快速分流?(实际上就是通过对象指针,如何快速定位它属于哪张SideTable):
SideTables本质是一张Hash表。
7faaf2f822a772df5c87fd1ab5ab992.jpg
如上图:
左侧是一个对象,这个对象指针做为一个 key, 经过Hash函数运算,然后会计算出一个值,来决定出这个对象对应的SideTable是哪张,或者说在数组的位置是哪个,或者说索引是哪个?
Hash查找:
例: 给定值是对象内存地址,目标值是数组下标索引。
下图解析:
*指针作为函数的参数,通过运算,就可以计算出一个数组下标索引值。
*表达式(通过内存地址和SideTables的数组个数进行取余运算,这样就可以计算出一个对象指针所对应的引用计数表或弱引用表在哪一张SideTable中)
9a946789c311afe2b0ed9b8603d93e2.jpg
Q: 为什么要通过Hash查找呢?
A: 通过Hash查找是为了提高查找效率,比如说存储的时候是通过Hash函数进行存储,比如说数组个数是8,假设内存地址是1,那取余就是1,我们就把对象A存储到数组对应的第一个位置,当我们访问这个对象的时候,也不需要根据数组进行遍历来比较指针值,而是通过这个函数来进行运算,比如说同一个对象,还是1,和8取余,还是1,那可以直接到第一个索引位置取出想要的内容,这个过程不涉及遍历操作,查找效率自然比较高!
内存地址的分布实际上是均匀分布,可以称Hash函数为均匀散列函数。
网友评论