前言
本文來(lái)探討, 結(jié)構(gòu)體對(duì)齊, 位域, 聯(lián)合體對(duì)齊, 系統(tǒng)內(nèi)存開(kāi)辟, 內(nèi)存對(duì)齊原則.
上一篇的坑
01.png
上一篇中留了一個(gè)大坑, 就是在匯編的時(shí)候objc_alloc這個(gè)符號(hào)其實(shí)在源碼中是沒(méi)有調(diào)用的. 由于_objc_rootAlloc和這個(gè)乍一看沒(méi)在意, 感覺(jué)就是一個(gè)東西, 所以忽略了. 后面多參考參考其他人的方式專門寫(xiě)一篇, 遇到類似這種問(wèn)題無(wú)從下手的問(wèn)題, 怎么入手的解決的辦法和邏輯.
因?yàn)檫@種問(wèn)題的入手思維, 嘗試不同的解決方式, 我覺(jué)得是非常重要的. 所以打算另起一篇
結(jié)構(gòu)體對(duì)齊
先討論沒(méi)有自定義pack的情況, 自定義pack參數(shù)只能是 '1', '2', '4', '8', or '16'就是按照指定的pack參數(shù)進(jìn)行對(duì)齊, 搞懂了沒(méi)有pack的情況 ,有pack的自然懂。
結(jié)構(gòu)體
結(jié)構(gòu)體對(duì)齊的原則:
在沒(méi)有定義 #pragma pack(value)的情況下, 原則如下:
- 第一個(gè)成員的首地址為0.
- 每個(gè)成員的首地址是自身大小的整數(shù)倍
- 結(jié)構(gòu)體的總大小,為其成員中所含最大類型的整數(shù)倍。
02.png
如上圖所示, 每一個(gè)成員的起始地址都是自身大小的整數(shù)倍, 結(jié)構(gòu)體的總大小是成員變量中最大的成員的整數(shù)倍.
結(jié)構(gòu)體嵌套結(jié)構(gòu)體
先上圖:
03.png
聯(lián)合體 && 結(jié)構(gòu)體嵌套聯(lián)合體
04.png
位域 && 結(jié)構(gòu)體嵌套位域
- 位域(位段)結(jié)構(gòu)的大小既取決于結(jié)構(gòu)內(nèi)部所有位域的總大小,也取決于該位域聲明時(shí)的類型
- 當(dāng)一個(gè)位域結(jié)構(gòu)內(nèi)部所有位域總大小小于該結(jié)構(gòu)中位域聲明時(shí)的類型中長(zhǎng)度最大的類型長(zhǎng)度
時(shí),該結(jié)構(gòu)大小為該類型長(zhǎng)度 (此處用用大小和長(zhǎng)度區(qū)分自定義結(jié)構(gòu)和C語(yǔ)言內(nèi)置類型)- 位域結(jié)構(gòu)的大小總為該結(jié)構(gòu)中存在的所有的位域在聲明時(shí)所用的內(nèi)置類型中長(zhǎng)度最大的內(nèi)置類型的長(zhǎng)度的整數(shù)倍
繼續(xù)上圖:
05.png
結(jié)構(gòu)體嵌套位域
06.png
總體來(lái)說(shuō), 位域聯(lián)合體這種單片機(jī)什么的用的多, 正常了解一下即可. 知道怎么計(jì)算, 對(duì)內(nèi)存有一個(gè)好的認(rèn)識(shí)就行了.
內(nèi)存對(duì)齊原則
上面的東西看懂了, OC的原則也是一樣, 有內(nèi)存優(yōu)化, 不完全按照順序排列, 對(duì)齊方式都是一致. 先看一下自定義類和原生的size區(qū)別:
07.png
08.png
上面兩個(gè)圖可以知道, 成員變量對(duì)一個(gè)類的實(shí)際大小有影響, 經(jīng)過(guò)測(cè)試, 方法, 屬性都沒(méi)有影響. 但是@property會(huì)自動(dòng)合成set, get, ivar所以屬性也可以實(shí)際影響到內(nèi)存大小. 有興趣的可以@synthesize @dynamic, 里面還是有坑點(diǎn)的.
09.png
10.png
圖9中對(duì), age, c1, c2進(jìn)行了優(yōu)化, 都放在了同一個(gè)8字節(jié)中處理, 測(cè)試在加入兩個(gè)char也會(huì)一同合并進(jìn)去, 都是來(lái)自于OC對(duì)內(nèi)存的優(yōu)化.
系統(tǒng)內(nèi)存開(kāi)辟
先看下面的代碼:
11.png
可以自己猜測(cè)一下結(jié)果先
sizeof
準(zhǔn)確點(diǎn)說(shuō)的話,sizeof并不是一個(gè)函數(shù),當(dāng)做是一個(gè)操作符,主要作用于編譯時(shí),作用的對(duì)象是數(shù)據(jù)類型。
sizeof 只會(huì)計(jì)算類型所占用的內(nèi)存大小,不會(huì)關(guān)心具體的對(duì)象的內(nèi)存布局;
sizeof(p), p是一個(gè)指向堆區(qū)Person空間的指針, 所以p的大小為8.
class_getInstanceSize
class_getInstanceSize為對(duì)象實(shí)際開(kāi)辟的內(nèi)存空間, 應(yīng)該都知道是8字節(jié)對(duì)齊, 也說(shuō)不上來(lái)為什么, 跟著objc的源碼看一下, 還是通過(guò)匯編慢慢往里找也可以. 上一篇已經(jīng)說(shuō)過(guò)方案了, 查找流程都一致. 直接看objc:
//不多bb, 直接全局搜索, 直接找到, 往里面進(jìn)
size_t class_getInstanceSize(Class cls)
{
if (!cls) return 0;
return cls->alignedInstanceSize();
}
uint32_t alignedInstanceSize() const {
return word_align(unalignedInstanceSize());
}
#ifdef __LP64__
# define WORD_SHIFT 3UL
# define WORD_MASK 7UL
# define WORD_BITS 64
#else
# define WORD_SHIFT 2UL
# define WORD_MASK 3UL
# define WORD_BITS 32
#endif
//核心就在這里, 上一篇也講到過(guò), 在32位中就是4字節(jié)對(duì)齊, 64位中就是8字節(jié)對(duì)齊,
//一個(gè)宏定義兼容了兩種系統(tǒng), 并且代碼也不用改動(dòng), 多好.
static inline size_t word_align(size_t x) {
return (x + WORD_MASK) & ~WORD_MASK;
}
malloc_size
實(shí)際調(diào)用的malloc_size, 點(diǎn)擊進(jìn)入查看:
12.png
進(jìn)入objc源碼查看也沒(méi)有, 完全無(wú)從下手, 此時(shí)可以留意左上角的紅線.
此時(shí): 進(jìn)入思考, 怎么找到目標(biāo)實(shí)現(xiàn), 是否開(kāi)源, 一無(wú)所知.
直接谷歌oc中malloc源碼在哪個(gè)文檔里, 出來(lái)一堆, 相信你看得懂
13.png
- 直接賦值粘貼方法谷歌搜索

