现在的位置: 首页 > 综合 > 正文

关于LFH堆

2013年12月07日 ⁄ 综合 ⁄ 共 1461字 ⁄ 字号 评论关闭

这段时间项目中遇到了内存碎片的问题,在双核四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堆类型的代码:

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;
}

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

----------------------------------------------

作者 hanzz2007 , 转载请注明出处

抱歉!评论已关闭.