¿Qué tan grande puede ser un malloc en C?


Tengo un malloc en C que es 26901^2*sizeof(doble)

Esto me hizo pensar cuál puede ser el valor más grande aquí?

Además, ¿tendría problemas para definir una macro para acceder a esta matriz 2D?

 #define DN(i,j) ((int)i * ny + (int)j)

Porque esto parece no estar funcionando para mí - o al menos no estoy seguro de que lo sea. No puedo averiguar cómo hacer que totalview bucee en una macro para decirme qué está mirando realmente un[DN(indx,jndx)].

Author: Derek, 2010-08-12

6 answers

Observaciones

Asumiendo un asignador típico, como el que usa glibc, hay algunas observaciones: {[18]]}

  1. Ya sea que la memoria se use realmente o no, la región debe reservarse contiguamente en la memoria virtual.
  2. Las regiones contiguas libres más grandes dependen del uso de memoria de las regiones de memoria existentes, y la disponibilidad de esas regiones para malloc.
  3. Las prácticas de asignación dependen de la arquitectura y el sistema operativo. Además las llamadas al sistema subyacentes para obtener regiones de memoria se ven afectadas por estas prácticas (como malloc llamando a través de mmap para adquirir páginas).

Experimento

Aquí hay un programa simple para asignar el bloque más grande posible (compilar con gcc largest_malloc_size.c -Wall -O2:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

static void *malloc_wrap(size_t size)
{
    void *p = malloc(size);
    if (p) {
        printf("Allocated %zu bytes from %p to %p\n", size, p, p + size);
    }
    else {
        printf("Failed to allocated %zu bytes\n", size);
    }
    return p;
}

int main()
{
    size_t step = 0x1000000;
    size_t size = step;
    size_t best = 0;
    while (step > 0)
    {
        void *p = malloc_wrap(size);
        if (p) {
            free(p);
            best = size;
        }
        else {
            step /= 0x10;
        }
        size += step;
    }
    void *p = malloc_wrap(best);
    if (p) {
        pause();
        return 0;
    }
    else {
        return 1;
    }
}

Ejecutando el programa anterior (./a.out) en mi máquina Linux stanley 2.6.32-24-generic-pae #39-Ubuntu SMP Wed Jul 28 07:39:26 UTC 2010 i686 GNU/Linux obtiene este resultado:

<snip>
Allocated 2919235584 bytes from 0x9763008 to 0xb7763008
Allocated 2936012800 bytes from 0x8763008 to 0xb7763008
Failed to allocated 2952790016 bytes
Failed to allocated 2953838592 bytes
Failed to allocated 2953904128 bytes
Failed to allocated 2953908224 bytes
Allocated 2936012800 bytes from 0x85ff008 to 0xb75ff008

Esta es una asignación de exactamente 2800MiB. Observar el mapeo relevante desde /proc/[number]/maps:

<snip>
0804a000-0804b000 rw-p 00001000 08:07 3413394    /home/matt/anacrolix/public/stackoverflow/a.out
085ff000-b7600000 rw-p 00000000 00:00 0          [heap]
b7600000-b7621000 rw-p 00000000 00:00 0 
b7621000-b7700000 ---p 00000000 00:00 0 
b7764000-b7765000 rw-p 00000000 00:00 0 
b7765000-b78b8000 r-xp 00000000 08:08 916041     /lib/tls/i686/cmov/libc-2.11.1.so
<snip>
bfc07000-bfc1c000 rw-p 00000000 00:00 0          [stack]

Conclusión

Parece que el montón se ha expandido en el área entre los datos del programa y el código, y las asignaciones de bibliotecas compartidas, que se ajustan perfectamente al límite del espacio de memoria del usuario/kernel (obviamente 3G/1G en este sistema).

Este resultado sugiere que el espacio máximo asignable usando malloc es aproximadamente igual a:{[18]]}

  1. La región del espacio de usuario (3GB en el ejemplo)
  2. Menos el desplazamiento al inicio de el montón (código de programa y datos)
  3. Menos el espacio reservado para la pila de subprocesos principal
  4. Menos el espacio ocupado por todos los mapeados en bibliotecas compartidas
  5. Finalmente, la región contigua más grande que se puede encontrar por la llamada al sistema subyacente dentro de la región disponible para el montón (que puede estar fragmentada por otras asignaciones)

