知識點補充
LPVOID VirtualAlloc{ LPVOID lpAddress, // 要分配的內存區域的地址 (當實參爲0時,由操做系統指定地址) DWORD dwSize, // 分配的大小 DWORD flAllocationType, // 分配的類型 DWORD flProtect // 該內存的初始保護屬性 }; 1. virtualAlloc 是一個 Window API 函數,該函數的功能是在調用進程的虛擬地址空間預約或者提交一部分頁 2. flAllocationType : MEM_RESERVE 保留分配地址,不分配物理內存。這樣能夠阻止其它分配函數 malloc 和 LocalAlloc 等再使用已保留的內存範圍,直到它被釋放 MEM_COMMIT 爲指定地址空間提交物理內存。這個函數初始化內在爲零
typedef struct tagGroup { int cntEntries; struct tagListHead listHead[64]; }GROUP, *PGROUP; 1. int cntEntries, 累計量,內存分配時+1,內存釋放時-1。當 cntEntries 爲 0 ,表示 8 page 所有收回,就能夠將此內存塊歸還給操做系統
typedef unsigned int BITVEC; typedef struct tagRegion { int indGroupUes; ...... BITVEC bitvGroupHi[32]; BITVEC bitvGroupLo[32]; struct tagGroup grtHeadList[32]; } 1. indGroupUes, 定位當前正在使用的 Group 索引 2. 32 組 GROUP 對應 32 * 64 bit,當 bitvGroup[i][j] (第 i 個 GROUP 中的第 j 條雙向鏈表)的 bit 位爲 1 表示鏈表有可用內存塊, 爲 0 表示鏈
typedef struct tagEntry { int sizeFront; struct tagEntry *pEntryNext; struct tagEntry *pEntryPrev; }ENTRY, *PENTRY; 1. pEntryNext、pEntryPrev 爲嵌入式指針,在內存塊分配先後分配後有不一樣的解釋
流程描述
1. 由 ioinit.c, line#18 申請 100h,區塊大小 130h (debugheader、cookie、16字節對齊調整) 2. 使用 Group[indGroupUes] => Group[0] ; bitvGroup[indGroupUes] [130h = 304 * 16 - 1] => bitvGroup[0] [18] bit 爲 0,代表對應 Group[0][18] (listHead[18])鏈表中無可用區塊,因而繼續向右查找,Group[0][63] 最後一條鏈表有可用區塊 3. 進行內存切割(參見上節文章流程)
__crtGetEnvironmentStringsA()
發起
環境變量處理完成後的內存歸還(main之氣) 240H
流程描述
1. 當前操做仍爲 Group[0] 2. 修改 cntEnteries 由 14 爲 13 3. 240h / 16 - 1 = 576 / 16 - 1 = 35 => listHead[35], 歸還的內存塊掛接(嵌入式指針完成)到 35 號鏈表 4. 修改 bitvGroup[0] [35] 爲 1 5. 修改 cookie 最後一位爲 0
流程描述 【申請 b0h】
1. 修改 cntEnteries 由 13 爲 14 2. b0h / 16 - 1 = 10 => bitvGroup[0][0] 爲 0, 表示無可用區塊 3. 查找臨近可用區塊, bitvGroup[0][35] 爲 1, 在 listHead[35] 鏈表管理的內存塊中進行切割 4. 240h - b0h = 190h, 190h / 16 - 1 = 24 切割後的內存塊從新調整掛在到 listHead[24] 5. 修改 bitvGroup[0][35] 爲 0,bitvGroup[0][24] 爲 1
流程描述【申請 230h】
1. 通過不斷的內存分配,Group[0] 管理的 page[1-8] (32KB) 已沒法知足 230h 2. Group[indGroupUes] => indGroupUes[1] 開始新的故事
爲了儘可能減小內存空間的碎片化以知足後期較大內存塊的申請需求,將相鄰空閒的區塊進行合併時 SBH 進行碎片整理的思路。(分爲向下合併、向上合併)
[最左側圖] 1. 應用程序歸還上圖藍色箭頭所指向內存地址[0x00000304](修改上下 cookie 最後 1 bit 爲 0) 2. 0x00000304 向上加 4bytes, 定位到上 cookie 地址 0x00000300,獲取到當前內存塊長度 0x300 3. 上 cookie 地址加 0x300 定位到下 cookie [最中間圖] 4. 下 cookie 加 4 bytes, 定位到下塊內存塊的上 cookie,檢查最後bit爲0,代表此塊內存空閒,兩內存塊進行合併【下內存塊須要調整對應的 listHead 和 bitvGroup】 [最右側圖] 5. 上 cookie 減 4 bytes, 定位到上內存塊的下 cookie, 檢查最後bit爲0, 代表此內存塊空間,接上步驟內存塊進行合併【上內存塊須要調整對應的 listHead 和 bitvGroup】 6. 0x300 + 0x300 + 0x300 = 0x900 大於 1 KB, 調整掛載到最後一條鏈表 list[63],設置對應的 bitvGroup 位
p 落在哪一個 Header 內
1. 已知 Header 首地址、 Header 數量、Header 大小 2. Header 內成員 pHeapData 指向 1MB 內存空間 3. 遍歷全部 Header, 查看 p 落在哪一個 Header 的 [pHeapData, pHeapData+1MB] 內
p 落在哪一個 Group 內
(p - pHeapData) / 32 - 1
p 落在哪一個 free-list 內
1. 上 cookie 地址 = p - 4 2. 從上 cookie 得到要釋放的內存塊大小 size 3. index = (size / 16) - 1
分段管理之妙,利於歸還 O.S(當
cntEntries
爲 0 時進行「全回收」)
SBH 作分段的管理(每次申請1MB虛擬空間 -> 每次申請32KB物理內存 -> 分爲8Page -> 64條鏈表)cookie
爲了內存分配的高效性,對內存的回收使用了 Defering 機制
當歸還完全部內存塊, SBH 將恢復到初始狀態
疊屋架牀,層層封裝是一種浪費嗎? 是,但有必要。由於每個上層調用都沒法保證下層是否有合適的內存管理