通過以下方法查看iOS
的引用計(jì)數(shù)管理:
- alloc
- retain
- release
- retainCount
- dealloc
源碼版本:objc4-723
alloc
+ (id)alloc {
return _objc_rootAlloc(self);
}
id
_objc_rootAlloc(Class cls)
{
return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);
}
static ALWAYS_INLINE id
callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
{
if (slowpath(checkNil && !cls)) return nil;
#if __OBJC2__
if (fastpath(!cls->ISA()->hasCustomAWZ())) {
// No alloc/allocWithZone implementation. Go straight to the allocator.
// fixme store hasCustomAWZ in the non-meta class and
// add it to canAllocFast's summary
if (fastpath(cls->canAllocFast())) {
// No ctors, raw isa, etc. Go straight to the metal.
bool dtor = cls->hasCxxDtor();
id obj = (id)calloc(1, cls->bits.fastInstanceSize());
if (slowpath(!obj)) return callBadAllocHandler(cls);
obj->initInstanceIsa(cls, dtor);
return obj;
}
else {
// Has ctor or raw isa or something. Use the slower path.
id obj = class_createInstance(cls, 0);
if (slowpath(!obj)) return callBadAllocHandler(cls);
return obj;
}
}
#endif
// No shortcuts available.
if (allocWithZone) return [cls allocWithZone:nil];
return [cls alloc];
}
當(dāng)我們調(diào)用類的alloc
方法時(shí)倦微,調(diào)用的方法棧如上所示,我們來看最后一部分代碼即可。
在這一部分代碼中,有兩個(gè)宏定義:
#define fastpath(x) (__builtin_expect(bool(x), 1))
#define slowpath(x) (__builtin_expect(bool(x), 0))
__builtin_expect
的主要作用就是幫助編譯器判斷條件跳轉(zhuǎn)的預(yù)期值,避免因執(zhí)行jmp
跳轉(zhuǎn)指令造成時(shí)間浪費(fèi)。
編譯器優(yōu)化時(shí)荐虐,根據(jù)條件跳轉(zhuǎn)的預(yù)期值,按正確地順序生成匯編代碼丸凭,把“很有可能發(fā)生”的條件分支放在順序執(zhí)行指令段福扬,而不是
jmp
指令段(jmp
指令會(huì)打亂CPU的指令執(zhí)行順序腕铸,大大影響CPU指令執(zhí)行效率)。
在本例中铛碑,if else
句型編譯后, 一個(gè)分支的匯編代碼緊隨前面的代碼狠裹,而另一個(gè)分支的匯編代碼需要使用jmp
指令才能訪問到,很明顯通過jmp
訪問需要更多的時(shí)間, 在復(fù)雜的程序中汽烦,有很多的if else
句型,又或者是一個(gè)有if else
句型的庫(kù)函數(shù)涛菠,每秒鐘被調(diào)用幾萬次,通常程序員在分支預(yù)測(cè)方面做得很糟糕撇吞,編譯器又不能精準(zhǔn)的預(yù)測(cè)每一個(gè)分支俗冻,這時(shí)jmp
產(chǎn)生的時(shí)間浪費(fèi)就會(huì)很大,函數(shù)__builtin_expert()
就是用來解決這個(gè)問題的梢夯。
所以言疗,這里的邏輯就是如果slowpath(checkNil && !cls)
為0
時(shí),該函數(shù)返回nil
颂砸,為1
時(shí)走接下里的邏輯。
在接下來的邏輯中死姚,fastpath(!cls->ISA()->hasCustomAWZ()
首先會(huì)判斷當(dāng)前class
或super class
是否實(shí)現(xiàn)了allocWithZone:
方法(AWZ
即AllocWithZone
)人乓,如果沒有實(shí)現(xiàn)這個(gè)方法,最終都會(huì)調(diào)用C
語(yǔ)言函數(shù)calloc
申請(qǐng)一塊內(nèi)存空間都毒。
如果實(shí)現(xiàn)了AWZ
方法色罚,就會(huì)執(zhí)行allocWithZone:
。
總結(jié)账劲,調(diào)用alloc
時(shí)戳护,經(jīng)過一系列方法調(diào)用,最終會(huì)在內(nèi)存中申請(qǐng)一塊內(nèi)存空間瀑焦,但此時(shí)并沒有設(shè)置引用計(jì)數(shù)腌且。
retain
- (id)retain {
return ((id)self)->rootRetain();
}
ALWAYS_INLINE id
objc_object::rootRetain()
{
return rootRetain(false, false);
}
id
objc_object::sidetable_retain()
{
#if SUPPORT_NONPOINTER_ISA
assert(!isa.nonpointer);
#endif
SideTable& table = SideTables()[this];
table.lock();
size_t& refcntStorage = table.refcnts[this];
if (! (refcntStorage & SIDE_TABLE_RC_PINNED)) {
refcntStorage += SIDE_TABLE_RC_ONE;
}
table.unlock();
return (id)this;
}
在上面代碼塊中,因?yàn)槠奘∪チ?code>rootRetain()的具體實(shí)現(xiàn)代碼榛瓮,在rootRetain()
中實(shí)際上調(diào)用了sidetable_retain()
方法铺董,也就是最后那部分代碼。
在了解最后一部分代碼前禀晓,需要了解一些概念上的知識(shí)精续,有助于更好的理解系統(tǒng)對(duì)對(duì)象引用計(jì)數(shù)管理的實(shí)現(xiàn)原理。
為了管理所有對(duì)象的引用計(jì)數(shù)以及對(duì)象的所有弱引用指針粹懒,系統(tǒng)創(chuàng)建了一個(gè)全局的SideTables
重付,SideTables
里存的是一個(gè)個(gè)的SideTable
,每個(gè)SideTable
實(shí)際上都是一個(gè)結(jié)構(gòu)體凫乖,如下:
struct SideTable {
spinlock_t slock;
RefcountMap refcnts;
weak_table_t weak_table;
//省略部分代碼
SideTables
本質(zhì)上是一個(gè)全局的hash
表确垫,key
值為對(duì)象的內(nèi)存地址愕把。
回到sidetable_retain()
方法,首先會(huì)根據(jù)當(dāng)前對(duì)象的指針到SideTables
中獲取SideTable
:
SideTable& table = SideTables()[this];
然后在SideTable
的結(jié)構(gòu)體當(dāng)中獲取當(dāng)前對(duì)象的引用計(jì)數(shù)值:
size_t &refcntStorage = table.refcnts[this];
需要注意的是森爽,這兩次查找都是hash查找恨豁。
然后再對(duì)應(yīng)用計(jì)數(shù)值進(jìn)行+1
操作:
refcntStorage += SIDE_TABLE_RC_ONE;
以上,就是進(jìn)行retain
操作時(shí)爬迟,系統(tǒng)對(duì)對(duì)象引用計(jì)數(shù)操作的具體實(shí)現(xiàn)橘蜜。
release
- (oneway void)release {
((id)self)->rootRelease();
}
ALWAYS_INLINE bool
objc_object::rootRelease()
{
return rootRelease(true, false);
}
uintptr_t
objc_object::sidetable_release(bool performDealloc)
{
#if SUPPORT_NONPOINTER_ISA
assert(!isa.nonpointer);
#endif
SideTable& table = SideTables()[this];
bool do_dealloc = false;
table.lock();
RefcountMap::iterator it = table.refcnts.find(this);
if (it == table.refcnts.end()) {
do_dealloc = true;
table.refcnts[this] = SIDE_TABLE_DEALLOCATING;
} else if (it->second < SIDE_TABLE_DEALLOCATING) {
// SIDE_TABLE_WEAKLY_REFERENCED may be set. Don't change it.
do_dealloc = true;
it->second |= SIDE_TABLE_DEALLOCATING;
} else if (! (it->second & SIDE_TABLE_RC_PINNED)) {
it->second -= SIDE_TABLE_RC_ONE;
}
table.unlock();
if (do_dealloc && performDealloc) {
((void(*)(objc_object *, SEL))objc_msgSend)(this, SEL_dealloc);
}
return do_dealloc;
}
篇幅所限,省略了rootRelease()
的代碼實(shí)現(xiàn)付呕,在rootRelease()
中會(huì)調(diào)用sidetable_release()
计福,也就是最后一部分代碼,我們可以將最后一部分代碼進(jìn)行簡(jiǎn)化徽职,簡(jiǎn)化后的代碼如下所示:
SideTable &table = SideTables()[this];
RefcountMap ::iterator it = table.refcnts.find(this);
it->second -= SIDE_TABLE_RC_ONE;
首先根據(jù)對(duì)象的內(nèi)存地址到hash
表中查找SideTable
:
SideTable &table = SideTables()[this];
然后根據(jù)查找到的SideTable
和對(duì)象的內(nèi)存地址獲取其引用計(jì)數(shù)并-1
:
RefcountMap ::iterator it = table.refcnts.find(this);
it->second -= SIDE_TABLE_RC_ONE;
另外象颖,當(dāng)對(duì)象需要被回收時(shí),系統(tǒng)還會(huì)利用objc_msgSend
向?qū)ο蟀l(fā)送dealloc
消息姆钉。
以上说订,就是進(jìn)行release
操作時(shí),系統(tǒng)對(duì)對(duì)象引用計(jì)數(shù)操作的具體實(shí)現(xiàn)潮瓶。
retainCount
- (NSUInteger)retainCount {
return ((id)self)->rootRetainCount();
}
inline uintptr_t
objc_object::rootRetainCount()
{
if (isTaggedPointer()) return (uintptr_t)this;
sidetable_lock();
isa_t bits = LoadExclusive(&isa.bits);
ClearExclusive(&isa.bits);
if (bits.nonpointer) {
uintptr_t rc = 1 + bits.extra_rc;
if (bits.has_sidetable_rc) {
rc += sidetable_getExtraRC_nolock();
}
sidetable_unlock();
return rc;
}
sidetable_unlock();
return sidetable_retainCount();
}
uintptr_t
objc_object::sidetable_retainCount()
{
SideTable& table = SideTables()[this];
size_t refcnt_result = 1;
table.lock();
RefcountMap::iterator it = table.refcnts.find(this);
if (it != table.refcnts.end()) {
// this is valid for SIDE_TABLE_RC_PINNED too
refcnt_result += it->second >> SIDE_TABLE_RC_SHIFT;
}
table.unlock();
return refcnt_result;
}
這里把retainCount
方法調(diào)用棧的全部代碼都貼出來了陶冷,主要來看最后一部分代碼,也就是sidetable_retainCount()
方法的代碼毯辅。
我們也可以帶著一個(gè)問題來看這段代碼埂伦,問題是:
為什么剛創(chuàng)建完的對(duì)象,它的retainCount
是0
思恐?
在這個(gè)方法的實(shí)現(xiàn)中沾谜,首先會(huì)根據(jù)對(duì)象的內(nèi)存地址獲取到SideTable
:
SideTable& table = SideTables()[this];
然后緊接著聲明了一個(gè)局部變量refcnt_result
,值為1
:
size_t refcnt_result = 1;
所以胀莹,剛alloc
出來的對(duì)象基跑,在引用計(jì)數(shù)表(SideTables
)中是沒有這個(gè)對(duì)象的key-value
映射的,所以table.refcnts.find(this)
讀出來的值是0
嗜逻,所以如果這里是0
涩僻,就將局部變量refcnt_result
的值返回,也即是1
栈顷。
如果table.refcnts.find(this)
讀出來的值不是0
逆日,則會(huì)把查找的結(jié)果做向右偏移的操作然后+1
,返回給調(diào)用方:
refcnt_result += it->second >> SIDE_TABLE_RC_SHIFT;
以上萄凤,就是進(jìn)行retainCount
操作時(shí)室抽,系統(tǒng)對(duì)獲取對(duì)象引用計(jì)數(shù)的具體實(shí)現(xiàn)原理。
dealloc
- (void)dealloc {
_objc_rootDealloc(self);
}
void
_objc_rootDealloc(id obj)
{
assert(obj);
obj->rootDealloc();
}
inline void
objc_object::rootDealloc()
{
if (isTaggedPointer()) return; // fixme necessary?
if (fastpath(isa.nonpointer &&
!isa.weakly_referenced &&
!isa.has_assoc &&
!isa.has_cxx_dtor &&
!isa.has_sidetable_rc))
{
assert(!sidetable_present());
free(this);
}
else {
object_dispose((id)this);
}
}
以上是dealloc
的方法調(diào)用棧靡努,從最后一個(gè)方法開始看坪圾,在這個(gè)方法中有一個(gè)if
判斷條件比較多:
- 首先判斷有沒有使用非指針型
isa
(nonpointer_isa
) - 判斷是否有
weak
指針指向它 - 判斷是否有關(guān)聯(lián)對(duì)象
- 判斷內(nèi)部實(shí)現(xiàn)是否涉及到有
C++
相關(guān)的內(nèi)容 - 當(dāng)前對(duì)象的引用計(jì)數(shù)是否是通過
SideTable
來維護(hù)的
只有當(dāng)當(dāng)前對(duì)象既不是nonpointer_isa
晓折,也沒有弱引用,還沒有涉及到C++
兽泄、關(guān)聯(lián)對(duì)象漓概、沒有使用SideTable
存儲(chǔ)相關(guān)引用計(jì)數(shù),才會(huì)調(diào)用C
函數(shù)free()
進(jìn)行釋放病梢,否則胃珍,就調(diào)用object_dispose()
對(duì)象清除函數(shù)進(jìn)行清除。
再來看object_dispose()
函數(shù)的實(shí)現(xiàn):
id
object_dispose(id obj)
{
if (!obj) return nil;
objc_destructInstance(obj);
free(obj);
return nil;
}
void *objc_destructInstance(id obj)
{
if (obj) {
// Read all of the flags at once for performance.
bool cxx = obj->hasCxxDtor();
bool assoc = obj->hasAssociatedObjects();
// This order is important.
if (cxx) object_cxxDestruct(obj);
if (assoc) _object_remove_assocations(obj);
obj->clearDeallocating();
}
return obj;
}
先來看objc_destructInstance
函數(shù)蜓陌,如果obj
對(duì)象存在的話觅彰,會(huì)先判斷當(dāng)前對(duì)象是否有用到涉及到C++
相關(guān)的東西,如果有钮热,稍后會(huì)調(diào)用object_cxxDestruct
函數(shù)進(jìn)行銷毀操作填抬。
還會(huì)判斷當(dāng)前對(duì)象是否擁有關(guān)聯(lián)對(duì)象,如果有的話稍后就會(huì)調(diào)用_object_remove_assocations
函數(shù)進(jìn)行清除操作隧期,所以我們并不需要在dealloc
方法中顯式的清除關(guān)聯(lián)對(duì)象飒责。
最后會(huì)調(diào)用obj
的clearDeallocating
函數(shù),函數(shù)實(shí)現(xiàn)如下:
inline void
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());
}
其會(huì)調(diào)用兩個(gè)函數(shù)厌秒,分別是:
sidetable_clearDeallocating()
clearDeallocating_slow()
(1)sidetable_clearDeallocating()
函數(shù)實(shí)現(xiàn)如下:
void
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();
}
其中還會(huì)調(diào)用weak_clear_no_lock()
函數(shù)读拆,實(shí)現(xiàn)如下:
void
weak_clear_no_lock(weak_table_t *weak_table, id referent_id)
{
objc_object *referent = (objc_object *)referent_id;
weak_entry_t *entry = weak_entry_for_referent(weak_table, referent);
if (entry == nil) {
/// XXX shouldn't happen, but does with mismatched CF/objc
//printf("XXX no entry for clear deallocating %p\n", referent);
return;
}
// zero out references
weak_referrer_t *referrers;
size_t count;
if (entry->out_of_line()) {
referrers = entry->referrers;
count = TABLE_SIZE(entry);
}
else {
referrers = entry->inline_referrers;
count = WEAK_INLINE_COUNT;
}
for (size_t i = 0; i < count; ++i) {
objc_object **referrer = referrers[i];
if (referrer) {
if (*referrer == referent) {
*referrer = nil;
}
else if (*referrer) {
_objc_inform("__weak variable at %p holds %p instead of %p. "
"This is probably incorrect use of "
"objc_storeWeak() and objc_loadWeak(). "
"Break on objc_weak_error to debug.\n",
referrer, (void*)*referrer, (void*)referent);
objc_weak_error();
}
}
}
weak_entry_remove(weak_table, entry);
}
weak_clear_no_lock
函數(shù)有兩個(gè)參數(shù),weak_table
及referent_id
鸵闪。
weak_table
就是弱引用表,記錄了指向該對(duì)象的弱引用指針暑诸。
referent_id
就是當(dāng)前正在被銷毀的對(duì)象蚌讼。
這里會(huì)把referent_id
強(qiáng)轉(zhuǎn)成objc_object
類型的結(jié)構(gòu)體指針,記做referent
个榕,根據(jù)referent
到weak_table
中獲取entry
篡石,進(jìn)而通過weak_entry_t
獲取弱引用數(shù)組referrers
,繞后遍歷這個(gè)referrers
數(shù)組西采,將指向該對(duì)象的弱引用指針全部置為nil
凰萨,最后從weak_table
中把這個(gè)entry
刪除。
所以械馆,當(dāng)一個(gè)對(duì)象有一個(gè)弱引用指針指向它的時(shí)候胖眷,當(dāng)這個(gè)對(duì)象被廢棄之后它的弱引用指針會(huì)被自動(dòng)置為nil
。
(2)clearDeallocating_slow()
函數(shù)實(shí)現(xiàn)如下:
NEVER_INLINE void
objc_object::clearDeallocating_slow()
{
assert(isa.nonpointer && (isa.weakly_referenced || isa.has_sidetable_rc));
SideTable& table = SideTables()[this];
table.lock();
if (isa.weakly_referenced) {
weak_clear_no_lock(&table.weak_table, (id)this);
}
if (isa.has_sidetable_rc) {
table.refcnts.erase(this);
}
table.unlock();
}
table.refcnts.erase(this);
函數(shù)的主要作用是從引用計(jì)數(shù)表中擦除該對(duì)象的引用計(jì)數(shù)霹崎。
總結(jié)一下珊搀,當(dāng)對(duì)象被釋放時(shí),也就是在系統(tǒng)的dealloc
方法實(shí)現(xiàn)中尾菇,系統(tǒng)會(huì)銷毀該對(duì)象所使用的C++
相關(guān)的內(nèi)容境析,還會(huì)刪除該對(duì)象的關(guān)聯(lián)對(duì)象囚枪,并且將指向該對(duì)象的弱引用指針全部置為nil
,這些操作完成后劳淆,會(huì)從全局的引用計(jì)數(shù)表中擦除該對(duì)象的引用計(jì)數(shù)链沼。
以上,就是dealloc
的實(shí)現(xiàn)原理沛鸵。
內(nèi)存管理方案
分類
- TaggedPointer
針對(duì)于小對(duì)象括勺,如NSNumber、NSDate等谒臼。 - NONPOINTER_ISA
對(duì)于64位架構(gòu)下的應(yīng)用程序采用這種內(nèi)存管理方案朝刊,在64位架構(gòu)下,ISA指針占64個(gè)bit位蜈缤,實(shí)際上有32位或40位就夠用了拾氓,剩余的是浪費(fèi)的,Apple為了提高內(nèi)存的利用率底哥,在剩余的bit位當(dāng)中存儲(chǔ)一些關(guān)于內(nèi)存管理的數(shù)據(jù)內(nèi)容咙鞍,也被稱為非指針型的isa。 - 散列表
包括引用計(jì)數(shù)表和弱引用表趾徽。
NONPOINTER_ISA
- indexed
0:代表它是一個(gè)純的isa指針续滋,里面的內(nèi)容直接代表當(dāng)前對(duì)象的類對(duì)象的地址。
1:代表isa指針里面存儲(chǔ)的不僅是類對(duì)象的地址孵奶,還有一些內(nèi)存管理的數(shù)據(jù)疲酌。 - has_assoc
當(dāng)前對(duì)象是否有關(guān)聯(lián)對(duì)象
0:沒有
1:有 - has_cxx_dtor
當(dāng)前對(duì)象是否有使用到C++語(yǔ)言方面的內(nèi)容 - 后續(xù)33位
當(dāng)前對(duì)象的類對(duì)象的指針地址 - magic
略,不影響分析 - weakly_referenced
當(dāng)前對(duì)象是否有弱引用指針 - deallocating
表示當(dāng)前對(duì)象是否在進(jìn)行dealloc操作 - has_sidetable_rc
指當(dāng)前isa指針當(dāng)中如果所存儲(chǔ)的引用計(jì)數(shù)已經(jīng)達(dá)到了上限的話了袁,需要外掛一個(gè)sidetable這樣的數(shù)據(jù)結(jié)構(gòu)去存儲(chǔ)相關(guān)的引用計(jì)數(shù)內(nèi)容朗恳。 - extra_rc
額外的引用計(jì)數(shù),當(dāng)引用計(jì)數(shù)在一個(gè)很小的值得范圍內(nèi)就會(huì)存到isa指針當(dāng)中载绿,而不是由單獨(dú)的引用計(jì)數(shù)表去存儲(chǔ)引用計(jì)數(shù)粥诫,
散列表方式
struct SideTable {
spinlock_t slock;
RefcountMap refcnts;
weak_table_t weak_table;
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);
};
在非嵌入式系統(tǒng)中,SideTables
管理著64個(gè)SideTable
崭庸,每個(gè)SideTable
有3個(gè)元素:
-
spinlock_t
自旋鎖 -
RefcountMap
引用計(jì)數(shù)表 -
weak_table_t
弱引用表
為什么不是一個(gè)SideTable
怀浆,而是由多個(gè)SideTable
組成SideTables
?
如果使用一個(gè)SideTable
管理系統(tǒng)所有對(duì)象的引用計(jì)數(shù)怕享,那么當(dāng)我們?cè)诙嗑€程中對(duì)其中一個(gè)對(duì)象進(jìn)行retain
执赡、release
等操作時(shí)就會(huì)對(duì)這個(gè)SideTable
加鎖,以此保證數(shù)據(jù)訪問的安全熬粗,那么在這個(gè)過程中實(shí)際上就產(chǎn)生了效率的問題搀玖,下一個(gè)對(duì)象要想進(jìn)行操作(比如修改引用計(jì)數(shù)),就必須要等鎖釋放只會(huì)才能操作這張表驻呐,如果成千上萬個(gè)對(duì)象同操作一張表灌诅,那么效率是極其低下的芳来。
系統(tǒng)為了解決效率低下的問題,系統(tǒng)引入了分離鎖的技術(shù)方案猜拾,我們可以把對(duì)象所對(duì)應(yīng)的引用計(jì)數(shù)表可以分拆成多個(gè)部分即舌,比如可以分拆成8個(gè),那么就會(huì)對(duì)8個(gè)表分別加鎖挎袜,假如A對(duì)象屬于表1顽聂,B對(duì)象屬于表2,那么此時(shí)就可以進(jìn)行并行操作盯仪,提高了訪問效率紊搪。
如何實(shí)現(xiàn)快速分流?
指通過一個(gè)對(duì)象的指針全景,如何快速定位到它屬于哪張SideTable
耀石?
SideTable
的本質(zhì)是一張Hash
表,這張Hash
表中可能有64張具體的SideTable
用以存儲(chǔ)不同對(duì)象的引用計(jì)數(shù)表和弱引用表爸黄。
Hash
表的key
是對(duì)象指針經(jīng)過hash
函數(shù)計(jì)算出一個(gè)值滞伟,這個(gè)值決定對(duì)象所屬的SideTable
是哪一個(gè),或者說在數(shù)組中(SideTables
)的位置是第幾個(gè)炕贵。
Hash查找
eg:給定值是對(duì)象的內(nèi)存地址梆奈,目標(biāo)值是數(shù)組(SideTables
)下標(biāo)索引。
f(ptr) = (uintptr_t)ptr % array.count
其他涉及到的技術(shù)
-
spinlock_t slock;
自旋鎖 -
RefcountMap refcnts;
引用計(jì)數(shù)表 -
weak_table_t weak_table;
弱引用表
spinlock_t
- 自旋鎖是一種忙等的鎖称开,指當(dāng)前鎖已經(jīng)被其他線程獲取亩钟,當(dāng)前線程會(huì)不斷探測(cè)這個(gè)鎖是否有被釋放,如果被釋放掉自己會(huì)第一時(shí)間獲取這個(gè)鎖鳖轰。
- 信號(hào)量是如果獲取不到這個(gè)鎖的時(shí)候径荔,會(huì)把自己線程進(jìn)行阻塞休眠,等到其他線程釋放這個(gè)鎖的時(shí)候喚醒當(dāng)前線程脆霎。
- 適用于輕量訪問,因?yàn)閷?duì)于引用計(jì)數(shù)的操作只是簡(jiǎn)單的
+1 -1
操作狈惫,這種操作都是輕量的操作睛蛛,所以在輕量的訪問場(chǎng)景下,都可以使用自旋鎖胧谈。
RefcountMap
實(shí)際上是一個(gè)Hash
表忆肾,可以通過對(duì)象的指針查找到對(duì)應(yīng)的引用計(jì)數(shù),查找的過程也是哈希查找菱肖,提高查找效率客冈。
- size_t共有64位
- 第一位是
weakly_referenced
,表示對(duì)象是否有弱引用稳强,0
就是沒有场仲,1
就是有和悦。 - 第二位是
deallocating
,表示當(dāng)前對(duì)象是否正在dealloc
渠缕。 - 其余位存儲(chǔ)的就是對(duì)象的實(shí)際引用計(jì)數(shù)值鸽素,具體的引用計(jì)數(shù)值實(shí)際上要偏移兩位,因?yàn)榍懊嬗袃晌皇?code>weakly_referenced和
deallocating
亦鳞,我們要把這兩位去掉馍忽。
- 第一位是
weak_table_t
實(shí)際上也是一張Hash
表,key
為對(duì)象指針燕差,value
為weak_entry_t
遭笋,weak_entry_t
是一個(gè)結(jié)構(gòu)體數(shù)組,數(shù)組存儲(chǔ)的每一個(gè)對(duì)象就是弱引用指針徒探,也就是在代碼中定義的__weak
變量瓦呼,這個(gè)變量的指針就存儲(chǔ)在weak_entry_t
中。
完刹帕。