Cómo verificar el tamaño del montón para un proceso en Linux

10 minutos de lectura

avatar de usuario
bashrc

Estaba escribiendo un código y seguía fallando. Más tarde, después de cavar los vertederos, me di cuenta de que estaba sobrepasando el límite máximo de almacenamiento dinámico (la vida hubiera sido más fácil si hubiera agregado un control en malloc). Aunque lo arreglé, ¿hay alguna forma de aumentar el tamaño de mi almacenamiento dinámico?

PD: Una pregunta bastante similar aquí, pero la respuesta no está clara para mí.

  • ¿No es el montón casi toda la memoria virtual asignada a su programa? ¿Es esta una plataforma de 32 bits (o más pequeña) y cuánta memoria está tratando de asignar?

    – Emil Vikström

    3 de diciembre de 2011 a las 10:25


avatar de usuario
Adán Zalcman

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 una 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 al núcleo más (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 de ejecución general, etc.) selecciona una víctima y le envía un SIGTERM. Esta técnica de administración de memoria se llama overcommit y es utilizada por el núcleo cuando /proc/sys/vm/overcommit_memory es 0 o 1. Ver contabilidad de exceso de compromiso en la documentación del kernel para más detalles.

Al escribir 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 mando. 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 agota 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. los sbrk() y mmap() las llamadas utilizadas por el asignador de glib tienen sus propias fallas, por ejemplo, la interrupción del programa llegó a otra dirección ya asignada (por ejemplo, memoria compartida o una página asignada previamente con mmap()) o se ha superado el número máximo de asignaciones de memoria del proceso.

  • ¿Es posible conseguir start_brk de usuario directamente si no ha almacenado el valor de retorno de sbrk llamadas?

    – Ciro Santilli Путлер Капут 六四事

    29/04/2015 a las 21:56

avatar de usuario
RedComet

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 buscar esta línea max memory size (kbytes, -m) 3008828esta 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 simple 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 utilizable 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 anterior informa 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.04×86 con 256 MiB de RAM y 400MiB de intercambio, el informe de 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 núcleo.

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 compensarlo.

Si desea saber cuánta 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.

  • ulimit -m no tiene ningún efecto en el kernel de Linux más reciente que 2.4.29.

    – Adam Zalcman

    3 de diciembre de 2011 a las 11:39

  • “ilimitado” no tiene sentido, ¿verdad? Tiene que haber un límite. El ulimit -m y el ulimit -v devuelven ilimitado en mi ubuntu. Creo que la manera perfecta de averiguarlo es ejecutar su utilidad.

    –Bikash Gyawali

    11 de diciembre de 2013 a las 16:16

Creo que tu problema original era ese 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, por lo 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.

  • la respuesta 3 fue el caso. Intenté verificar ulimits pero no encontré nada para el tamaño del montón. Sí, puedo aumentar el tamaño de la pila usando ulimit. )

    – bashrc

    3 de diciembre de 2011 a las 11:13

Me gustaría agregar un punto a las respuestas anteriores.

Las aplicaciones tienen la ilusión de que malloc() devuelve bloques ‘sólidos’; en realidad, un búfer puede existir disperso, pulverizado, en muchas páginas de RAM. El hecho crucial aquí es este: la memoria virtual de un proceso, que contiene su código o contiene algo como una gran matriz, deber ser contiguo. Incluso admitamos que el código y los datos estén separados; una gran matriz, char str[universe_size]deben ser contiguos.

Ahora: ¿puede una sola aplicación agrandar el montón arbitrariamente para asignar dicha matriz?

La respuesta podría ser ‘sí’ si no hubiera nada más ejecutándose en la máquina. El montón puede ser ridículamente grande, pero debe tener límites. En algún momento, las llamadas a sbrk() (en Linux, la función que, en resumen, ‘agranda’ el montón) deberían tropezar con el área reservada para otra aplicación.

Esta Enlace proporciona algunos ejemplos interesantes y clarificadores, échale un vistazo. No encontré la información en Linux.

Puede encontrar la identificación del proceso de su proceso webapp/java desde la parte superior. Use jmap heap – para obtener la asignación del montón. Probé esto en AWS-Ec2 para beanstalk elásticos y da el montón asignado. Aquí está la respuesta detallada Configuración de Xmx en el tallo de elasticbean a través de las propiedades del entorno

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad