从源码角度剖析VC6下的内存分配与切割的运作
目錄
- 前言
- 1、heap初始化
- 2、第一次分配內(nèi)存,計算真正區(qū)塊大小
- 3、new_region管理中心
- 4、__sbh_alloc_new_group()切割第一次分配好的內(nèi)存
- 5、開始切割內(nèi)存
前言
malloc與free帶來的內(nèi)存管理是應(yīng)付小區(qū)塊的,即SBH(small block heap),這點也可以從源代碼中看出:
VC6下會做門檻檢測
VC10下不會做門檻檢測,它總是會調(diào)用系統(tǒng)提供的HeapAlloc。這是因為操作系統(tǒng)提供的函數(shù)也有類似的功能了。
當(dāng)然還需要注意,我們分析的步驟是按照函數(shù)調(diào)用的次序來的:(從下往上的函數(shù)調(diào)用次序如下)
1、heap初始化
首先向操作系統(tǒng)要一大塊內(nèi)存(如4096),稱為_crtheap.
然后使用HeapAlloc從_crtheap中獲取16個HEADER大小的內(nèi)存,并獲取內(nèi)存指針。
每個HEADER長這樣:
| 圖1 | 圖2 |
2、第一次分配內(nèi)存,計算真正區(qū)塊大小
在debug模式下,通過調(diào)用malloc_dbg,分配得到32個8字節(jié)的內(nèi)存,即100h.
然后調(diào)整大小,擴充一個如下的結(jié)構(gòu):nDataSize記錄真正大小。gap用戶調(diào)試器檢查保護(hù)nsize內(nèi)存。注意gap一共上下兩個,但是結(jié)構(gòu)體里面只定義了上面的那個。
| 圖1 | 圖2 |
通過調(diào)用_heap_alloc_base來獲取每個blockSize內(nèi)存的頭指針。
然后通過頭尾指針將所有分配出來的block串接起來,變?yōu)橐粋€鏈表:
頭指針為_pFirstBlock;
尾指針為_pLastBlock;
_heap_alloc_base具體內(nèi)容
之前在通過heap_alloc_dbg中調(diào)用_heap_alloc_base來獲取每個blockSize內(nèi)存的頭指針。
現(xiàn)在來看看它具體步驟:
1、首先將擴充完的大小與__sbh_threshold進(jìn)行比較,所過是小區(qū)塊就用shb服務(wù),否則由操作系統(tǒng)服務(wù)。
小區(qū)塊的定義是:這塊內(nèi)存大小+cookie大小 < 1024個字節(jié)
接下來看__sbh_alloc_block具體細(xì)節(jié):
對擴充完的區(qū)塊再次進(jìn)行擴充,加上8個字節(jié)(上下cookie)并且進(jìn)行roundup操作(變?yōu)?6的整數(shù)倍)
以上圖為例,最終得到的結(jié)果是0x130,但是在cookie中寫入的卻是131。這是因為由于是16的倍數(shù),所以最后一位一定是0。最后一位0/1表示是在SBH手上還是已經(jīng)分配出去了。
3、new_region管理中心
之前在第一步heap初始化的時候分配了16個HEADER,每個HEADER現(xiàn)在用來管理1MB的內(nèi)存。每個HEADER有兩根指針:一根指向虛擬地址空間,一根指向管理中心。
管理中心被稱為new_region。
region設(shè)計如下:
它含有:
1個整數(shù)
64個char
32個Hi和32個Lo并起來,構(gòu)成32組,每組64個bit。(用來管理哪些區(qū)塊有或者沒有)
32個group,每個group由64根雙向鏈表組成。
| 圖1 | 圖2 |
4、__sbh_alloc_new_group()切割第一次分配好的內(nèi)存
已知擁有32個group,對應(yīng)了1MB的虛擬內(nèi)存空間,平均下來每個group管理32KB內(nèi)存。
每32KB的內(nèi)存還要再分割,分成8page(注意這里的內(nèi)存都是連續(xù)的),每page4KB,然后用指針把8個page串起來。串起來之后將它們掛到group的最后一條鏈表。這便是SBH現(xiàn)在所作的事情。
下面紅色部分便是group與32KB內(nèi)存的具體銜接圖。
0xfff…實際上就是-1,作用:
將來回收內(nèi)存的時候需要合并內(nèi)存,合并的過程中需要用-1作為阻隔器,即只能合并-1與-1之間的內(nèi)存。
上-1下面有3格,第一格記錄的是兩個-1之間的內(nèi)存,一開始大小為4080,后兩格作為指針將page從前到后串聯(lián)起來。
每個page大小4096字節(jié),去掉兩個-1,剩下4088個字節(jié)。由于每個紅色區(qū)塊要保證大小是16倍數(shù)。所以需要將4088再分割出去8個字節(jié)(也就是保留)。
每個4K都這樣處理,形成如下的圖:
5、開始切割內(nèi)存
先前講到了每個group中有64根雙向鏈表,平均分配下來:
第一條鏈表管理16個字節(jié)的內(nèi)存
第二條鏈表管理32個字節(jié)的內(nèi)存
…
最后一條管理1024個字節(jié)的內(nèi)存(實際上大于1k的內(nèi)存全都?xì)w最后一條管理)
現(xiàn)在開始想象開始切割page1的內(nèi)存,當(dāng)page1內(nèi)存小于1k的時候就不應(yīng)該由最后一條鏈表來管,應(yīng)該要計算,然后將指定鏈表拉過來。現(xiàn)在來看具體的切割:
在之前的步驟中我們以及知道我們需要的內(nèi)存是多大了:130h(100h+debugHeader+cookie+roundup)
所以,現(xiàn)在還剩下ec0h的內(nèi)存。切割完后,系統(tǒng)將紅色的地址:007d0ed0,傳出去。
客戶程序拿到該指針,便認(rèn)為擁有了這一塊內(nèi)存。
由上可知,切割其實只是cookie的調(diào)整 + 指針的傳送。
注意紅色指針還需要調(diào)整(扣除debugHeader),調(diào)整到右邊的結(jié)構(gòu)塊的綠色部分,該部分才是使用者(ioinit)真正拿到的地址。
總結(jié)
以上是生活随笔為你收集整理的从源码角度剖析VC6下的内存分配与切割的运作的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 纸牌屋第五季剧情介绍
- 下一篇: 算法题复习(快排、链表、二分、哈希、双指