2009-05-14 15 views
6

Escribí un programa C en Linux que mallocs la memoria, la ejecuté en un bucle y TOP no mostró ningún consumo de memoria.¿Algunos asignacióndores son flojos?

luego hice algo con esa memoria y TOP mostró el consumo de memoria.

Cuando realizo malloc, ¿realmente "obtengo memoria", o hay una administración de memoria "floja", que solo me da la memoria si/cuando la uso?

(También hay una opción que sólo conocen la parte superior sobre el consumo de memoria cuando lo uso, así que no estoy seguro de esto ..)

Gracias

Respuesta

15

En Linux, malloc peticiones de memoria con sbrk() o mmap(): en cualquier caso, su espacio de direcciones se expande inmediatamente, pero Linux no asigna páginas reales de memoria física hasta la primera escritura en la página en cuestión. Puede ver la expansión del espacio de direcciones en la columna VIRT, mientras que el uso real de la memoria física en RES.

+0

¿Es lo mismo para windows? – TStamper

+0

No estoy familiarizado con lo que Windows hace, lo siento. – bdonlan

+0

bdonlan: Correcto, pero deben tener cuidado con el efecto de de tenedor "* El niño no hereda las cerraduras de memoria de su matriz (MLOCK (2), mlockall (2)) " Cuál será la forma más carga de aplicación cuando mira en la parte superior – RandomNickName42

3

Sí, la memoria no está mapeada en su espacio de memoria a menos que lo toque. La memoria mallocing solo configurará las tablas de paginación para que sepan cuándo se produce una falla de página en la memoria asignada, la memoria debe estar mapeada.

0

¿Está utilizando las optimizaciones del compilador? ¿Tal vez el optimizador ha eliminado la asignación ya que no está utilizando los recursos asignados?

+0

Gracias Ryan, miré el binario con desensamblador y la llamada 'malloc' estaba allí. –

+0

+1 para contrarrestar los votos negativos. Esta es una buena respuesta para la pregunta tal como es. –

+1

El compilador no puede eliminar una función sin una implementación visible o una que pueda tener efectos secundarios. – BeeOnRope

3

Esto comienza un poco fuera de tema (y luego lo relacionaré con su pregunta), pero lo que está sucediendo es similar a lo que sucede cuando bifurca un proceso en Linux. Al bifurcar hay un mecanismo llamado copiar sobre escritura que solo copia el espacio de memoria para el nuevo proceso cuando la memoria también está escrita. De esta forma, si el ejecutor del proceso bifurcado es un nuevo programa de inmediato, habrá ahorrado la sobrecarga de copiar la memoria original de los programas.

Volviendo a su pregunta, la idea es similar. Como han señalado otros, al solicitar la memoria obtiene el espacio de la memoria virtual de inmediato, pero las páginas reales solo se asignan cuando se les escribe.

¿Cuál es el propósito de esto? Básicamente hace que la memoria mallocing sea una operación de tiempo más o menos constante Big O (1) en lugar de una operación Big O (n) (similar a la forma en que se distribuye el programador de Linux, en lugar de hacerlo en un gran bloque).

Para demostrar lo que quiero decir que hice el siguiente experimento:

[email protected]:~/test_code$ time ./bigmalloc 

real 0m0.005s 
user 0m0.000s 
sys 0m0.004s 
[email protected]:~/test_code$ time ./deadbeef 

real 0m0.558s 
user 0m0.000s 
sys 0m0.492s 
[email protected]:~/test_code$ time ./justwrites 

real 0m0.006s 
user 0m0.000s 
sys 0m0.008s 

El programa bigmalloc asigna 20 millones de enteros, pero no hace nada con ellos. deadbeef escribe una int en cada página, lo que da como resultado 19531 escrituras y justwrites asigna 19531 ints y las elimina. Como puede ver, deadbeef tarda aproximadamente 100 veces más en ejecutarse que bigmalloc y unas 50 veces más que Justwrite.

#include <stdlib.h>  

int main(int argc, char **argv) { 

int *big = malloc(sizeof(int)*20000000); // allocate 80 million bytes 

return 0; 

} 

.

#include <stdlib.h>  

int main(int argc, char **argv) { 

int *big = malloc(sizeof(int)*20000000); // allocate 80 million bytes 

// immediately write to each page to simulate all at once allocation 

// assuming 4k page size on 32bit machine 

for (int* end = big + 20000000; big < end; big+=1024) *big = 0xDEADBEEF ;  

return 0; 

} 

.

#include <stdlib.h> 

int main(int argc, char **argv) { 

int *big = calloc(sizeof(int),19531); // number of writes 

return 0; 
} 
+0

¡Respuesta impresionante, gracias! (Estaba bastante sorprendido de saber que 0xDEADBEAF es un término conocido http://en.wikipedia.org/wiki/Hexspeak) –

0

La función se denomina overcommit - kernel "promesas" que la memoria mediante el aumento del tamaño del segmento de datos, pero no asigna memoria física a la misma.Cuando toca una dirección en ese nuevo espacio, la página del proceso falla en el kernel, que luego intenta asignarle páginas físicas.

0

Sí, tenga en cuenta los VirtualAlloc banderas,

MEM_RESERVE 
MEM_COMMIT 

.

Heh, pero para Linux, o cualquier sistema POSIX/BSD/SVR#, vfork(), ha existido desde hace mucho tiempo y ofrece una funcionalidad similar.

La función vfork() difiere de tenedor() sólo en ese proceso el niño puede compartir código y datos con el proceso de llamando al (proceso padre). Este acelera significativamente la actividad de clonación con riesgo para la integridad del proceso padre si vfork() se usa incorrectamente.

El uso de vfork() para cualquier propósito excepto como un preludio a una llamada inmediata a una función de la familia exec , o para _exit(), no se aconseja.

La función vfork() se puede utilizar para crear nuevos procesos sin totalmente copiar el espacio de direcciones del proceso antiguo . Si un proceso bifurcado es simplemente va a llamar a exec, no se utiliza el espacio de datos copiado del padre al hijo por fork(). Esto es particularmente ineficaz en un entorno paginado , haciendo vfork() particularmente útil. Dependiendo de el tamaño del espacio de datos del padre, vfork() puede dar lugar a una importante mejora en el rendimiento de con respecto a fork().

Cuestiones relacionadas