关于LFH堆 .

这段时间项目中遇到了内存碎片的问题,在双核四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 plain copy print ?
  1. BOOL SetLowFragmentHeaps()  
  2. {  
  3.  enum {  
  4.         LFHHeap = 2  
  5.     };  
  6.   
  7.     DWORD   dwHeapNum    = GetProcessHeaps(0, NULL);  
  8.     HANDLE* pHeapHandles = new HANDLE[dwHeapNum];  
  9.     DWORD   dwNum        = GetProcessHeaps(dwHeapNum, pHeapHandles);  
  10.   
  11.     ULONG   lHeapType    = LFHHeap;  
  12.     BOOL    bSuccess     = TRUE;  
  13.     HANDLE  hCrtHeap     = (HANDLE)_get_heap_handle();  
  14.   
  15.     while ( --dwNum )  
  16.     {     
  17.         BOOL bRet = HeapSetInformation( pHeapHandles[dwNum],   
  18.                                         HeapCompatibilityInformation,  
  19.                                         &lHeapType, sizeof(lHeapType) );  
  20.         // 对于crt堆,必须设为LFH堆!   
  21.         if ( !bRet && hCrtHeap == pHeapHandles[dwNum] )  
  22.         {  
  23.             bSuccess = FALSE;  
  24.             break;  
  25.         }  
  26.     }  
  27.   
  28.     delete[] pHeapHandles;  
  29.     return bSuccess;  
  30. }  

最后谈一下对自定义内存池的看法,很多人(包括之前的我)都一直认为要想实现高效的内存分配,就得自己实现一个内存池,潜意识里觉得系统预提供的设施不足以满足我们的性能需求,但是就我遇到的所有情形而言,系统的提供设施(无论是锁还是内存池或是其他)往往要优于自己的所谓的高效实现,经过百般调优的系统设施在绝大多数情形下足以满足我们的需求,所以当有系统为我们提供好高效健壮的实现,又何必要自己重新发明轮子呢?

你可能感兴趣的:(关于LFH堆 .)