J’ai une application C ++ qui utilise de grands tableaux de données et j’ai constaté, tout en testant, qu’il manquait de mémoire, alors qu’il rest encore beaucoup de mémoire. J’ai réduit le code à un exemple de scénario de test comme suit;
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"); }
Si je crée un nouveau projet MFC, inclut cette fonction et l’exécute à partir de InitInstance, il fonctionne correctement en mode débogage (la mémoire est allouée comme prévu), mais échoue en mode de publication (malloc renvoie NULL). Passage unique dans les temps d’exécution C, ma fonction est en ligne, je reçois ce qui suit
// 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; }
Appeler _nh_malloc_base
void * __cdecl _nh_malloc_base (size_t size, int nhFlag) { void * pvReturn; // validate size if (size > _HEAP_MAXREQ) return NULL; ' '
Et (size> _HEAP_MAXREQ) renvoie true et par conséquent ma mémoire n’est pas allouée. La mise en place d’une montre revient avec 512 Mo, ce qui suggère que le programme est lié à une bibliothèque d’exécution différente avec un _HEAP_MAXREQ beaucoup plus petit. Grepping les dossiers VC ++ pour _HEAP_MAXREQ affiche le 0xFFFFFFE0 attendu, donc je ne peux pas comprendre ce qui se passe ici. Quelqu’un sait-il que des modifications ou des versions du tube cathodique pourraient causer ce problème, ou est-ce que je manque quelque chose de plus évident?
Edit: Comme suggéré par Andreas, regarder sous cette vue d’assemblage montre ce qui suit;
--- 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)
Avec les registres comme suit;
EAX = 009C8AF0 EBX = FFFFFFFF ECX = 009C8A88 EDX = 00747365 ESI = 00430F80 EDI = 00430F80 EIP = 0040B160 ESP = 0013FDF4 EBP = 0013FFC0 EFL = 00000206
La comparaison semble donc correspondre à la constante correcte, c’est-à-dire @ 040B160 cmp dword ptr [esp + 4], 0FFFFFFEOh, également esp + 4 = 0013FDF8 = 1F400000 (my 512 Mo)
Deuxième édition: le problème était en réalité dans HeapAlloc, selon le post d’Andreas. Le passage à un nouveau segment distinct pour les gros objects, à l’aide de HeapCreate & HeapAlloc, n’a pas permis de résoudre le problème, pas plus que la tentative d’utilisation de VirtualAlloc avec divers parameters. Certaines expériences plus poussées ont montré que, lorsque l’allocation d’une grande partie de la mémoire contiguë échoue, deux blocs plus petits générant la même mémoire totale sont acceptables. par exemple, lorsqu’un malloc de 300 Mo échoue, 2 mallocs de 150 Mo fonctionnent correctement. Il semble donc qu’il me faut une nouvelle classe array pouvant vivre dans plusieurs fragments de mémoire biggish plutôt que dans un seul bloc contigu. Pas un problème majeur, mais je me serais attendu à un peu plus de Win32 de nos jours.
Dernière modification: Ce qui suit a donné 1,875 Go d’espace, bien que non contigu
#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); }
Est-ce que c’est le casting que le débogueur vous joue un tour en mode release? Ni le pas à pas simple ni les valeurs de variables ne sont fiables en mode publication.
J’ai essayé votre exemple dans VS2003 en mode de publication, et quand on marche pas à pas, on dirait que le code atterrit sur la ligne de return NULL
, mais quand je continue à marcher dans HeapAlloc
, je suppose que c’est cette fonction qui échoue. , en regardant le déassembly if (size > _HEAP_MAXREQ)
révèle ce qui suit:
00401078 cmp dword ptr [esp+4],0FFFFFFE0h
donc je ne pense pas que ce soit un problème avec _HEAP_MAXREQ
.