¿Cómo sabe free cuánto cuesta liberar?

385

En la programación en C, puede pasar cualquier tipo de puntero que desee como argumento para liberar, ¿cómo sabe el tamaño de la memoria asignada para liberar? Cada vez que paso un puntero a alguna función, también tengo que pasar el tamaño (es decir, un conjunto de 10 elementos necesita recibir 10 como parámetro para conocer el tamaño del conjunto), pero no tengo que pasar el tamaño al Función libre. ¿Por qué no, y puedo usar esta misma técnica en mis propias funciones para evitar tener que cargar con la variable adicional de la longitud de la matriz?

Joshua Cheek
fuente
Una pregunta similar: stackoverflow.com/questions/851958/… (aunque yo diría que no está del todo duplicado)
John Carter
El sistema de amigos es otra forma de hacerlo que se puede determinar en función del puntero, sin sobrecarga en cada bloque.
EvilTeach
Esta publicación lo explica bien: stackoverflow.com/questions/1957099/…
Zeeshan Mahmood

Respuestas:

349

Cuando llama malloc(), especifica la cantidad de memoria para asignar. La cantidad de memoria realmente utilizada es un poco más que esto e incluye información adicional que registra (al menos) qué tan grande es el bloque. No puede (confiablemente) acceder a esa otra información, y tampoco debe :-).

Cuando llama free(), simplemente mira la información adicional para averiguar qué tan grande es el bloque.

Gary McGill
fuente
44
Para su información, por ejemplo, BSD tiene malloc_size()que acceder de forma confiable al tamaño de bloque desde un malloc()puntero ed. Pero no hay una forma confiable y portátil.
laalto 05 de
50
Creo que es importante decir que este bloque de información adicional está situado antes del puntero devuelto.
Georg Schölly, el
39
@gs Bueno, eso depende de la implementación. Pero sí, ahí es donde suele estar.
Falaina el
31
¿Te imaginas el horror si se free()requiere que el programador informe con precisión qué tan grande era el malloc()bloque? Las pérdidas de memoria son lo suficientemente malas como están.
MusiGenesis
35
¿Por qué esa información está disponible para malloc()y free(), pero tiene que almacenar el tamaño de una matriz? ¿Por qué no harían posible hacer algo como blockSize(ptr)si estuvieran almacenando la información de todos modos?
corsiKa
144

La mayoría de las implementaciones de las funciones de asignación de memoria C almacenarán información contable para cada bloque, ya sea en línea o por separado.

Una forma típica (en línea) es asignar realmente un encabezado y la memoria que solicitó, rellenados a un tamaño mínimo. Entonces, por ejemplo, si solicitó 20 bytes, el sistema puede asignar un bloque de 48 bytes:

  • Encabezado de 16 bytes que contiene tamaño, marcador especial, suma de verificación, punteros al bloque siguiente / anterior, etc.
  • Área de datos de 32 bytes (sus 20 bytes se completaron con un múltiplo de 16).

La dirección que se le proporciona es la dirección del área de datos. Luego, cuando libere el bloque, freesimplemente tomará la dirección que le dé y, suponiendo que no haya rellenado esa dirección o la memoria que la rodea, verifique la información contable inmediatamente antes. Gráficamente, eso estaría en la línea de:

 ____ The allocated block ____
/                             \
+--------+--------------------+
| Header | Your data area ... |
+--------+--------------------+
          ^
          |
          +-- The address you are given

Tenga en cuenta que el tamaño del encabezado y el relleno están totalmente definidos por la implementación (en realidad, todo está definido por la implementación (a), pero la opción de contabilidad en línea es común).

Las sumas de verificación y los marcadores especiales que existen en la información contable son a menudo la causa de errores como "Arena de memoria corrupta" o "Doble libre" si los sobrescribe o los libera dos veces.

El relleno (para hacer que la asignación sea más eficiente) es la razón por la que a veces puedes escribir un poco más allá del final del espacio solicitado sin causar problemas (aún así, no hagas eso, es un comportamiento indefinido y, solo porque a veces funciona, no funciona) t significa que está bien hacerlo).


(una) He escrito implementaciones de mallocen sistemas embebidos donde obtuviste 128 bytes sin importar lo que pediste (ese era el tamaño de la estructura más grande del sistema), suponiendo que pediste 128 bytes o menos (las solicitudes de más cumplir con un valor de retorno NULL). Se usó una máscara de bits muy simple (es decir, no en línea) para decidir si se asignó un fragmento de 128 bytes o no.

Otros que he desarrollado tenían diferentes grupos para fragmentos de 16 bytes, fragmentos de 64 bytes, fragmentos de 256 bytes y fragmentos de 1K, nuevamente usando una máscara de bits para decidir qué bloques se usaron o estaban disponibles.

Ambas opciones lograron reducir la sobrecarga de la información contable y aumentar la velocidad mallocy free(sin necesidad de fusionar bloques adyacentes al liberar), particularmente importante en el entorno en el que estábamos trabajando.

paxdiablo
fuente
@paxdiablo ¿Eso significa que malloc no asigna bloques de memoria contiguos?
user10678
2
@ user10678, el único requisito real malloces que le proporcione, para el caso exitoso, un bloque de memoria al menos tan grande como lo que solicitó. Los bloques individuales son contiguos en términos de cómo se accede a los elementos dentro de ellos, pero no es necesario que las arenas de las que provienen los bloques sean contiguas.
paxdiablo
Pregunta relacionada: ¿Por qué no hay variación de malloc / free, donde especifica el tamaño al liberar y, por lo tanto, no tiene que almacenar el tamaño?
user253751
@ user253751, porque entonces hay una cosa más que debes seguir, más allá del puntero. Es innecesario y peligroso: void *x = malloc(200); free(x, 500);se no va a terminar bien :-) En cualquier caso, la eficiencia, el real tamaño de la memoria intermedia puede ser más grande (que no se puede confiar en esta).
paxdiablo
@paxdiablo También evita el desperdicio de memoria para mantener el tamaño.
user253751
47

