09 年翻譯的東西。html
原文見: http://www.nobugs.org/developer/win32/debug_crt_heap.html
在DeviceStudio的Debug編譯模式下, crt中的堆內存分配操做----包括malloc()和free()----使用一個特殊的, 便於調試的版本, 咱們稱之爲crt debug堆(譯註: 下面簡稱CDH). 相比於電光火石(譯註: 原文blazingly, 我想不出更確切的說法)的運行效率, 調試版本更關注對於堆錯誤的定位, 它經過如下三種手法實現以上訴求:api
1.用守護內存塊包圍新分配的內存, 這樣就能夠偵測到緩衝過載和欠載. 所謂守護內存塊就是一系列被填充爲0xfd的內存字節, 又被稱爲」無主之地」.函數
2.用一個特殊的值(0xcd)初始化新申請的內存.翻譯
3.同時用一個特殊的值填充(0xdd) 被釋放的內存.debug
爲了便於記憶, 能夠這樣理解以上用於填充的值:指針
1.0xcd意爲Clean Memory.調試
2.0xdd意爲Dead Memory.orm
3.0xfd意爲Fences(譯註: 柵欄).htm
CDH將大部分工做交由win32 api中的堆函數HeapAlloc()和HeapFree()完成, 每進程4Gb的虛地址空間的分塊和管理是由kernel32.dl中的Win32堆本身完成的. 對象
當你調用malloc(8)分配8字節的內存時, CDH會調用HeapAlloc()申請48字節的內存, 額外的40字節被用來存放內存塊的額外信息----好比調用malloc()的源文件和行號, 以及指向上/下一個內存塊的指針.在後面的列表中, 全部的crt調試信息均被標記爲紅色.
HeaoAlloc()自己也須要記錄簿記(譯註: 原文bookkeeping)信息, 事實上,一個HeaoAlloc()調用會在進程地址空間裏保留80字節內存, 其中8字節的簿記信息出如今真正使用的40字節以前, 剩下的32字節在真正使用的40字節以後.在下面的列表裏, Win32堆簿記信息被標記爲灰色.最後, HeapAlloc()返回的內存老是被一4字節對齊初始化爲0xBAADF00D.順道說一句: 當你使用經由VirtualAlloc()管理內存的虛擬內存管理器申請內存頁時, 獲取的內存頁會被初始化爲0, 因此能夠判定HeapAlloc()按照上述模式執行了額外的初始化工做.
crt取得40字節的內存塊後會填入本身的簿記信息. 頭兩個字(譯註: 即WORD)用來存放直向」前一個」和」後一個」crt堆內存塊的指針. 這裏的先後不能從字面去理解, 由於所謂指向」後一個」內存塊的指針事實上指向的是時間順序上緊鄰本內存塊以前分配的內存塊, 相應的, 指向「前一個」的指針指向的是下一個將被分配的內存塊. 之因此這樣命名, 是由於內存塊鏈表是從最後分配的內存塊開始的. 同時, 爲了使堆檢查代碼能遍歷每一個內存塊, CDH還保存着第一塊和最後一塊內存的地址(_pFirstBlock和_pLastBlock).
若是調用malloc()代碼所在的文件名和行號是已知的, 它們將被被保存在第三第四個字中, 緊接着下面一個字表示本塊申請了多少字節內存. 再下面一個字是類型域, 等於1表示new或malloc()分配的普通塊, 2表示crt分配的供內部使用的塊. 0表示已經被用戶釋放可是還未歸還給win32堆的塊(詳見下文). 一般來講, 新申請的內存塊本位置等於1. 最後一塊是計數器, 每執行一次內存分配計數器加1.
經過malloc()獲得的8字節內存無用內存包圍. 這些空內存被填充爲0xfd, 當整個內存塊被free()時, crt會檢查這些空內存存放的值是否仍然是0xfd. 若是值改變了, 說明程序有錯誤存在. 不幸的是, 錯誤可能發生的遠比檢查到的早, 若是須要更精確的檢查, 可使用Purify或者Boundschecker, 他們能讓程序中止在內存崩潰的點上, 若是你不想花錢, 你能夠等我寫篇文章告訴你怎樣巧妙的實現這個功能.
真正被使用的8字節內存被初始化爲0xcd, 若是你的對象中間出現連續的0xcd, 那麼你必定是忘記了初始化一些東西.
當你調用free()釋放上述8字節的內存時, crt首先會用0xdddddddd填充所有48字節的內存塊(包括簿記信息), 這樣就能夠經過檢查這塊內存的值獲知這塊內存在釋放後是否右被寫入過(好比使用野指針寫內存).
接下來, crt一般會調用HeapFree()函數將本內存塊歸還給win32堆, win32堆會將本內存塊填充爲0xFEEEFEEE. 注意, crt並不維護」空閒塊列表」, 這些都由HeapFree()來作(譯註: 也就是說空閒列表是由win32 堆來維護的).
可是, 你可讓crt不把被釋放的內存塊歸還給win32堆(譯註: 也就是不調用HeapFree()), 方法是將_CRTDBG_DELAY_FREE_MEM_DF傳遞給_CrtSetDbgFlag(), 這在你跟蹤野指針錯誤時將會派上用場,在這種狀況下, 內存不會被複用, 因此釋放過的內存的值必然是0xDDDDDDDD, 處非你對釋放過的內存執行了寫操做. 你能夠調用_CrtCheckMemory()檢查釋放過的內存是否被篡改.(譯註: 這個函數缺省狀況下須要顯式調用, 你也能夠將_CRTDBG_CHECK_ALWAYS_DF傳遞給_CrtSetDbgFlag(), 這樣每次分配和釋放內存時都會調用_CrtCheckMemory())
一個例子:
下面是調用p = malloc(8)而後調用free(p)的過程當中內存的變化表, malloc(8)返回的指針爲0x00321000, 我列出了偏移後的內存值, 以便你找到你本身分配的內存信息.
Address |
Offset |
After HeapAlloc() |
After malloc() |
During free() |
After HeapFree() |
Comments |
0x00320FD8 |
-40 |
0x01090009 |
0x01090009 |
0x01090009 |
0x0109005A |
Win32 heap info |
0x00320FDC |
-36 |
0x01090009 |
0x00180700 |
0x01090009 |
0x00180400 |
Win32 heap info |
0x00320FE0 |
-32 |
0xBAADF00D |
0x00320798 |
0xDDDDDDDD |
0x00320448 |
Ptr to next CRT heap block (allocated earlier in time) |
0x00320FE4 |
-28 |
0xBAADF00D |
0x00000000 |
0xDDDDDDDD |
0x00320448 |
Ptr to prev CRT heap block (allocated later in time) |
0x00320FE8 |
-24 |
0xBAADF00D |
0x00000000 |
0xDDDDDDDD |
0xFEEEFEEE |
Filename of malloc() call |
0x00320FEC |
-20 |
0xBAADF00D |
0x00000000 |
0xDDDDDDDD |
0xFEEEFEEE |
Line number of malloc() call |
0x00320FF0 |
-16 |
0xBAADF00D |
0x00000008 |
0xDDDDDDDD |
0xFEEEFEEE |
Number of bytes to malloc() |
0x00320FF4 |
-12 |
0xBAADF00D |
0x00000001 |
0xDDDDDDDD |
0xFEEEFEEE |
Type (0=Freed, 1=Normal, 2=CRT use, etc) |
0x00320FF8 |
-8 |
0xBAADF00D |
0x00000031 |
0xDDDDDDDD |
0xFEEEFEEE |
Request #, increases from 0 |
0x00320FFC |
-4 |
0xBAADF00D |
0xFDFDFDFD |
0xDDDDDDDD |
0xFEEEFEEE |
No mans land |
0x00321000 |
+0 |
0xBAADF00D |
0xCDCDCDCD |
0xDDDDDDDD |
0xFEEEFEEE |
The 8 bytes you wanted |
0x00321004 |
+4 |
0xBAADF00D |
0xCDCDCDCD |
0xDDDDDDDD |
0xFEEEFEEE |
The 8 bytes you wanted |
0x00321008 |
+8 |
0xBAADF00D |
0xFDFDFDFD |
0xDDDDDDDD |
0xFEEEFEEE |
No mans land |
0x0032100C |
+12 |
0xBAADF00D |
0xBAADF00D |
0xDDDDDDDD |
0xFEEEFEEE |
Win32 heap allocations are rounded up to 16 bytes |
0x00321010 |
+16 |
0xABABABAB |
0xABABABAB |
0xABABABAB |
0xFEEEFEEE |
Win32 heap bookkeeping |
0x00321014 |
+20 |
0xABABABAB |
0xABABABAB |
0xABABABAB |
0xFEEEFEEE |
Win32 heap bookkeeping |
0x00321018 |
+24 |
0x00000010 |
0x00000010 |
0x00000010 |
0xFEEEFEEE |
Win32 heap bookkeeping |
0x0032101C |
+28 |
0x00000000 |
0x00000000 |
0x00000000 |
0xFEEEFEEE |
Win32 heap bookkeeping |
0x00321020 |
+32 |
0x00090051 |
0x00090051 |
0x00090051 |
0xFEEEFEEE |
Win32 heap bookkeeping |
0x00321024 |
+36 |
0xFEEE0400 |
0xFEEE0400 |
0xFEEE0400 |
0xFEEEFEEE |
Win32 heap bookkeeping |
0x00321028 |
+40 |
0x00320400 |
0x00320400 |
0x00320400 |
0xFEEEFEEE |
Win32 heap bookkeeping |
0x0032102C |
+44 |
0x00320400 |
0x00320400 |
0x00320400 |
0xFEEEFEEE |
Win32 heap bookkeeping |