Ungewöhnliche Heap-Größenbeschränkungen in VS2003 C ++
Ich besitze eine C ++ - App, die große Datenmengen verwendet, und habe beim Testen festgestellt, dass der Arbeitsspeicher knapp wird, obwohl noch genügend Arbeitsspeicher verfügbar ist. Ich habe den Code wie folgt auf einen Beispieltestfall reduziert.
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");
}
Wenn ich ein neues MFC-Projekt erstelle, diese Funktion einbinde und in InitInstance ausführe, funktioniert sie im Debug-Modus (Speicher wird wie erwartet zugewiesen) einwandfrei, schlägt jedoch im Release-Modus fehl (malloc gibt NULL zurück). Wenn ich einmal durch die Freigabe in die C-Laufzeiten gehe, wird meine Funktion eingeblendet. Ich erhalte Folgendes
// 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;
}
Aufruf von _nh_malloc_base
void * __cdecl _nh_malloc_base (size_t size, int nhFlag)
{
void * pvReturn;
// validate size
if (size > _HEAP_MAXREQ)
return NULL;
'
'
Und (size> _HEAP_MAXREQ) gibt true zurück und daher wird mein Speicher nicht zugewiesen. Wenn Sie die Größe überprüfen, werden die erwarteten 512 MB zurückgegeben, was darauf hindeutet, dass das Programm eine Verknüpfung zu einer anderen Laufzeitbibliothek mit einem viel kleineren _HEAP_MAXREQ herstellt. Wenn ich die VC ++ - Ordner nach _HEAP_MAXREQ durchsuche, wird die erwartete 0xFFFFFFE0 angezeigt, sodass ich nicht herausfinden kann, was hier passiert. Kennt jemand irgendwelche CRT-Änderungen oder -Versionen, die dieses Problem verursachen könnten, oder fehle ich etwas, das viel offensichtlicher ist?
Bearbeiten: Wie von Andreas vorgeschlagen, zeigt ein Blick auf diese Baugruppenansicht Folgendes:
--- 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)
Mit den Registern wie folgt;
EAX = 009C8AF0 EBX = FFFFFFFF ECX = 009C8A88 EDX = 00747365 ESI = 00430F80 EDI = 00430F80 EIP = 0040B160 ESP = 0013FDF4 EBP = 0013FFC0 EFL = 00000206
Der Vergleich scheint also mit der korrekten Konstante zu sein, d.h.
Zweite Bearbeitung: Problem war eigentlich in HeapAlloc, laut Andreas Beitrag. Der Wechsel zu einem neuen separaten Heap für große Objekte mithilfe von HeapCreate & HeapAlloc hat weder zur Behebung des Problems beigetragen, noch wurde versucht, VirtualAlloc mit verschiedenen Parametern zu verwenden. Einige weitere Experimente haben gezeigt, dass, wenn die Zuweisung eines großen Abschnitts des zusammenhängenden Speichers fehlschlägt, zwei kleinere Blöcke, die den gleichen Gesamtspeicher ergeben, in Ordnung sind. z.B. Wenn ein 300-MB-Malloc ausfällt, funktionieren 2 x 150-MB-Mallocs einwandfrei. Es sieht also so aus, als würde ich eine neue Array-Klasse brauchen, die in mehreren großen Speicherfragmenten leben kann, anstatt in einem einzelnen zusammenhängenden Block. Kein großes Problem, aber ich hätte heute ein bisschen mehr von Win32 erwartet.
Letzte Änderung: Das folgende Beispiel ergab 1,875 GB Speicherplatz, wenn auch nicht zusammenhängend
#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);
}