這段時間項目中遇到了記憶體片段的問題,在雙核四G的機器上,明明進程的虛擬記憶體只用了500M左右,但new卻會拋出bad_alloc異常,用process explorer查看進程的記憶體的使用方式,發現雖然virtual memory僅為500M左右,但已指派的地址空間virtual size卻已將近2G了,然後又用vmmap查看進程地址空間分配情況,發現堆記憶體配置佔用比很高,而且記憶體位址中,總是兩個reserve中總是夾著一個commit,很顯然,這是記憶體位址空間嚴重片段的表徵。記憶體片段的問題慣常的解決方案是使用記憶體池,最簡單的記憶體池就是預先分配好大塊連續記憶體,然後使用時拆分成最頻繁使用固定大小的小塊記憶體。但是我們這個項目中由於用到一些比較大且較複雜的開源庫,要想定位相關代碼的位置,無論從時間和精力上都比較困難。所以,我決定從執行階段程式庫標準記憶體配置常式上著手。原本的方法是實現一個記憶體池,然後替換掉crt標準的記憶體配置常式。然而,閱讀crt記憶體相關代碼發現,malloc實際是通過windows堆api實現的,我們知道windows堆架構分為前端和後端兩部分,前端的堆分為兩種,一種是旁查列表實現的效能較高的LAL堆,還有一種是LFH堆。LFH堆顧名思義就是底片段堆,而LFH對於頻繁分配小記憶體塊的情形可以有效降低記憶體片段。而crt中使用的crt堆預設是LAL堆,於是抱著姑且一試的心態,我將crt堆設成了LFH堆,再次運行程式,驚喜的發現虛擬記憶體地址空間的利用效率接近100%,原本進程中只能儲存500個會話,現今可以儲存5000個會話,足以滿足項目需求。下面是我寫的一個簡單設定LFH堆類型的代碼:
[cpp]
view plaincopyprint?
- BOOL SetLowFragmentHeaps()
- {
- enum {
- LFHHeap = 2
- };
-
- DWORD dwHeapNum = GetProcessHeaps(0, NULL);
- HANDLE* pHeapHandles = new HANDLE[dwHeapNum];
- DWORD dwNum = GetProcessHeaps(dwHeapNum, pHeapHandles);
-
- ULONG lHeapType = LFHHeap;
- BOOL bSuccess = TRUE;
- HANDLE hCrtHeap = (HANDLE)_get_heap_handle();
-
- while ( --dwNum )
- {
- BOOL bRet = HeapSetInformation( pHeapHandles[dwNum],
- HeapCompatibilityInformation,
- &lHeapType, sizeof(lHeapType) );
- // 對於crt堆,必須設為LFH堆!
- if ( !bRet && hCrtHeap == pHeapHandles[dwNum] )
- {
- bSuccess = FALSE;
- break;
- }
- }
-
- delete[] pHeapHandles;
- return bSuccess;
- }
BOOL SetLowFragmentHeaps(){ enum { LFHHeap = 2 }; DWORD dwHeapNum = GetProcessHeaps(0, NULL); HANDLE* pHeapHandles = new HANDLE[dwHeapNum]; DWORD dwNum = GetProcessHeaps(dwHeapNum, pHeapHandles); ULONG lHeapType = LFHHeap; BOOL bSuccess = TRUE; HANDLE hCrtHeap = (HANDLE)_get_heap_handle(); while ( --dwNum ) { BOOL bRet = HeapSetInformation( pHeapHandles[dwNum], HeapCompatibilityInformation, &lHeapType, sizeof(lHeapType) ); // 對於crt堆,必須設為LFH堆! if ( !bRet && hCrtHeap == pHeapHandles[dwNum] ) { bSuccess = FALSE; break; } } delete[] pHeapHandles; return bSuccess;}
最後談一下對自訂記憶體池的看法,很多人(包括之前的我)都一直認為要想實現高效的記憶體配置,就得自己實現一個記憶體池,潛意識裡覺得系統預提供的設施不足以滿足我們的效能需求,但是就我遇到的所有情形而言,系統的提供設施(無論是鎖還是記憶體池或是其他)往往要優於自己的所謂的高效實現,經過百般調優的系統設施在絕大多數情形下足以滿足我們的需求,所以當有系統為我們提供好高效健壯的實現,又何必要自己重新發明輪子呢?