Необычные ограничения размера кучи в VS2003 C ++ - PullRequest
6 голосов
/ 18 марта 2010

У меня есть приложение на 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, включаю эту функцию и запускаю его из InitInstance, он отлично работает в режиме отладки (память выделяется, как и ожидалось), но не работает в режиме выпуска (malloc возвращает NULL). Пошаговое выполнение релиза во время выполнения 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;
}

Вызов _nh_malloc_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, и, следовательно, моя память не выделяется. Установка часов по размеру возвращается с выделенными 512 МБ, что говорит о том, что программа подключается к другой библиотеке времени выполнения с гораздо меньшим _HEAP_MAXREQ. Свертывание папок VC ++ для _HEAP_MAXREQ показывает ожидаемый 0xFFFFFFE0, поэтому я не могу понять, что здесь происходит. Кто-нибудь знает о каких-либо изменениях или версиях ЭЛТ, которые могут вызвать эту проблему, или я упускаю что-то более очевидное?

Редактировать: Как предположил Андреас, при взгляде на это в этом представлении сборки показано следующее;

--- 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 = 0013FDF4 EBP = 0013FFC0 EFL = 00000206

Таким образом, сравнение, по-видимому, относится к правильной константе, то есть @ 040B160 cmp dword ptr [esp + 4], 0FFFFFFE0h, также esp + 4 = 0013FDF8 = 1F400000 (мои 512 МБ)

Второе редактирование: На самом деле проблема была в HeapAlloc, согласно сообщению Андреаса. Переход на новую отдельную кучу для больших объектов с использованием HeapCreate & HeapAlloc не помог решить проблему и не попытался использовать VirtualAlloc с различными параметрами. Некоторые дальнейшие эксперименты показали, что в случае сбоя при выделении одного большого раздела смежной памяти, два меньших блока, дающие одинаковую общую память, в порядке. например при сбое malloc 300 МБ нормально работают 2 x 150 МБ памяти. Так что, похоже, мне понадобится новый класс массива, который может находиться в нескольких фрагментах большой памяти, а не в одном непрерывном блоке. Не большая проблема, но я бы ожидал немного большего от Win32 в наши дни.

Последнее редактирование: В результате было выделено 1,875 ГБ свободного места, хотя и несмежно

#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 Ответ

1 голос
/ 18 марта 2010

Может быть, актерский отладчик подшучивает над тобой в режиме релиза? Ни один шаг, ни значения переменных не являются надежными в режиме релиза.

Я попробовал ваш пример в VS2003 в режиме выпуска, и при однократном пошаговом выполнении он сначала выглядит так, как будто код попадает на строку return NULL, но когда я продолжаю пошаговое выполнение, он в конечном итоге переходит в HeapAlloc, я бы предположил что эта функция не работает, глядя на разборку if (size > _HEAP_MAXREQ) показывает следующее:

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

так что я не думаю, что это проблема с _HEAP_MAXREQ.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...