摘要:用表達(dá)式表示以上結(jié)論或者結(jié)構(gòu)體內(nèi)存對(duì)齊核心結(jié)論它是編譯器做的優(yōu)化,空間換時(shí)間的思想。整個(gè)聯(lián)合體占用的空是所有字段多帶帶占用空間大小中取占用空間大小最大的字段。使用命令繼續(xù)運(yùn)行我們可以看到上有項(xiàng)已經(jīng)不是了,說明上次分配過內(nèi)存。
baiyan
全部視頻:https://segmentfault.com/a/11...
源視頻地址:http://replay.xesv5.com/ll/24...
復(fù)習(xí)宏的用法:
typedef struct _zend_alloc_globals { zend_mm_heap *mm_heap; } zend_alloc_globals; ... # define AG(v) (alloc_globals.v) static zend_alloc_globals alloc_globals;
這個(gè)帶有參數(shù)的宏取得zend_alloc_globals結(jié)構(gòu)體類型里的zend_mm_heap結(jié)構(gòu)體字段,如AG(mm_heap) = alloc_globals.mm_heap
宏就是替換。
結(jié)構(gòu)體與結(jié)構(gòu)體內(nèi)存對(duì)齊 結(jié)構(gòu)體先看一段結(jié)構(gòu)體代碼struct.c:
#includeint main() { struct a{ char a; int b; long c; void *d; int e; char *f; }s; s.a = "c"; s.b = 1; s.c = 22l; s.d = NULL; s.e = 1; s.f = &s.a; printf("sizeof struct a is %d", (int)sizeof(s)); }
編譯:gcc -g struct.c -o struct
運(yùn)行:./struct,sizeof(a)的打印結(jié)果為:40
對(duì)這個(gè)結(jié)構(gòu)體進(jìn)行g(shù)db調(diào)試:
在這里我們可以看到第一行是所有結(jié)構(gòu)體變量的初始值,注意指針變量是一個(gè)隨機(jī)的地址,在給s.d賦值的過程中,地址變成了0x0,它是一個(gè)特殊的地址值,代表NULL。
除此之外,我們注意到結(jié)構(gòu)體s的地址和a變量的地址是相同的。
用表達(dá)式表示以上結(jié)論:&s = &s.a = f或者s = s.a = *f
結(jié)構(gòu)體內(nèi)存對(duì)齊核心結(jié)論:它是編譯器做的優(yōu)化,空間換時(shí)間的思想。
對(duì)齊方式:按照結(jié)構(gòu)體所有字段的最小公倍數(shù)做對(duì)齊(最小公倍數(shù)是8B就按8B對(duì)齊;是4B就按4B對(duì)齊),且與結(jié)構(gòu)體字段的排列順序是相關(guān)。如圖:
我們利用gdb驗(yàn)證一下以上的結(jié)論,還是利用上述同樣的代碼:
我們看到變量a的起始地址是150,而b的地址是154,顯然做了對(duì)齊。如果不對(duì)齊,b的地址應(yīng)該為151,說明a和b中間空了3B的大小。c的地址是158,就是下一個(gè)8B的起始地址,而d的地址是160(注意這里是16進(jìn)制,158+8 = 160的時(shí)候才會(huì)進(jìn)位),證明了c占用了8B,下面d變量也同理占用了8B。注意e變量,它是一個(gè)int,如果不對(duì)齊應(yīng)該只占用4B。而它占用了170(f的起始地址)- 168(e的起始地址) = 8B,所以一定是做了內(nèi)存對(duì)齊的。
注意一個(gè)特例:如果b是一個(gè)char類型,那么是直接緊跟在上一個(gè)char后面,如圖:
注意這里的地址均為邏輯地址,每次編譯后的邏輯地址是相同的,而物理地址是不同的。
注意:如果調(diào)換順序,把b和c調(diào)換位置,就會(huì)變成8B(a)+8B(c)+8B(b)+8B(d)+8B(e)+8B(f) = 48B
注意:一定是所有字段的最小公倍數(shù)是幾字節(jié),就按幾字節(jié)對(duì)齊,我們看一下結(jié)構(gòu)體中只有char類型變量的情況:
#includeint main() { struct a{ char a; char b; char c; }s; s.a = "c"; s.b = "b"; s.c = "a"; printf("sizeof struct a is %d ", (int)sizeof(s)); }
這個(gè)結(jié)構(gòu)體中只有char類型變量
編譯運(yùn)行,輸出sizeof(s)的結(jié)果為:3
為什么不是4或者8?因?yàn)?,1,1的最小公倍數(shù)就是1,就按照1B來對(duì)齊,而不是4B、8B
同理,如果都是int類型的變量,那么sizeof(s)的結(jié)果為12,也很好理解了
聯(lián)合體核心結(jié)論:所有聯(lián)合體字段共用一塊內(nèi)存空間。整個(gè)聯(lián)合體占用的空是所有字段多帶帶占用空間大小中取占用空間大小最大的字段。
同樣先看一段代碼:
#includeint main() { union a{ char a; int b; long c; void *d; int e; char *f; }s; s.a = "c"; s.b = 1; s.c = 22l; s.d = NULL; s.e = 1; s.f = &s.a; printf("sizeof struct a is %d", (int)sizeof(s)); }
這段代碼與上段結(jié)構(gòu)體的代碼只將struct修改為union,其他均不變
編譯運(yùn)行,輸出sizeof(a)的結(jié)果為:8
我們利用gdb調(diào)試一下這段代碼:
我們可以看到,后面的變量一賦值,就會(huì)覆蓋前面的變量值
再看一下每個(gè)變量的地址,我們可以清楚地看到,所有變量的起始地址都是一樣的。
其他思考:一個(gè)void *類型的變量,能否直接取它的內(nèi)容?答案:不可以,其他有類型的指針變量可以取內(nèi)容是因?yàn)橛涗浟水?dāng)前類型的長(zhǎng)度,而void *類型沒有長(zhǎng)度,無法直接取,除非使用強(qiáng)制類型轉(zhuǎn)換或者指定長(zhǎng)度。
延伸:PHP所有變量基于zval,zval就是由3個(gè)聯(lián)合體組成(zend_value,u1,u2)這里不展開
大小端:
大端:也叫高尾端,即數(shù)據(jù)尾端(低位)放在高地址
小端:也叫低尾端,即數(shù)據(jù)尾端(低位)放在低地址
網(wǎng)絡(luò)字節(jié)序是大端的
網(wǎng)絡(luò)字節(jié)序是大端的,所以小端機(jī)器需要對(duì)收到或發(fā)出的數(shù)據(jù)包進(jìn)行大小端的轉(zhuǎn)換
如何判斷是大端還是小端:參考:判斷機(jī)器大小端的兩種實(shí)現(xiàn)方式
利用指針:int為4個(gè)字節(jié),通過強(qiáng)轉(zhuǎn)成char類型,取低1個(gè)字節(jié),如果恰好是0x78,那就說明低1個(gè)字節(jié)恰好存在低地址,為小端。如果取低1個(gè)字節(jié)的結(jié)果是0x12,低1個(gè)字節(jié)存在高地址,為大端。
int main() { int i = 0x12345678; if (*(char*)&i == 78) { printf("小端"); } else { printf("大端"); } }
利用聯(lián)合體:本質(zhì)思想和指針方法相同,利用了聯(lián)合體共用同一塊存儲(chǔ)空間的特性。
int main() { union w{ int a; char b; }c; c.a = 1; if (c.b == 1) { printf("小端"); } else { printf("大端"); } }
利用gdb觀察PHP內(nèi)存分配時(shí)的情況,示例代碼:
gdb php并在_emalloc()處打斷點(diǎn),運(yùn)行代碼:
我們可以清晰地看到mm_heap這個(gè)變量,它在small、large內(nèi)存中存儲(chǔ)一些額外的page分配信息等等。其中比較重要的size、peak、free_slot、main_chunk等變量。free_slot數(shù)組暫時(shí)還是空。再看mm_heap中的main_chunk字段,它來表示一個(gè)chunk,是一個(gè)雙向鏈表。第一個(gè)字段heap是一個(gè)指向zend_mm_heap的指針,可以快速找到第一個(gè)記錄信息的page,并且可以發(fā)現(xiàn)它的地址和上面直接打印alloc_globals.mm_heap的地址是相同的。再觀察free_map字段,它由8個(gè)個(gè)uint64類型組成,代表各個(gè)page的使用情況,這里第1個(gè)page存了zend_mm_heap結(jié)構(gòu)體,已經(jīng)被使用。再看map字段,它是一個(gè)數(shù)組,大小為512,每個(gè)都是uint32類型,打印數(shù)組第一項(xiàng)的值,以16進(jìn)制表示為0x40000001,代表large內(nèi)存,最后一位是1,代表分配1個(gè)page。
使用c命令繼續(xù)運(yùn)行:
我們可以看到free_slot上有3項(xiàng)已經(jīng)不是0x0了,說明上次分配過small內(nèi)存。且現(xiàn)在free_map的值發(fā)生了變化,而map說明第1、2、3、4、9頁已經(jīng)被使用了,那么為什么中間有幾個(gè)0呢,是因?yàn)榈?個(gè)map值,按照16進(jìn)制打印為0x40000005,是large內(nèi)存,且需要分配5個(gè)頁,所以后面的4個(gè)頁都是0。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/31267.html
摘要:此文用于匯總跟隨陳雷老師及團(tuán)隊(duì)的視頻,學(xué)習(xí)源碼過程中的思考整理與心得體會(huì),此文會(huì)不斷更新視頻傳送門每日學(xué)習(xí)記錄使用錄像設(shè)備記錄每天的學(xué)習(xí)源碼學(xué)習(xí)源碼學(xué)習(xí)內(nèi)存管理筆記源碼學(xué)習(xí)內(nèi)存管理筆記源碼學(xué)習(xí)內(nèi)存管理筆記源碼學(xué)習(xí)基本變量筆記 此文用于匯總跟隨陳雷老師及團(tuán)隊(duì)的視頻,學(xué)習(xí)源碼過程中的思考、整理與心得體會(huì),此文會(huì)不斷更新 視頻傳送門:【每日學(xué)習(xí)記錄】使用錄像設(shè)備記錄每天的學(xué)習(xí) PHP7...
摘要:在這里使用學(xué)而思網(wǎng)校的錄像設(shè)備,記錄每天學(xué)習(xí)的內(nèi)容執(zhí)行潘森執(zhí)行潘森執(zhí)行潘森趙俊峰紅黑樹景羅紅黑樹景羅配置三叉樹田志澤新建模塊馬運(yùn)運(yùn)配置田志澤田志澤田志澤李樂田志澤田志澤文件系統(tǒng) 在這里使用學(xué)而思網(wǎng)校的錄像設(shè)備,記錄每天學(xué)習(xí)的內(nèi)容: 2019-07-15 ~ 2019-07-19 07-18 nginx http 執(zhí)行 by 潘森 07-17 nginx http 執(zhí)行 by 潘森 07...
閱讀 1336·2023-04-25 23:47
閱讀 912·2021-11-23 09:51
閱讀 4432·2021-09-26 10:17
閱讀 3706·2021-09-10 11:19
閱讀 3254·2021-09-06 15:10
閱讀 3546·2019-08-30 12:49
閱讀 2421·2019-08-29 13:20
閱讀 1730·2019-08-28 18:14