可以看到apple 開(kāi)源官方文檔里面有, 假設(shè)我們什么都搜不到, 什么都不知道, 只能去stack overflow找尋一下, 如果malloc的任何信息都搜索不到, 那么只能靠自己鉆研了, 嘗試像別人請(qǐng)教請(qǐng)教也自己鉆研一下.
繼續(xù)進(jìn)入libmalloc的庫(kù), github上有很多提供可編譯的版本拉過(guò)來(lái)直接用.
從上面的圖可知有malloc.h, 直接malloc的實(shí)現(xiàn)搜索malloc.c:
15.png
直接找到malloc.c了, 進(jìn)入探索一下.
- 匯編
16.png
2, 源碼17.png
從上面匯編可以看出, 是一直在調(diào)用 看一下find_registered_zone內(nèi)部, 但是經(jīng)過(guò)調(diào)試斷點(diǎn)其實(shí)return的地方都斷不到, 此時(shí)只能跟著匯編走流程了, 一步一步, 大概斷言到了幾個(gè)流程, 但是由于我對(duì)x86的指令集不熟悉, 電腦usb口壞了, 只能大致記錄一下過(guò)程, 等電腦修好大致在走一遍.
//最后走入了malloc.c 652行 最后等同于malloc_zones[0];
1. lite_zone->malloc_zones->nano_size->_nano_vet_and_size_of_live
->_nano_block_inuse_p
'''
這些代碼的注釋大多都是虛擬地址分配的, 我看的大致的意思就是注冊(cè)過(guò)的內(nèi)存malloc_zones, 當(dāng)前進(jìn)來(lái)的替換掉0位置的數(shù)據(jù), 然后把0中的size返回回去. 也有可能是我理解錯(cuò)誤, 后面電腦修好了 我在驗(yàn)證一下.
總結(jié)
知識(shí)太多, 有效吸收理解, 并不需要浪費(fèi)大量時(shí)間研究并不是很有意義的東西, 對(duì)我們普通開(kāi)發(fā)者來(lái)說(shuō), 比如find_registered_zone這段代碼, 大致的注釋就是虛擬內(nèi)存分配的一些東西, 需要搞懂虛擬內(nèi)存分配的流程, 像這種的有興趣可以直接去讀深入理解計(jì)算機(jī)原理, 理解之后估計(jì)能找到那種互通的感覺(jué).
跟上時(shí)代的步伐, 一步一步慢慢走..穩(wěn)住我們能贏!!!















