內(nèi)存布局
代碼段:保存程序二進(jìn)制。
bss:一般保存全局靜態(tài)變量等倘要。
data:保存初始化的全局變量圾亏,靜態(tài)變量。
棧:保存函數(shù)封拧,方法志鹃。iOS開發(fā)中一般為0x7段。
堆:通過alloc出來的對象泽西,保存在堆里曹铃。 0x6段
TaggedPointer
TaggedPointer為蘋果為內(nèi)存優(yōu)化提供的一個方案。
主要存儲一些小字節(jié)的數(shù)據(jù)捧杉。
TaggesPointer被設(shè)計為2大部分陕见,標(biāo)志位區(qū)和數(shù)據(jù)區(qū)秘血。
由特殊標(biāo)志位來標(biāo)志類型。使得讀寫更加效率评甜。
SideTables
哈希表
先說說存儲結(jié)構(gòu)灰粮,實際上在我們學(xué)過的數(shù)據(jù)結(jié)構(gòu)可以歸結(jié)為兩類:連續(xù)的的存儲結(jié)構(gòu)和不聯(lián)系的存儲結(jié)構(gòu),其代表分別為數(shù)組和鏈表忍坷。而我們學(xué)過的堆棧粘舟,隊列,樹承匣,圖蓖乘,都可以用這兩種結(jié)構(gòu)來實現(xiàn)。連續(xù)的存儲結(jié)構(gòu)——數(shù)組韧骗,在數(shù)據(jù)的查找和修改上具有很好的優(yōu)點嘉抒,很方便,時間復(fù)雜度很小袍暴。但是在數(shù)據(jù)的增添和刪除上則顯得很麻煩些侍,空間復(fù)雜度很大。而非連續(xù)政模,非順序的存儲結(jié)構(gòu)——鏈表恰和數(shù)組相反岗宣,數(shù)據(jù)的增添和刪除容易,空間復(fù)雜度很小淋样,查找和修改復(fù)雜耗式,時間復(fù)雜度很大。
那么有沒有一種數(shù)據(jù)結(jié)構(gòu)能折衷一下數(shù)組和鏈表的優(yōu)缺點呢趁猴?那就是——哈希表刊咳,既滿足了數(shù)據(jù)的查找和修改很容易,同時又不占用很多空間的特點儡司。
哈希表是基于哈希函數(shù)的娱挨,哈希表中的元素是有哈希函數(shù)確定的,哈希表作為一種數(shù)據(jù)結(jié)構(gòu)捕犬,我們用哈希表來存儲數(shù)據(jù)跷坝,在保存的時候存入的是一個<key—value>的結(jié)構(gòu),value由哈希函數(shù)作用于key上得到碉碉。但是存在一個哈希沖突問題柴钻,那就是當(dāng)你用hash函數(shù)作用在兩個互不相同的key上,得到的value值相等垢粮。
在runtime中顿颅,維護(hù)著一個sideTables,他是一個哈希表。
sideTables由多個SideTable組成粱腻。
sideTable又由一個自旋鎖slock,一個引用計數(shù)表refcnts斩跌,一個弱引用表weak_table組成绍些。
Retain和Release
- Retain
objc_object::rootRetain(bool tryRetain, bool handleOverflow)
{
if (isTaggedPointer()) return (id)this;
bool sideTableLocked = false;
bool transcribeToSideTable = false;
isa_t oldisa;
isa_t newisa;
do {
transcribeToSideTable = false;
oldisa = LoadExclusive(&isa.bits);
newisa = oldisa;
if (slowpath(!newisa.nonpointer)) {
ClearExclusive(&isa.bits);
if (!tryRetain && sideTableLocked) sidetable_unlock();
if (tryRetain) return sidetable_tryRetain() ? (id)this : nil;
else return sidetable_retain();
}
// don't check newisa.fast_rr; we already called any RR overrides
if (slowpath(tryRetain && newisa.deallocating)) {
ClearExclusive(&isa.bits);
if (!tryRetain && sideTableLocked) sidetable_unlock();
return nil;
}
uintptr_t carry;
newisa.bits = addc(newisa.bits, RC_ONE, 0, &carry); // extra_rc++
if (slowpath(carry)) {
// newisa.extra_rc++ overflowed
if (!handleOverflow) {
ClearExclusive(&isa.bits);
return rootRetain_overflow(tryRetain);
}
// Leave half of the retain counts inline and
// prepare to copy the other half to the side table.
if (!tryRetain && !sideTableLocked) sidetable_lock();
sideTableLocked = true;
transcribeToSideTable = true;
newisa.extra_rc = RC_HALF;
newisa.has_sidetable_rc = true;
}
} while (slowpath(!StoreExclusive(&isa.bits, oldisa.bits, newisa.bits)));
if (slowpath(transcribeToSideTable)) {
// Copy the other half of the retain counts to the side table.
sidetable_addExtraRC_nolock(RC_HALF);
}
if (slowpath(!tryRetain && sideTableLocked)) sidetable_unlock();
return (id)this;
}
當(dāng)調(diào)用retain時,isa.bits中的extra_rc+1
當(dāng)extra_rc溢出時耀鸦,sideTable.refcnts+1柬批。
而之前提到的TaggedPointer是不進(jìn)行引用計數(shù)的。
-
retainCount方法
返回extra_rc+1的值袖订,如果sidetable_rc有值再加上sideTable中的值氮帐。
3.release
objc_object::rootRelease(bool performDealloc, bool handleUnderflow)
{
if (isTaggedPointer()) return false;
bool sideTableLocked = false;
isa_t oldisa;
isa_t newisa;
retry:
do {
oldisa = LoadExclusive(&isa.bits);
newisa = oldisa;
if (slowpath(!newisa.nonpointer)) {
ClearExclusive(&isa.bits);
if (sideTableLocked) sidetable_unlock();
return sidetable_release(performDealloc);
}
// don't check newisa.fast_rr; we already called any RR overrides
uintptr_t carry;
newisa.bits = subc(newisa.bits, RC_ONE, 0, &carry); // extra_rc--
if (slowpath(carry)) {
// don't ClearExclusive()
goto underflow;
}
} while (slowpath(!StoreReleaseExclusive(&isa.bits,
oldisa.bits, newisa.bits)));
if (slowpath(sideTableLocked)) sidetable_unlock();
return false;
underflow:
// newisa.extra_rc-- underflowed: borrow from side table or deallocate
// abandon newisa to undo the decrement
newisa = oldisa;
if (slowpath(newisa.has_sidetable_rc)) {
if (!handleUnderflow) {
ClearExclusive(&isa.bits);
return rootRelease_underflow(performDealloc);
}
// Transfer retain count from side table to inline storage.
if (!sideTableLocked) {
ClearExclusive(&isa.bits);
sidetable_lock();
sideTableLocked = true;
// Need to start over to avoid a race against
// the nonpointer -> raw pointer transition.
goto retry;
}
// Try to remove some retain counts from the side table.
size_t borrowed = sidetable_subExtraRC_nolock(RC_HALF);
// To avoid races, has_sidetable_rc must remain set
// even if the side table count is now zero.
if (borrowed > 0) {
// Side table retain count decreased.
// Try to add them to the inline count.
newisa.extra_rc = borrowed - 1; // redo the original decrement too
bool stored = StoreReleaseExclusive(&isa.bits,
oldisa.bits, newisa.bits);
if (!stored) {
// Inline update failed.
// Try it again right now. This prevents livelock on LL/SC
// architectures where the side table access itself may have
// dropped the reservation.
isa_t oldisa2 = LoadExclusive(&isa.bits);
isa_t newisa2 = oldisa2;
if (newisa2.nonpointer) {
uintptr_t overflow;
newisa2.bits =
addc(newisa2.bits, RC_ONE * (borrowed-1), 0, &overflow);
if (!overflow) {
stored = StoreReleaseExclusive(&isa.bits, oldisa2.bits,
newisa2.bits);
}
}
}
if (!stored) {
// Inline update failed.
// Put the retains back in the side table.
sidetable_addExtraRC_nolock(borrowed);
goto retry;
}
// Decrement successful after borrowing from side table.
// This decrement cannot be the deallocating decrement - the side
// table lock and has_sidetable_rc bit ensure that if everyone
// else tried to -release while we worked, the last one would block.
sidetable_unlock();
return false;
}
else {
// Side table is empty after all. Fall-through to the dealloc path.
}
}
// Really deallocate.
if (slowpath(newisa.deallocating)) {
ClearExclusive(&isa.bits);
if (sideTableLocked) sidetable_unlock();
return overrelease_error();
// does not actually return
}
newisa.deallocating = true;
if (!StoreExclusive(&isa.bits, oldisa.bits, newisa.bits)) goto retry;
if (slowpath(sideTableLocked)) sidetable_unlock();
__sync_synchronize();
if (performDealloc) {
((void(*)(objc_object *, SEL))objc_msgSend)(this, SEL_dealloc);
}
return true;
}
extra_rc - 1
當(dāng)extra_rc下溢出時,查找sideTable洛姑。如果sideTable也沒有時上沐,對this發(fā)送SEL_dealloc方法進(jìn)行析構(gòu)。