De la comp.lang.clista de preguntas frecuentes: ¿Cómo sabe free cuántos bytes liberar?

La implementación malloc / free recuerda el tamaño de cada bloque a medida que se asigna, por lo que no es necesario recordarle el tamaño al liberarlo. (Por lo general, el tamaño se almacena adyacente al bloque asignado, por lo que las cosas generalmente se rompen mal si los límites del bloque asignado están incluso ligeramente sobrepasados)

jdehaan
fuente
2
Esta no es una respuesta. La pregunta es exactamente esta: ¿por qué puede buscar de forma confiable y gratuita el tamaño del bloque, pero no hay una función disponible para el programador que lo haga?
Bananach
De hecho, este es un detalle de implementación para la API de Malloc y no hay una API para recuperar esta información de forma estándar (que yo sepa). El "sistema" lo registra y lo usa free. Tal vez la respuesta no te satisfaga, pero no creo que obtengas una con información más genéricamente aplicable :-)
jdehaan
6

Esta respuesta se reubica desde ¿Cómo free () sabe cuánta memoria desasignar? donde se me impidió responder con una aparente pregunta duplicada. Esta respuesta debería ser relevante para este duplicado:


Para el caso de malloc, el asignador de almacenamiento dinámico almacena una asignación del puntero devuelto original a los detalles relevantes necesarios parafree almacenar la memoria más adelante. Esto generalmente implica almacenar el tamaño de la región de memoria en cualquier forma relevante para el asignador en uso, por ejemplo, el tamaño bruto, o un nodo en un árbol binario utilizado para rastrear asignaciones, o un recuento de "unidades" de memoria en uso.

freeno fallará si "renombra" el puntero, o lo duplica de alguna manera. Sin embargo, no se cuenta como referencia, y solo el primero freeserá correcto. Los frees adicionales son errores de "doble libre".

Intentar freecualquier puntero con un valor diferente a los devueltos por mallocs anteriores , y aún no liberado, es un error. No es posible liberar parcialmente las regiones de memoria devueltas de malloc.

Matt Joiner
fuente
Cambié el valor de un puntero devuelto por una llamada malloc. Y lo liberé sin error. ¿Por qué? Ver aquí: stackoverflow.com/questions/42618390/…
smwikipedia
4

En una nota relacionada, la biblioteca GLib tiene funciones de asignación de memoria que no guardan el tamaño implícito, y luego simplemente pasa el parámetro de tamaño a libre. Esto puede eliminar parte de los gastos generales.

EFraim
fuente
3

malloc()y free()dependen del sistema / compilador, por lo que es difícil dar una respuesta específica.

Más información sobre esta otra pregunta .

LiraNuna
fuente
2
Realmente dependen de la biblioteca (generalmente la biblioteca C, que generalmente está muy vinculada al sistema operativo). Para el compilador, son solo funciones.
Donal Fellows
2

El administrador de almacenamiento dinámico almacenó la cantidad de memoria que pertenece al bloque asignado en algún lugar cuando llamó malloc.

Nunca implementé uno yo mismo, pero supongo que la memoria justo en frente del bloque asignado podría contener la metainformación.

Timbo
fuente
3
Esa es una implementación posible, pero se podría diseñar un sistema en el que se rastree toda la memoria en una sola tabla en una página totalmente diferente, no necesariamente en ningún lugar cercano al grupo de memoria que se está asignando.
ephemient
2

La técnica original era asignar un bloque un poco más grande y almacenar el tamaño al principio, luego dar a la aplicación el resto del blog. El espacio extra tiene un tamaño y posiblemente se vincula para enhebrar los bloques libres juntos para su reutilización.

Sin embargo, hay ciertos problemas con esos trucos, como el comportamiento deficiente de la memoria caché y la administración de la memoria. El uso de la memoria en el bloque tiende a ubicar cosas innecesariamente y también crea páginas sucias que complican el intercambio y la copia en escritura.

Entonces, una técnica más avanzada es mantener un directorio separado. También se han desarrollado enfoques exóticos donde las áreas de memoria usan los mismos tamaños de potencia de dos.

En general, la respuesta es: se asigna una estructura de datos separada para mantener el estado.

DigitalRoss
fuente
1

Para responder a la segunda mitad de su pregunta: sí, puede, y un patrón bastante común en C es el siguiente:

typedef struct {
    size_t numElements
    int elements[1]; /* but enough space malloced for numElements at runtime */
} IntArray_t;

#define SIZE 10
IntArray_t* myArray = malloc(sizeof(intArray_t) + SIZE * sizeof(int));
myArray->numElements = SIZE;
MSalters
fuente
Esa es una técnica completamente diferente a los usos malloc uno BSD para objetos pequeños (aunque es una técnica perfectamente buena para crear matrices de estilo Pascal)
Pete Kirkham
0

Cuando llamamos a malloc, simplemente consume más byte de su requisito. Este consumo de más bytes contiene información como suma de cheques, tamaño y otra información adicional. Cuando llamamos gratis en ese momento, va directamente a esa información adicional donde se encuentra la dirección y también cuánto bloque será gratis.

Varun Chhangani
fuente
0

para responder a la segunda pregunta, sí, podría (más o menos) usar la misma técnica que malloc() simplemente asignando la primera celda dentro de cada matriz al tamaño de la matriz. que le permite enviar la matriz sin enviar un argumento de tamaño adicional.

avish
fuente