Notas

Con respecto a las implementaciones de glibc y Linux, los siguientes fragmentos de manual son de gran interés:

malloc

   Normally, malloc() allocates memory from the heap, and adjusts the size
   of the heap as required, using sbrk(2).  When allocating blocks of mem‐
   ory larger than MMAP_THRESHOLD bytes, the glibc malloc() implementation
   allocates the memory as a  private  anonymous  mapping  using  mmap(2).
   MMAP_THRESHOLD  is  128  kB  by  default,  but is adjustable using mal‐
   lopt(3).

mmap

   MAP_ANONYMOUS
          The mapping is not backed by any file; its contents are initial‐
          ized to zero.

Epílogo

Esta prueba se realizó en un kernel x86. Esperaría resultados similares de un kernel x86_64, aunque con regiones de memoria mucho más grandes devueltas. Otros sistemas operativos pueden variar en su colocación de asignaciones, y el manejo de mallocs grandes, por lo que los resultados podrían ser bastante diferentes.

 36
Author: Matt Joiner,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-08-15 08:17:24

Eso depende de su implementación malloc!

Según Wikipedia, "Desde la versión v2.3, la biblioteca C de GNU (glibc) utiliza un ptmalloc2 modificado, que a su vez está basado en dlmalloc v2.7.0."dlmalloc se refiere a la implementación malloc de Doug Lea. Lo importante a tener en cuenta en esta implementación es que los mallocs grandes se logran a través de la funcionalidad de archivos mapeados de memoria del sistema operativo, por lo que estos bloques pueden ser bastante grandes sin muchos problemas de encontrar un bloque contiguo.

 9
Author: Hut8,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-08-11 22:08:49

La pregunta malloc es respondida (depende del sistema operativo, que no especificas), así que sobre eso define:

#define DN(i,j) ((int)i * ny + (int)j)

No es muy seguro, porque alguien podría hacer DN(a+b,c) que se expande a

((int)a+b * ny + (int)c)

Que probablemente no es lo que querías. Así que pon un montón de paréntesis ahí:

#define DN(i,j) ((int)(i) * ny + (int)(j))

Para ver a qué apunta DN(indx,jndx), solo printf("%d\n",DN(indx,jndx));

 7
Author: mvds,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-08-11 22:14:55

El parámetro size en una llamada a malloc es de tipo size_t, que varía según la implementación. Ver esta pregunta para más.

 1
Author: Will,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:25:21

Esto me hizo pensar cuál puede ser el valor más grande aquí?

26'901^2 = 723'663'801. Si su doble es de 8 bytes, entonces es menos de 8 GB. No veo ningún problema en asignar esa gran parte de la memoria y mis aplicaciones asignan rutinariamente (en sistemas de 64 bits) mucho más. (El mayor consumo de memoria que he visto fue de 420 GB (en el sistema Solaris 10 numa con 640 GB de RAM) con el bloque continuo más grande de ~24 GB.)

El mayor valor es difícil de identificar ya que es una plataforma dependiente: similar a los sistemas de 32 bits, depende de la división de espacio de usuario / espacio de núcleo. Tal como están las cosas en este momento, creo que uno llegaría primero al límite de la RAM física real, antes de alcanzar el límite de lo que libc puede asignar. (Y al kernel no le importa, simplemente expande la memoria virtual a menudo sin siquiera considerar si hay suficiente RAM para anclarla.)

 1
Author: Dummy00001,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-08-11 22:21:54

El bloque de memoria más grande que puedes preguntar malloc() for es el mayor valor size_t - esto es SIZE_MAX de <limits.h>. La mayor cantidad que puede solicitar con éxito depende obviamente del sistema operativo y la configuración de la máquina individual.

Su macro no es seguro. Realiza el cálculo del índice con una variable int, que solo se requiere para tener un rango de hasta 32767. Cualquier valor superior a este puede causar desbordamiento firmado, lo que resulta en comportamiento indefinido. Probablemente sea mejor hacer el cálculo como un size_t, ya que ese tipo debe ser capaz de contener cualquier índice de matriz válido:

#define DN(i, j) ((size_t)(i) * ny + (size_t)(j))

(Aunque tenga en cuenta que si proporciona valores negativos para i o j, obtendrá un índice muy fuera de los límites).

 1
Author: caf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-08-11 23:52:53