usar como c++ windows-7 dynamic-memory-allocation allocator

c++ - como - Por qué la memoria eliminada no puede ser reutilizada



malloc sizeof (5)

Estoy usando C ++ en Windows 7 con MSVC 9.0, y también he podido probar y reproducir en Windows XP SP3 con MSVC 9.0.

Si asigno 1 GB de objetos de 0,5 MB de tamaño, cuando los elimino, todo está bien y se comporta como se esperaba. Sin embargo, si asigno 1 GB de objetos de 0,25 MB cuando los elimino, la memoria permanece reservada (amarilla en el Monitor de espacio de direcciones ) y, a partir de ese momento, solo podrá utilizarse para asignaciones inferiores a 0,25 MB.

Este código simple le permitirá probar ambos escenarios al cambiar qué estructura es typedef''d. Después de haber asignado y eliminado las estructuras, asignará 1 GB de búferes de carga de 1 MB para ver si los búferes de caracteres utilizarán la memoria que una vez ocuparon las estructuras.

struct HalfMegStruct { HalfMegStruct():m_Next(0){} /* return the number of objects needed to allocate one gig */ static int getIterations(){ return 2048; } int m_Data[131071]; HalfMegStruct* m_Next; }; struct QuarterMegStruct { QuarterMegStruct():m_Next(0){} /* return the number of objects needed to allocate one gig */ static int getIterations(){ return 4096; } int m_Data[65535]; QuarterMegStruct* m_Next; }; // which struct to use typedef QuarterMegStruct UseType; int main() { UseType* first = new UseType; UseType* current = first; for ( int i = 0; i < UseType::getIterations(); ++i ) current = current->m_Next = new UseType; while ( first->m_Next ) { UseType* temp = first->m_Next; delete first; first = temp; } delete first; for ( unsigned int i = 0; i < 1024; ++i ) // one meg buffer, i''m aware this is a leak but its for illustrative purposes. new char[ 1048576 ]; return 0; }

A continuación puede ver mis resultados desde dentro de Address Space Monitor . Permítanme enfatizar que la única diferencia entre estos dos resultados finales es el tamaño de las estructuras asignadas hasta el marcador de 1 GB .

Esto me parece un problema bastante serio, y que muchas personas podrían estar sufriendo y ni siquiera saberlo.

  • Entonces, ¿esto es por diseño o debería ser considerado un error?
  • ¿Puedo hacer que pequeños objetos eliminados sean realmente gratuitos para ser utilizados por asignaciones más grandes?
  • Y más por curiosidad, ¿una Mac o una máquina Linux sufren el mismo problema?

No puedo afirmar de manera positiva que este sea el caso, pero esto se parece a la fragmentación de la memoria (en una de sus muchas formas). El asignador (malloc) puede mantener cubos de diferentes tamaños para permitir una asignación rápida, después de liberar la memoria, en lugar de devolverla directamente al sistema operativo, mantiene los depósitos para que las asignaciones posteriores del mismo tamaño puedan procesarse desde el mismo recuerdo. Si este es el caso, la memoria estaría disponible para asignaciones adicionales del mismo tamaño.

Este tipo de optimización generalmente está deshabilitado para objetos grandes , ya que requiere reservar memoria incluso si no se usa. Si el umbral está entre tus dos tamaños, eso explicaría el comportamiento.

Tenga en cuenta que si bien puede parecer extraño, en la mayoría de los programas (no de prueba, pero en la vida real) se repiten los patrones de uso de la memoria: si solicitó 100k bloques una vez, la mayoría de las veces lo hará de nuevo. . Y mantener la memoria reservada puede mejorar el rendimiento y reducir la fragmentación que se obtendría de todas las solicitudes otorgadas desde el mismo segmento.

Si quieres invertir algo de tiempo, puedes aprender cómo funciona tu asignador analizando el comportamiento. Escriba algunas pruebas, que adquirirán el tamaño X, libérelo, luego adquiera el tamaño Y y luego muestre el uso de la memoria. Corrija el valor de X y juegue con Y. Si las solicitudes para ambos tamaños se otorgan desde los mismos depósitos, no tendrá memoria reservada / no utilizada (imagen a la izquierda), mientras que cuando se otorguen tamaños desde diferentes cubos verá el efecto en la imagen de la derecha.

Normalmente no codigo Windows, y ni siquiera tengo Windows 7, así que no puedo afirmar de manera positiva que este es el caso, pero parece que sí.


Puedo confirmar el mismo comportamiento con g ++ 4.4.0 en Windows 7, por lo que no está en el compilador. De hecho, el programa falla cuando getIterations() devuelve 3590 o más. ¿Obtiene el mismo límite? Esto parece un error en la asignación de memoria del sistema de Windows. Está muy bien que las almas conocedoras hablen sobre la fragmentación de la memoria, pero todo se borró aquí, por lo que el comportamiento observado definitivamente no debería suceder.


Usando su código realicé su prueba y obtuve el mismo resultado. Sospecho que David Rodríguez tiene razón en este caso.

Ejecuté la prueba y obtuve el mismo resultado que tú. Parece que puede haber este comportamiento "cubo" pasando.

Probé dos pruebas diferentes también. En lugar de asignar 1GB de datos usando buffers de 1MB asigné la misma manera que la primera vez que se asignó la memoria después de eliminarla. La segunda prueba que asigné al buffer de medio meg limpió y luego asignó el búfer de metr del quater, agregando hasta 512MB para cada uno. Ambas pruebas tuvieron el mismo resultado de memoria al final, solo a 512 se le asignó una gran cantidad de memoria reservada.

Como David menciona, la mayoría de las aplicaciones tienden a hacer una asignación del mismo tamaño. Uno puede ver claramente por qué esto podría ser un problema.

Quizás la solución a esto es que si asigna muchos objetos más pequeños de esta manera, sería mejor asignar un gran bloque de memoria y administrarlo usted mismo. Luego, cuando hayas terminado, libera el bloque grande.



Hablé con algunas autoridades sobre el tema (Greg, si estás afuera, saluda, D) y puedo confirmar que lo que David está diciendo es básicamente correcto.

A medida que el montón crece en la primera pasada de asignación de ~ 0.25MB de objetos, el montón se reserva y se compromete la memoria. A medida que el montón se reduce en el pase de eliminación, se libera a cierto ritmo, pero no necesariamente libera los rangos de direcciones virtuales que reservó en el pase de asignación. En el último pase de asignación, las asignaciones de 1MB están pasando por alto el montón debido a su tamaño y así comienzan a competir con el montón de VA.

Tenga en cuenta que el montón está reservando el VA, no manteniéndolo comprometido. VirtualAlloc y VirtualFree pueden ayudar a explicar lo diferente si tiene curiosidad. Este hecho no resuelve el problema con el que se encontró, que es que el proceso se quedó sin espacio de direcciones virtuales .