Creo que tu problema original fue que malloc
no pudo asignar la memoria solicitada en su sistema.
Por qué sucedió esto es específico de su sistema.
Cuando se carga un proceso, se le asigna memoria hasta una determinada dirección que es el punto de interrupción del sistema para el proceso. Más allá de esa dirección, la memoria no está asignada para el proceso. Entonces, cuando el proceso "llega" al punto de "interrupción", solicita más memoria del sistema y una forma de hacerlo es a través de la llamada al sistema sbrk
malloc
haría eso debajo del capó pero en su sistema por alguna razón falló.
Puede haber muchas razones para esto, por ejemplo:
1) Creo que en Linux hay un límite para el tamaño máximo de memoria. Creo que es ulimit
y tal vez acertaste eso. Comprobar si está ajustado a un límite
2) Quizás su sistema estaba demasiado cargado
3) Su programa hace una mala gestión de la memoria y termina con la memoria fragmentada, así que malloc
no se puede obtener el tamaño de fragmento que solicitó.
4) Su programa corrompe el malloc
estructuras de datos internas, es decir, mal uso del puntero
etc.
El montón suele ser tan grande como la memoria virtual direccionable de su arquitectura.
Debe verificar los límites actuales de su sistema con el ulimit -a
comando y busque esta línea max memory size (kbytes, -m) 3008828
, esta línea en mi OpenSuse 11.4 x86_64 con ~3.5 GiB de RAM dice que tengo aproximadamente 3 GB de RAM por proceso.
Entonces realmente puede probar su sistema usando este sencillo programa para verificar la memoria máxima utilizable por proceso:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc,char* argv[]){
size_t oneHundredMiB=100*1048576;
size_t maxMemMiB=0;
void *memPointer = NULL;
do{
if(memPointer != NULL){
printf("Max Tested Memory = %zi\n",maxMemMiB);
memset(memPointer,0,maxMemMiB);
free(memPointer);
}
maxMemMiB+=oneHundredMiB;
memPointer=malloc(maxMemMiB);
}while(memPointer != NULL);
printf("Max Usable Memory aprox = %zi\n",maxMemMiB-oneHundredMiB);
return 0;
}
Este programa obtiene memoria en incrementos de 100MiB, presenta la memoria asignada actualmente, le asigna 0 y luego libera la memoria. Cuando el sistema no puede proporcionar más memoria, devuelve NULL y muestra la cantidad máxima de RAM útil final.
La advertencia es que su sistema comenzará a intercambiar memoria en gran medida en las etapas finales. Dependiendo de la configuración de su sistema, el núcleo puede decidir eliminar algunos procesos. Utilizo incrementos de 100 MiB, por lo que hay algo de espacio para respirar para algunas aplicaciones y el sistema. Debes cerrar todo lo que no quieras bloquear.
Habiendo dicho eso. En mi sistema donde estoy escribiendo esto, nada se estrelló. Y el programa de arriba reporta apenas lo mismo que ulimit -a
. La diferencia es que en realidad probó la memoria y por medio de memset()
confirmó que la memoria fue entregada y utilizada.
Para la comparación en una máquina virtual Ubuntu 10.04x86 con 256 MiB de RAM y 400 MiB de intercambio, el informe ulimit fue memory size (kbytes, -m) unlimited
y mi pequeño programa reportó 524.288.000 bytes, que es aproximadamente la memoria RAM y el intercambio combinados, descontando la memoria RAM utilizada por otros programas y el kernel.
Editar:como escribió Adam Zalcman, ulimit -m
ya no se respeta en los kernels de Linux 2.6 y posteriores, por lo que estoy corregido. Pero ulimit -v
es honrado Para obtener resultados prácticos, debe reemplazar -m con -v y buscar virtual memory (kbytes, -v) 4515440
. Parece una mera casualidad que mi caja suse tuviera el valor -m coincidiendo con lo que informó mi pequeña utilidad. Debe recordar que esta es la memoria virtual asignada por el núcleo, si la RAM física es insuficiente, se necesitará espacio de intercambio para compensarla.
Si desea saber cuánta memoria RAM física está disponible sin alterar ningún proceso o el sistema, puede usar
long total_available_ram =sysconf(_SC_AVPHYS_PAGES) * sysconf(_SC_PAGESIZE) ;
esto excluirá la memoria caché y el búfer, por lo que este número puede ser mucho menor que la memoria disponible real. Los cachés del sistema operativo pueden ser bastante grandes y su desalojo puede brindar la memoria adicional necesaria, pero eso lo maneja el kernel.
La gestión de almacenamiento dinámico y memoria es una función proporcionada por su biblioteca C (probablemente glibc). Mantiene el montón y te devuelve fragmentos de memoria cada vez que haces un malloc()
. No conoce el límite de tamaño del montón:cada vez que solicita más memoria de la que está disponible en el montón, simplemente va y le pide más al kernel (ya sea usando sbrk()
o mmap()
).
De forma predeterminada, el kernel casi siempre le dará más memoria cuando se le solicite. Esto significa que malloc()
siempre devolverá una dirección válida. Solo cuando hace referencia a una página asignada por primera vez, el kernel realmente se molestará en encontrar una página para usted. Si descubre que no puede entregarle uno, ejecuta un asesino OOM que, según cierta medida, se llama maldad (que incluye el tamaño de la memoria virtual de su proceso y de sus hijos, el buen nivel, el tiempo total de ejecución, etc.) selecciona una víctima y le envía un SIGTERM
. Esta técnica de administración de memoria se llama overcommit y la utiliza el núcleo cuando /proc/sys/vm/overcommit_memory
es 0 o 1. Consulte la contabilidad de exceso de compromiso en la documentación del núcleo para obtener más detalles.
Escribiendo 2 en /proc/sys/vm/overcommit_memory
puede deshabilitar la sobreasignación. Si lo hace, el núcleo realmente verificará si tiene memoria antes de prometerlo. Esto resultará en malloc()
devolviendo NULL si no hay más memoria disponible.
También puede establecer un límite en la memoria virtual que un proceso puede asignar con setrlimit()
y RLIMIT_AS
o con el ulimit -v
dominio. Independientemente de la configuración de sobreasignación descrita anteriormente, si el proceso intenta asignar más memoria que el límite, el núcleo lo rechazará y malloc()
devolverá NULL. Tenga en cuenta que en el kernel de Linux moderno (incluida toda la serie 2.6.x) el límite en el tamaño residente (setrlimit()
con RLIMIT_RSS
o ulimit -m
comando) es ineficaz.
La siguiente sesión se ejecutó en el kernel 2.6.32 con 4 GB de RAM y 8 GB de intercambio.
$ cat bigmem.c
#include <stdlib.h>
#include <stdio.h>
int main() {
int i = 0;
for (; i < 13*1024; i++) {
void* p = malloc(1024*1024);
if (p == NULL) {
fprintf(stderr, "malloc() returned NULL on %dth request\n", i);
return 1;
}
}
printf("Allocated it all\n");
return 0;
}
$ cc -o bigmem bigmem.c
$ cat /proc/sys/vm/overcommit_memory
0
$ ./bigmem
Allocated it all
$ sudo bash -c "echo 2 > /proc/sys/vm/overcommit_memory"
$ cat /proc/sys/vm/overcommit_memory
2
$ ./bigmem
malloc() returned NULL on 8519th request
$ sudo bash -c "echo 0 > /proc/sys/vm/overcommit_memory"
$ cat /proc/sys/vm/overcommit_memory
0
$ ./bigmem
Allocated it all
$ ulimit -v $(( 1024*1024 ))
$ ./bigmem
malloc() returned NULL on 1026th request
$
En el ejemplo anterior, el intercambio o la eliminación de OOM nunca podrían ocurrir, pero esto cambiaría significativamente si el proceso realmente intentara tocar toda la memoria asignada.
Para responder a su pregunta directamente:a menos que tenga un límite de memoria virtual establecido explícitamente con ulimit -v
comando, no hay límite de tamaño de almacenamiento dinámico aparte de los recursos físicos de la máquina o el límite lógico de su espacio de direcciones (relevante en sistemas de 32 bits). Su glibc seguirá asignando memoria en el montón y solicitará más y más del núcleo a medida que crezca su montón. Eventualmente, puede terminar intercambiando mal si se agota toda la memoria física. Una vez que se agote el espacio de intercambio, el asesino OOM del kernel eliminará un proceso aleatorio.
Sin embargo, tenga en cuenta que la asignación de memoria puede fallar por muchas más razones además de la falta de memoria libre, la fragmentación o alcanzar un límite configurado. El sbrk()
y mmap()
las llamadas utilizadas por el asignador de glib tienen sus propias fallas, p. la interrupción del programa llegó a otra dirección ya asignada (por ejemplo, memoria compartida o una página previamente asignada con mmap()
) o se ha superado el número máximo de asignaciones de memoria del proceso.