У меня есть приложение 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 возвращает ПУСТОЙ УКАЗАТЕЛЬ). Единственное продвижение посредством выпуска во время выполнения 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;
'
'
И (размер> _HEAP_MAXREQ) возвращает true, и следовательно моя память не становится выделенной. Помещение часов на размере возвращается с exptected 512 МБ, который предполагает, что программа связывается в другую библиотеку времени выполнения с намного меньшим _HEAP_MAXREQ. Grepping, который папки VC ++ для _HEAP_MAXREQ показывают ожидаемому 0xFFFFFFE0, таким образом, я не могу выяснить то, что происходит здесь. Кто-либо знает о каких-либо изменениях CRT или версиях, которые вызвали бы эту проблему, или я пропускаю что-то более очевидный путь?
Править: Как предложил Andreas, смотря на это при этом представлении блока показывает следующее;
--- 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 = 00 747 365 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 согласно сообщению Andreas. Изменение на новую отдельную "кучу" для больших объектов, использование HeapCreate & HeapAlloc, не помогли облегчить проблему, ни сделали попытку использовать VirtualAlloc с различными параметрами. Некоторое дальнейшее экспериментирование показало это, где выделение один большой раздел непрерывных сбоев памяти, два меньших блока, приводящие к той же общей памяти, в порядке. например, где 300 МБ malloc сбои, 2 x 150 МБ mallocs работают хорошо. Таким образом, похоже, что мне будет нужен новый класс массива, который может жить во многих великоватых фрагментах памяти, а не единственном непрерывном блоке. Не основная проблема, но я ожидал бы немного больше из 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);
}
Может ли быть такое, что отладчик разыгрывает вас в режиме release? Ни одиночный шаг, ни значения переменных не являются надежными в release-режиме.
Я попробовал ваш пример в VS2003 в режиме release, и при одиночном шаге сначала кажется, что код приземляется на строке return NULL
, но когда я продолжаю шаг, он в конце концов продолжается в HeapAlloc
, я бы предположил, что именно эта функция дает сбой, если посмотреть на дизассемблер if (size > _HEAP_MAXREQ)
и увидеть следующее:
00401078 cmp dword ptr [esp+4],0FFFFFFE0h
поэтому я не думаю, что это проблема с _HEAP_MAXREQ
.