2010-03-18 16 views
6

Tengo una aplicación C++ que usa grandes conjuntos de datos y me he dado cuenta al probar que se está quedando sin memoria, mientras todavía hay mucha memoria disponible. Reduje el código a un caso de prueba de ejemplo de la siguiente manera;Limitaciones inusuales de tamaño de almacenamiento dinámico en VS2003 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"); 

} 

Si creo un nuevo proyecto de MFC, incluyen esta función, y ejecutarlo desde InitInstance, que funciona bien en modo de depuración (memoria asignada como se esperaba), pero no en modo de lanzamiento (malloc devuelve NULL). paso a paso único mediante una liberación en los tiempos de ejecución de C, mi función inline se me sale el siguiente

// 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; 
} 

Calling _nh_malloc_base

void * __cdecl _nh_malloc_base (size_t size, int nhFlag) 
{ 
     void * pvReturn; 

     // validate size 
     if (size > _HEAP_MAXREQ) 
      return NULL; 
' 
' 

Y (tamaño> _HEAP_MAXREQ) devuelve verdadero y por lo tanto, mi memoria no consigue asignado. Poner un reloj en el tamaño vuelve con los 512MB exptected, lo que sugiere que el programa está enlazando en una biblioteca de tiempo de ejecución diferente con un _HEAP_MAXREQ mucho más pequeño. Al expandir las carpetas de VC++ para _HEAP_MAXREQ se muestra el esperado 0xFFFFFFE0, por lo que no puedo entender qué está sucediendo aquí. ¿Alguien sabe de algún cambio CRT o versiones que causarían este problema, o me estoy perdiendo algo mucho más obvio?

Edit: Según lo sugerido por Andreas, al mirar esto bajo esta vista de ensamblaje se muestra lo siguiente;

--- 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 [[email protected] (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) 

Con los registros de la siguiente manera;

EAX = 009C8AF0 EBX = FFFFFFFF ECX ​​= 009C8A88 EDX = 00747365 ESI = 00430F80 EDI = 00430F80 EIP = 0040B160 ESP = 0013FDF4 EBP = 0013FFC0 EFL = 00000206

Así que el comparar no parecen estar en contra de la constante correcta , es decir, @ 040B160 cmp dword ptr [esp + 4], 0FFFFFFE0h, también esp + 4 = 0013FDF8 = 1F400000 (mi 512mb)

Segunda edición: problema era en realidad en HeapAlloc, como por mensaje Andreas'. Cambiar a un nuevo montón separado para objetos grandes, usando HeapCreate & HeapAlloc, no ayudó a aliviar el problema ni intentó utilizar VirtualAlloc con varios parámetros. Algunos experimentos adicionales han demostrado que cuando la asignación de una gran sección de la memoria contigua falla, dos bloques más pequeños que producen la misma memoria total están bien. p.ej. donde falla un malloc de 300MB, 2 mallocs de 150MB funcionan bien. Por lo tanto, parece que necesitaré una nueva clase de matriz que pueda vivir en varios fragmentos de memoria grandes en lugar de un único bloque contiguo. No es un problema importante, pero hubiera esperado un poco más de Win32 en este día y edad.

Última edición: El siguiente produjo 1.875GB del espacio, aunque no contiguos

#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); 
} 
+2

Si observa el desmontaje, ¿qué valor se utiliza para '_HEAP_MAXREQ' en la comparación'> '? –

Respuesta

1

Que sea el reparto en el que el depurador está jugando una mala pasada en la liberación modo? Ni el paso único ni los valores de las variables son confiables en el modo de lanzamiento.

Probé tu ejemplo en VS2003 en modo de lanzamiento, y cuando solo paso parece al principio que el código está aterrizando en la línea return NULL, pero cuando continúo pisando continúa eventualmente en HeapAlloc, supongo que es esto función que está fallando, mirando el desmontaje if (size > _HEAP_MAXREQ) revela lo siguiente:

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

así que no creo que sea un problema con _HEAP_MAXREQ.

+0

Intenté localizar más allá de la devolución NULL, y obtuve resultados similares, con la falla ocurrida en mi aplicación original en 'return HeapAlloc (_crtheap, 0, size);' en malloc.c, básicamente mostrando que mi programa de prueba es defectuoso. Probaré con otro programa de prueba y volveré a publicarlo, ya que el problema aún ocurre en mi aplicación principal. –

+1

@Shane 512 MB es bastante memoria, sin duda existe la posibilidad de que no tenga tanta memoria * contigua * en el espacio de direcciones virtuales de su proceso. (Las asignaciones pueden haber sido colocadas de una manera diferente en el modo de depuración, explicando por qué funciona allí). –

+0

Estoy llegando a la conclusión yo mismo. Probé otro programa de prueba con 2 mallocs de 512mb cada uno, que funcionó, mientras que mi aplicación está tratando de asignar 2 bloques de 300mb y fallar. Supongo que se trata de una fragmentación de montón relacionada, y tendré que revisar cómo uso el montón en mi aplicación. Gracias por todos los comentarios. –

Cuestiones relacionadas