У меня есть приложение на 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);
}