代码之家  ›  专栏  ›  技术社区  ›  SmacL

VS2003 C++中异常堆大小的限制

  •  6
  • SmacL  · 技术社区  · 16 年前

    我有一个C++应用程序,它使用大量的数据数组,并且在测试时注意到内存不足,而仍然有足够的内存可用。我已经将代码简化为一个示例测试用例,如下所示:

    void    MemTest()
    {
        size_t Size = 500*1024*1024;  // 512mb
        if (Size > _HEAP_MAXREQ)
            TRACE("Invalid Size");
        void * mem = malloc(Size);
        if (mem == NULL)
            TRACE("allocation failed");
    
    }
    

    如果我创建一个新的MFC项目,包括这个函数,并从inittInstance运行它,它在调试模式(内存按预期分配)下工作正常,但在释放模式下失败(malloc返回空值)。单步执行释放到C运行时,我的函数被内联,我得到以下内容

    // malloc.c
    
    void * __cdecl _malloc_base (size_t size)
    
    {
            void *res = _nh_malloc_base(size, _newmode);
    
            RTCCALLBACK(_RTC_Allocate_hook, (res, size, 0));
    
            return res;
    }
    

    呼叫\u nh \u malloc \u base

    void * __cdecl _nh_malloc_base (size_t size, int nhFlag)
    {
            void * pvReturn;
    
            //  validate size
            if (size > _HEAP_MAXREQ)
                return NULL;
    '
    '
    

    并且(size>_heap_maxreq)返回true,因此不会分配内存。将一个表的大小放回expected 512MB,这意味着该程序链接到一个不同的运行时库,运行时库的堆要小得多。对堆maxreq的vc++文件夹进行grepping会显示预期的0xffffefe0,所以我不知道这里发生了什么。有人知道会导致这个问题的任何CRT更改或版本,或者我遗漏了更明显的东西吗?

    编辑: 正如安德烈亚斯所建议的,在这个组装视图下看这个,可以看到以下内容:

    --- f:\vs70builds\3077\vc\crtbld\crt\src\malloc.c ------------------------------
    _heap_alloc:
    0040B0E5  push        0Ch  
    0040B0E7  push        4280B0h 
    0040B0EC  call        __SEH_prolog (40CFF8h) 
    0040B0F1  mov         esi,dword ptr [size] 
    0040B0F4  cmp         dword ptr [___active_heap (434660h)],3 
    0040B0FB  jne         $L19917+7 (40B12Bh) 
    0040B0FD  cmp         esi,dword ptr [___sbh_threshold (43464Ch)] 
    0040B103  ja          $L19917+7 (40B12Bh) 
    0040B105  push        4    
    0040B107  call        _lock (40DE73h) 
    0040B10C  pop         ecx  
    0040B10D  and         dword ptr [ebp-4],0 
    0040B111  push        esi  
    0040B112  call        __sbh_alloc_block (40E736h) 
    0040B117  pop         ecx  
    0040B118  mov         dword ptr [pvReturn],eax 
    0040B11B  or          dword ptr [ebp-4],0FFFFFFFFh 
    0040B11F  call        $L19916 (40B157h) 
    $L19917:
    0040B124  mov         eax,dword ptr [pvReturn] 
    0040B127  test        eax,eax 
    0040B129  jne         $L19917+2Ah (40B14Eh) 
    0040B12B  test        esi,esi 
    0040B12D  jne         $L19917+0Ch (40B130h) 
    0040B12F  inc         esi  
    0040B130  cmp         dword ptr [___active_heap (434660h)],1 
    0040B137  je          $L19917+1Bh (40B13Fh) 
    0040B139  add         esi,0Fh 
    0040B13C  and         esi,0FFFFFFF0h 
    0040B13F  push        esi  
    0040B140  push        0    
    0040B142  push        dword ptr [__crtheap (43465Ch)] 
    0040B148  call        dword ptr [__imp__HeapAlloc@12 (425144h)] 
    0040B14E  call        __SEH_epilog (40D033h) 
    0040B153  ret              
    $L19914:
    0040B154  mov         esi,dword ptr [ebp+8] 
    $L19916:
    0040B157  push        4    
    0040B159  call        _unlock (40DDBEh) 
    0040B15E  pop         ecx  
    $L19929:
    0040B15F  ret              
    _nh_malloc:
    0040B160  cmp         dword ptr [esp+4],0FFFFFFE0h 
    0040B165  ja          _nh_malloc+29h (40B189h) 
    

    登记册如下:

    eax=009c8af0 ebx=ffffffff ecx=009c8a88 edx=00747365 esi=00430f80 edi=00430f80 eip=0040b160 esp=0013ff4 ebp=0013ffc0 efl=00000206

    因此,比较似乎与正确的常量相反,即@040b160 cmp dword ptr[esp+4],0fffffe0h,也就是esp+4=0013fff8=1f400000(我的512MB)

    第二次编辑: 根据安德烈亚斯的文章,这个问题实际上是在heapalloc中。使用HeapCreate和HeapAlloc为大型对象更改为新的独立堆,既没有帮助缓解问题,也没有尝试将VirtualAlloc与各种参数一起使用。一些进一步的实验表明,如果分配一个大的连续内存段失败,那么两个产生相同总内存的较小的块就可以了。例如,当300MB malloc发生故障时,2 x 150MB mallocs工作正常。所以看起来我需要一个新的数组类,它可以存在于许多较大的内存片段中,而不是一个连续的块中。这不是一个大问题,但在当今这个时代,我对Win32的期望会更高一些。

    最后编辑: 以下产生了1.875GB的空间,尽管不是连续的

    #define TenMB 1024*1024*10
    
    void    SmallerAllocs()
    {
    
        size_t Total = 0;
        LPVOID  p[200];
        for (int i = 0; i < 200; i++)
        {
            p[i] = malloc(TenMB);
            if (p[i])
                Total += TenMB; else
                break;
        }
        CString Msg;
        Msg.Format("Allocated %0.3lfGB",Total/(1024.0*1024.0*1024.0));
        AfxMessageBox(Msg,MB_OK);
    }
    
    1 回复  |  直到 16 年前
        1
  •  1
  •   Andreas Brinck    16 年前

    可能是因为在发布模式下,调试程序正在对您播放一个技巧?在释放模式下,单步进和变量值都不可靠。

    我在VS2003中以发布模式尝试过您的示例,当单步执行时,它一开始看起来就像代码正在登录到 return NULL 但当我继续前进时,它最终会继续前进 HeapAlloc ,我想是这个函数失败了,看看反汇编 if (size > _HEAP_MAXREQ) 显示以下内容:

    00401078  cmp         dword ptr [esp+4],0FFFFFFE0h 
    

    所以我认为这不是问题 _HEAP_MAXREQ .