Análisis de los principios subyacentes de ptmalloc.

Tabla de contenido

I. Descripción general

2. Comprensión básica

2.1 Diseño de memoria predeterminado de procesos de 32 bits

2.2 brk y sbrk y mmap

3. Gestión de la memoria

2.1 Estructura

2.1.1 main_arena dado non_main_arena

2.1.2 malloc_chunk

2.1.3 Contenedores de listas libres

2.1.4 Inicialización

2.2 Asignación y liberación de memoria

2.3 Precauciones de uso

3. Análisis comparativo de los mecanismos de implementación de ptmalloc, tcmalloc y jemalloc


I. Descripción general

ptmalloc es el administrador de memoria predeterminado de la biblioteca GNU C de código abierto (glibc). Actualmente, la mayoría de los programas de servidor Linux utilizan la serie de funciones malloc/free proporcionada por ptmalloc, y su rendimiento es mucho peor que el jemalloc de Meta y el tcmalloc de Google.

El programa del servidor llama a la función malloc / free proporcionada por ptmalloc para solicitar y liberar memoria. ptmalloc proporciona una gestión centralizada de la memoria para lograr la mayor cantidad posible:

  • Es más eficiente para los usuarios solicitar y liberar memoria, evitando la concurrencia y el bloqueo de aplicaciones de memoria multiproceso.

  • Busque un equilibrio entre el uso de la memoria y el consumo de rendimiento libre/malloc durante la interacción con el sistema operativo, reduzca la fragmentación de la memoria y llame con poca frecuencia a funciones de llamada al sistema.

Para mejorar la eficiencia de la función de asignación de memoria malloc, ptmalloc solicitará una parte de la memoria del sistema operativo con anticipación para que la use el usuario, y ptmalloc administrará la memoria usada y libre; cuando el usuario necesite liberar la Memoria libre, ptmalloc reciclará la memoria. La memoria se administra y se decide si reciclarla en el sistema operativo de acuerdo con la situación real ( conectividad del grupo de memoria ) .

2. Comprensión básica

2.1 Diseño de memoria predeterminado de procesos de 32 bits

La pila se expande de arriba a abajo, el montón se expande de abajo hacia arriba y el área de mapeo mmap se expande de arriba a abajo. El área de mapeo mmap y el montón se expanden relativamente hasta que se agota el área restante en el espacio de direcciones virtuales

2.2 brk y sbrk y mmap

int brk(const void *addr)
void* sbrk(intptr_t incr)
  • La función de ambos es expandir el límite superior del montón.
  • El parámetro de brk() se establece en la nueva dirección de límite superior de brk. Devuelve 1 en caso de éxito y 0 en caso de error.
  • El parámetro de sbrk() es el tamaño de la memoria solicitada y devuelve la dirección del nuevo límite superior del montón.
void* mmap(void* addr, size_t length, int prot, int flags, int fd, off_t offset)
int munmap(void& addr, size_t length)
  • El primer uso de mmap es asignar este archivo de disco a la memoria.
  • El segundo uso de mmap es el mapeo anónimo. Este archivo de disco no está mapeado, pero se solicita una parte de la memoria del área de mapeo. Malloc usa el segundo uso.
  • munmap se usa para liberar memoria

3. Gestión de la memoria

2.1 Estructura

Para resolver el problema de la contención de bloqueos de subprocesos múltiples, la asignación de memoria se divide en un área de asignación principal (main_area) y un área de asignación no principal (no_main_area). Al mismo tiempo, para facilitar la gestión de la memoria, la memoria preaplicada se divide en muchos fragmentos mediante el método de marcado de límites; en el asignador de memoria ptmalloc, malloc_chunk es la unidad organizativa básica, que se utiliza para gestionar (describir) diferentes tipos de fragmentos, funciones y tamaños. Se conectan fragmentos similares en serie para formar una lista enlazada, que se denomina contenedor.

2.1.1 main_arena dado non_main_arena

En el asignador de memoria, para resolver el problema de la contención de bloqueos de subprocesos múltiples, se divide en el área de asignación principal main_area (la esencia del área de asignación es el grupo de memoria, que administra fragmentos) y el área de asignación no principal no_main_area.

  • ​ El área de asignación principal y el área de asignación no primaria forman una lista circular enlazada para la gestión

  • Cada área de asignación utiliza un bloqueo mutex para evitar que los subprocesos accedan al área de asignación.

  • Cada proceso tiene solo un área de asignación primaria y se permiten múltiples áreas de asignación no primaria.

  • ​ ptmalloc aumenta dinámicamente el tamaño del área de asignación de acuerdo con la llamada del sistema al área de asignación. Una vez que el número de áreas de asignación aumenta, no disminuirá.

  • El área de asignación principal se puede asignar usando brk () y mmap (), mientras que el área de asignación no principal solo puede usar mmap () para asignar bloques de memoria.

  • Al solicitar memoria pequeña, se generarán muchos fragmentos de memoria y ptmalloc también necesita bloquear el área de asignación al ordenar.

Cuando un hilo necesita usar malloc para asignar memoria, primero verifica si hay un área de asignación en la variable privada del hilo y, si existe, intentará bloquearla. Si el bloqueo tiene éxito, la memoria se asignará utilizando el área de asignación; si falla, se recorrerá la lista circular enlazada para obtener un área de asignación desbloqueada. Si no hay un área de asignación desbloqueada en toda la lista vinculada, se abrirá una nueva área de asignación, se agregará a la lista circular global vinculada y se bloqueará, y luego el área de asignación se utilizará para la asignación. Cuando se libera esta memoria, también se adquirirá primero el bloqueo del área de asignación donde se encuentra el bloque de memoria que se va a liberar. Si otros subprocesos están utilizando el área de asignación, debe esperar a que otros subprocesos liberen el bloqueo mutex del área de asignación antes de que se pueda liberar la memoria.

Aviso:

  • Aunque mmap() asigna el área de asignación no primaria, no tiene nada que ver con el uso directo de mmap() para asignaciones mayores a 128K. La memoria mayor a 128K se asigna usando mmap() y se devuelve al sistema directamente usando ummap() después de su uso.

  • Cada hilo primero obtendrá un área en malloc y utilizará el grupo de memoria del área para asignar su propia memoria.Existe una relación de competencia.

  • Para evitar la competencia, puede utilizar la estrategia de almacenamiento local de subprocesos, thead cache (tc en tcmalloc significa exactamente esto)

2.1.2 malloc_chunk

ptmalloc administra de manera uniforme los fragmentos libres en las áreas de mapeo del montón y mmap. Cuando el usuario realiza una solicitud de asignación, primero intentará encontrar y dividir los fragmentos libres, evitando así llamadas frecuentes al sistema y reduciendo el costo de la asignación de memoria. Para gestionar y encontrar mejor los fragmentos libres, se agrega la información de control necesaria antes y después del espacio preasignado.

  • prev_size: si el fragmento anterior está libre, este campo indica el tamaño del fragmento anterior; si no está libre, este campo no tiene sentido (conozca la dirección del fragmento actual, reste prev_size y obtendrá la dirección del fragmento anterior. prev_size se utiliza principalmente para fusionar fragmentos libres adyacentes relacionados)
  • tamaño: el tamaño del fragmento actual y registra los siguientes otros atributos
    • El fragmento anterior está en uso (P = 1)
    • El fragmento actual es la asignación del área de mapeo mmap (M = 1) o la asignación del área del montón (M = 0)
    • El fragmento actual pertenece al área de asignación no principal (A = 0) o al área de asignación no principal (A = 1)
  • fd y bk: solo existirán cuando el fragmento esté libre. Su función es agregar el bloque de fragmento libre correspondiente a la lista de bloques de fragmentos libres para una administración unificada. Si el bloque de fragmento se asigna a la aplicación, entonces estos dos punteros son inútiles , por lo que esta área también se utiliza como espacio de aplicación.
  • fd_nextsize y bk_nextsize: el fragmento actual existe en contenedores grandes. Los fragmentos libres en contenedores grandes se ordenan por tamaño. Si hay varios fragmentos del mismo tamaño, agregar estos dos campos puede acelerar el recorrido de los fragmentos libres y encontrar los fragmentos libres. que satisfacen las necesidades. , fd_nextsize apunta al siguiente primer fragmento libre que es más grande que el fragmento actual, y bk_nextsize apunta al primer fragmento libre anterior que es más pequeño que el fragmento actual. Si el bloque de fragmentos se asigna a la aplicación, entonces estos dos punteros son inútiles, por lo que esta área también se utiliza como espacio de la aplicación.
//ptmalloc源码中定义结构体malloc_chunk来描述这些块
struct malloc_chunk
{
  INTERNAL_SIZE_T      prev_size;    /* Size of previous chunk (if free).  */  
  INTERNAL_SIZE_T      size;         /* Size in bytes, including overhead. */  
  
  struct malloc_chunk* fd;           /* double links -- used only if free. */  
  struct malloc_chunk* bk;  
  
  /* Only used for large blocks: pointer to next larger size.  */  
  struct malloc_chunk* fd_nextsize;      /* double links -- used only if free. */  
  struct malloc_chunk* bk_nextsize; 
};

trozo en uso

  • El puntero del fragmento apunta a la dirección donde comienza el fragmento; el puntero de memoria apunta a la dirección donde comienza el bloque de memoria del usuario.
  • Cuando P = 0, significa que el fragmento anterior está libre y prev_size es válido.
  • Cuando P = 1, significa que se está utilizando el fragmento anterior y prev_size no es válido. p se utiliza principalmente para fusionar bloques de memoria. El primer bloque asignado por ptmalloc siempre establece p en 1 para evitar que el programa haga referencia a un área inexistente.
  • ​ M=1 es la asignación del área de mapeo mmap; M=0 es la asignación del área del montón
  • ​ A=0 se asigna al área de asignación principal; A=1 se asigna al área de asignación no primaria.

trozo gratis

 Cuando el fragmento está inactivo, el estado M no existe, solo el estado AP. Porque M indica si la memoria está asignada por brk o mmap, y la memoria asignada por mmap se asigna directamente a munmap cuando está libre y no se colocará en la lista vinculada libre. En lo que originalmente era el área de datos del usuario se almacenan cuatro punteros. El puntero fd apunta al siguiente fragmento libre y bk apunta al fragmento libre anterior. Malloc usa estos dos punteros para conectar fragmentos de tamaño similar en una lista doblemente enlazada.

Reutilización del espacio en trozos

  • Para minimizar el espacio ocupado por fragmentos, ptmalloc utiliza la reutilización del espacio. En diferentes estados de un fragmento, ciertas áreas tienen diferentes significados para lograr la reutilización.
  • Cuando está inactivo, un fragmento requiere al menos 2 espacios size_t y 2 espacios del tamaño de un puntero para almacenar prev_size, size, fd y bk, que son 16 bytes.
  • Cuando un fragmento está en uso, el campo prev_size del siguiente fragmento definitivamente no es válido, por lo que este espacio también puede ser utilizado por el fragmento actual.

Por lo tanto, la fórmula de cálculo para el tamaño de un fragmento en uso es: in_use_size = (solicitud del usuario + 8 - 4)

Se agregan 8 bytes para almacenar prev_size y size, pero debido a que se toman prestados 4 bytes del siguiente fragmento, se restan 4. Debido a que el fragmento libre y el fragmento en uso usan el mismo espacio, el valor máximo debe tomarse como el espacio asignado real, es decir, el espacio asignado final es chunk_size = max(in_use_size,16)

trozo especial

trozo superior

  • El fragmento superior es equivalente a la memoria libre en la parte superior del área de asignación. Cuando los contenedores no pueden cumplir con los requisitos de asignación de memoria, se asignará en el fragmento superior.
  • Cuando el tamaño del fragmento superior es mayor que el tamaño solicitado por el usuario, el fragmento superior se dividirá en dos partes, el fragmento del usuario y el fragmento restante (tamaño restante). Entre ellos, el fragmento restante se convierte en el nuevo fragmento superior.
  • Cuando el tamaño del fragmento superior es menor que el tamaño solicitado por el usuario, el fragmento superior se expande mediante la llamada al sistema sbrk (área principal) o mmap (área de subprocesos).

fragmento mapeado

  • Cuando la memoria asignada es muy grande (mayor que el umbral de asignación, el valor predeterminado es 128k) y es necesario mapear mmap, se colocará en el fragmento mmaped. Cuando se libere la memoria en el fragmento mmaped, se devolverá directamente al sistema operativo (bandera M en el fragmento) para 1)

último trozo restante

  • ​El último fragmento restante es un fragmento especial, al igual que el fragmento superior y el fragmento mapeado. Este fragmento no se encontrará en ningún contenedor. Cuando es necesario asignar un fragmento pequeño, no se puede encontrar un fragmento adecuado en los contenedores pequeños. Si el tamaño del último fragmento restante es mayor que el tamaño del fragmento pequeño requerido, el último fragmento restante se divide en dos fragmentos, uno de los cuales se devuelve al usuario y el otro se convierte en el nuevo último fragmento restante.

2.1.3 Contenedores de listas libres

Cuando el usuario libera memoria, ptmalloc no devolverá inmediatamente la memoria al sistema operativo. En su lugar, será administrada por los contenedores de listas enlazadas libres de ptmalloc. La próxima vez que el proceso necesite memoria de malloc, ptmalloc encontrará un bloque de memoria adecuado en el contenedores libres Asigne a los usuarios para evitar llamadas frecuentes al sistema y reducir la sobrecarga de asignación de memoria.

ptmalloc conecta fragmentos de tamaños similares utilizando una lista doblemente enlazada. Dicha lista enlazada se llama bin. ptmalloc mantiene un total de 128 bins, y cada bin mantiene fragmentos de listas doblemente enlazadas de tamaños similares. Según el tamaño del trozo, están disponibles los siguientes contenedores:

 Nota: En plataformas de 32 bits, bin[0] y bin[127] no existen. bin[1] son ​​contenedores sin clasificar, bin[2]~bin[126] son ​​contenedores ordenados

contenedor sin clasificar

  • La cola de contenedores sin clasificar se encuentra en el segundo (subíndice 1) de la matriz de contenedores y es un búfer de contenedores para acelerar la asignación.
  • Cuando la memoria liberada por el usuario es mayor que max_fast o los fragmentos después de fusionar contenedores rápidos ingresarán primero a los contenedores sin clasificar, no hay límite en el tamaño de los fragmentos y pueden ingresar fragmentos de cualquier tamaño. Este enfoque le da a ptmalloc una segunda oportunidad de reutilizar el fragmento recientemente liberado. Se omite el tiempo necesario para encontrar un contenedor adecuado, por lo que la asignación y liberación son más rápidas.
  • Cuando el usuario realiza malloc, si los contenedores rápidos no encuentran fragmentos adecuados, malloc primero buscará fragmentos libres adecuados en el contenedor sin clasificar. Si no hay un contenedor adecuado, ptmalloc colocará el fragmento en el contenedor sin clasificar en contenedores y luego buscará un fragmento libre adecuado en los contenedores.

contenedores pequeños

  • Los fragmentos de menos de 512 bytes se denominan fragmentos pequeños y los contenedores que almacenan fragmentos pequeños se denominan contenedores pequeños. Los subíndices empiezan en 2 y terminan en 63, un total de 62. La diferencia entre cada contenedor pequeño es de 8 bytes y los fragmentos del mismo contenedor pequeño tienen el mismo tamaño.
  • ​Cada contenedor pequeño incluye una lista enlazada circular bidireccional de bloques libres. El fragmento libre se agrega al extremo frontal de la lista vinculada y el fragmento requerido se elimina del extremo posterior de la lista vinculada.
  • Dos fragmentos libres conectados se fusionarán en un fragmento libre. La fusión elimina el impacto de la fragmentación pero ralentiza la velocidad libre.
  • Al realizar la asignación, cuando el contenedor pequeño no está vacío, el contenedor correspondiente eliminará el último fragmento de la lista de contenedores y se lo devolverá al usuario. Al liberar un fragmento, compruebe si el fragmento anterior o posterior está libre. Si es así, combínelo, es decir, elimine el fragmento de la lista enlazada a la que pertenece y combínelo en un nuevo fragmento. El nuevo fragmento se agregará al frente de la lista enlazada del contenedor sin clasificar.

contenedores grandes

  • Los fragmentos de más de 512 bytes se denominan fragmentos grandes, y los contenedores guardados como fragmentos grandes se denominan contenedores grandes y se ubican detrás de contenedores pequeños. Los subíndices empiezan en 64 y terminan en 126, un total de 63. En los contenedores grandes, cada contenedor contiene un fragmento dentro de un rango determinado. Los fragmentos se ordenan por tamaño decreciente. Si el tamaño es el mismo, se ordenan según el tiempo de uso más reciente.
  • ​Dos fragmentos libres adyacentes se fusionarán en un fragmento libre.
  • Al asignar, siga "el más pequeño primero, el mejor ajuste" y recorra de arriba a abajo para encontrar un fragmento cuyo tamaño se acerque más a las necesidades del usuario. Una vez encontrado, el fragmento correspondiente se dividirá en dos fragmentos, el fragmento del usuario (tamaño de solicitud del usuario) se devuelve al usuario y el resto del fragmento restante se agrega al contenedor sin clasificar.
  • gratis es similar a un contenedor pequeño

contenedores rápidos

Cuando un programa se está ejecutando, a menudo necesita solicitar y liberar un espacio de memoria más pequeño. Después de que el asignador fusiona varios fragmentos pequeños adyacentes, puede haber una solicitud de otro pequeño bloque de memoria inmediatamente y el asignador necesita dividir un bloque de la gran memoria libre, lo cual es relativamente ineficiente, por lo que se introducen contenedores rápidos.

  • Los contenedores rápidos son buffers de alta velocidad para contenedores y hay alrededor de 10 colas de longitud fija (contenedores). Cada contenedor rápido registra una lista enlazada individualmente de fragmentos libres (lista bin, la lista enlazada individualmente se usa porque los fragmentos de la lista vinculada en el contenedor rápido no se eliminarán), y las adiciones y eliminaciones de fragmentos ocurren en el extremo frontal del lista enlazada.
  • Cuando el usuario libera un fragmento que no es mayor que max_fast (el valor predeterminado es 64 bytes), se colocará en contenedores rápidos de forma predeterminada. Cuando el fragmento que debe asignarse al usuario es menor o igual que max_fast, malloc primero irá a los contenedores rápidos para encontrar si hay un fragmento adecuado. Ya sea que se asignen o liberen fragmentos dentro de un cierto tamaño, pasarán a través de contenedores rápidos.
  • Al realizar la asignación, el primer fragmento recuperado en la lista bin se eliminará y se devolverá al usuario. El fragmento libre se agregará al frente de la lista bin indexada.

2.1.4 Inicialización

  • En el área del montón, start_brk apunta al comienzo del montón y brk apunta a la parte superior del montón. Puede utilizar brk() y sbrk() para aumentar el espacio del montón asignado al usuario. Antes de usar malloc, el valor de brk es igual a start_brk, es decir, tamaño del montón = 0
  • Al comienzo de ptmalloc, si el espacio solicitado es menor que el umbral de asignación de mmap (el valor predeterminado es 128 KB), el área de asignación principal llamará a sbrk() para agregar un espacio de tamaño (128 KB + tamaño de fragmento) como montón, y el espacio no- El área de asignación principal llamará a mmap. Asigna un espacio con un tamaño de HEAP_MAX_SIZE (el valor predeterminado es 1 MB para sistemas de 32 bits y 64 MB para sistemas de 64 bits) como un submontón.
  • Cuando el usuario solicita la asignación de memoria, primero encontrará un fragmento adecuado en esta área para el usuario. Cuando el usuario libera el fragmento en el montón, ptmalloc utilizará contenedores rápidos y contenedores para organizar el fragmento libre.
  • Si el tamaño del fragmento que debe asignarse es menor que el umbral de asignación de mmap y el espacio del montón no es suficiente, el área de asignación principal aumentará el tamaño del montón llamando a sbrk(), y el área de asignación no principal llamará mmap para mapear un nuevo submontón. Es decir, aumenta el tamaño del fragmento superior. Cada vez que el montón aumenta, el valor se alineará a 4 KB.
  • Cuando la solicitud del usuario excede el umbral de asignación de mmap y el área de asignación principal no se puede asignar usando sbrk(), o el área de asignación no principal no puede asignar la memoria requerida en el fragmento superior, ptmalloc intentará usar mmap() para directamente asignar un fragmento de memoria al espacio de memoria del proceso. Los fragmentos asignados directamente usando mmap () se asignan directamente cuando se liberan y ya no pertenecen al espacio de memoria del proceso. Cualquier acceso a esta memoria generará un error de segmentación. El espacio asignado en el montón o submontón puede permanecer en el espacio de memoria del proceso y se puede hacer referencia a él nuevamente.

2.2 Asignación y liberación de memoria

Proceso malloc de asignación de memoria

1. Obtenga el bloqueo del área de asignación para evitar conflictos entre subprocesos múltiples (cada proceso tiene un administrador malloc y varios subprocesos en un proceso comparten este administrador, por lo que hay competencia)

2. Calcule el tamaño real del fragmento de memoria que debe asignarse

3. Si el tamaño del fragmento < max_fast (64 bytes), busque un fragmento adecuado en contenedores rápidos; si no existe, vaya a 5

4. Si el tamaño del fragmento es < 512 bytes, busque el fragmento en los contenedores pequeños. Si existe, la asignación finaliza.

5. Lo que se debe asignar es una gran cantidad de memoria, o el fragmento no se puede encontrar en los contenedores pequeños:

  • a. Atraviese contenedores rápidos, combine fragmentos adyacentes y vincule a contenedores sin clasificar
  • b. Recorra los trozos en un contenedor sin clasificar:
    • ① Capaz de cortar trozos y asignarlos directamente, y la asignación finaliza
    • ② Coloque el fragmento en contenedores pequeños o grandes según el tamaño del espacio del fragmento. Una vez completado el recorrido, vaya a 6

6. Lo que se debe asignar es una gran cantidad de memoria, o no se pueden encontrar fragmentos adecuados en los contenedores pequeños y sin clasificar, y todos los fragmentos en los contenedores rápidos y sin clasificar se han borrado:

  • Busque desde contenedores grandes y recorra la lista vinculada hasta encontrar el primer fragmento que sea mayor que el tamaño que se asignará y córtelo. Los fragmentos restantes se colocan en contenedores sin clasificar y la asignación finaliza.

7. La recuperación de contenedores rápidos y los contenedores no encuentran un fragmento adecuado. Determine si el tamaño del fragmento superior cumple con el tamaño de fragmento requerido y asígnelo desde el fragmento superior.

8. La parte superior no puede satisfacer la demanda y es necesario ampliarla:

  • Cuando el tamaño del fragmento superior es mayor que la solicitud del usuario, el fragmento superior se dividirá en dos partes: el fragmento del usuario y el fragmento restante, donde el fragmento restante se convierte en el nuevo fragmento superior.
  • Cuando el tamaño del fragmento superior es menor que la solicitud del usuario, el fragmento superior se expande mediante la llamada al sistema sbrk() o mmap().

9. Cuando el fragmento superior no puede cumplir con los requisitos de asignación, si es el área de asignación principal, llame a sbrk() para aumentar el tamaño del fragmento superior; si es el área de asignación no principal, llame a mmap para asignar un nuevo submontón y aumente el tamaño del fragmento superior; o use mmap () para asignar directamente:

  • Si el fragmento que se asignará es mayor o igual que el umbral de asignación de mmap, use la llamada al sistema mmap para asignar un espacio de 4 KB de alineación de tamaño de fragmento al espacio de memoria del programa. Luego devuelve el puntero de memoria al usuario.
  • Si el fragmento a asignar es menor o igual que el umbral de asignación de mmap, determine si es la primera vez que llama a malloc. Si es el área de asignación principal, se requiere un trabajo de inicialización y un espacio de tamaño (chunk_size + 128 KB) align Se asignan 4 KB como montón inicial. Si se ha inicializado, el área de asignación principal llama a sbrk() para aumentar el espacio del montón. El área de asignación no principal corta un fragmento en el fragmento superior para cumplir con los requisitos de asignación y devuelve el puntero de memoria al usuario.

Proceso libre de liberación de memoria

1. Obtenga el candado del área de asignación.

2. Si free es un puntero nulo, devuelve

3. Si el fragmento actual es la memoria asignada por el área de mapeo mmap, llame a munmap () para liberar la memoria.

4. Si el fragmento está adyacente al fragmento superior, combínelo directamente con el fragmento superior y vaya a 8.

5. Si el tamaño del fragmento > max_fast, colóquelo en un contenedor sin clasificar y verifique si hay fusión:

  • a. Si no hay fusión, será gratis.
  • b. Hay una situación de fusión y está adyacente al fragmento superior, vaya a 8

6. Si el tamaño del fragmento <max_fast, colóquelo en el contenedor rápido y verifique si hay fusión:

  • a.fast bin no cambia el estado del fragmento, si no hay fusión, es gratuito.
  • b. Si hay una situación de fusión, vaya a 7

7. En los contenedores rápidos, si los trozos adyacentes están libres, combine los dos trozos y colóquelos en el contenedor sin clasificar. Si el tamaño combinado es> 64 KB, se activará la operación de combinación de contenedores rápidos. Los fragmentos en los contenedores rápidos se atravesarán y fusionarán, y los fragmentos fusionados se colocarán en el contenedor sin clasificar. Si el fragmento fusionado es adyacente al fragmento superior, se fusionará con el fragmento superior y pasará a 8

8. Si el tamaño del fragmento superior > umbral de reducción de mmap (el valor predeterminado es 128 KB), para el área de asignación principal, intentará devolver parte del fragmento superior al sistema operativo.

2.3 Precauciones de uso

Para evitar la explosión de la memoria de Glibc, debe prestar atención a:

  1. La memoria asignada más tarde se libera primero, porque ptmalloc reduce la memoria a partir del fragmento superior. Si los fragmentos adyacentes al fragmento superior no se pueden liberar, los fragmentos debajo del fragmento superior no se pueden liberar.
  2. ptmalloc no es adecuado para administrar la memoria de larga duración, especialmente la asignación y liberación continua e irregular de memoria de larga duración, lo que hará que la memoria de ptmalloc explote.
  3. Los programas ejecutados por subprocesos múltiples en etapas no son adecuados para ptmalloc. La memoria de dichos programas es más adecuada para la gestión del grupo de memoria.
  4. Intente reducir la cantidad de subprocesos en el programa y evite la asignación y liberación frecuente de memoria. La asignación frecuente conducirá a una competencia de bloqueo, lo que eventualmente conducirá a un aumento en las áreas de asignación no primaria, una mayor fragmentación de la memoria y un rendimiento reducido.
  5. Para evitar pérdidas de memoria, ptmalloc es muy sensible a las pérdidas de memoria. De acuerdo con su mecanismo de reducción de memoria, si el fragmento adyacente al fragmento superior no se recicla, una gran cantidad de memoria libre debajo del fragmento superior no se devolverá al sistema operativo.
  6. Esto evita que el programa asigne demasiada memoria, o que la memoria del sistema se agote debido al aumento repentino de la memoria Glibc y el sistema finalice el programa debido a OOM. Calcule el tamaño máximo de memoria física que el programa puede usar, configure /proc/sys/vm/overcommit_memory, /proc/sys/vm/overcommit_ratio del sistema y use ulimt -v para limitar la cantidad de espacio de memoria virtual que el programa puede usar. Úselo para evitar que el programa sea eliminado por OOM.

3. Análisis comparativo de los mecanismos de implementación de ptmalloc, tcmalloc y jemalloc

ptmalloc(glibc malloc):

  • ptmalloc es el asignador de memoria predeterminado en la biblioteca GNU C (glibc) y se usa ampliamente en sistemas Linux.
  • Basado en la implementación malloc de Doug Lea, utiliza una variedad de tecnologías, como listas enlazadas libres, separadores y enlace retardado del montón.
  • ptmalloc se caracteriza por su madurez, estabilidad y estrecha integración con la biblioteca GNU C

tcmalloc(Google malloc):

  • tcmalloc es un asignador de memoria desarrollado por Google, utilizado principalmente en el código C++ de Google.
  • tcmalloc mejora el rendimiento al reducir la contención de bloqueos y la fragmentación de la memoria
  • Utilice el concepto de almacenamiento en caché local de subprocesos (Thread-Caching Malloc) para distribuir la tarea de asignación de memoria a diferentes subprocesos para reducir la competencia por estructuras de datos compartidas.
  • tcmalloc también tiene otras estrategias de optimización, como la fusión de objetos pequeños, el almacenamiento en caché de asignadores eficientes, etc.

jemalloc:

  • jemalloc es un asignador de memoria de propósito general desarrollado por la comunidad FreeBSD y adoptado gradualmente por otros sistemas.
  • jemalloc se esfuerza por proporcionar una asignación de memoria altamente escalable y de baja fragmentación
  • Utiliza múltiples técnicas, como regiones de memoria separadas, asignadores de amigos, caché local de subprocesos, etc.
  • jemalloc también proporciona funciones avanzadas, como liberación de ejecución de subprocesos en segundo plano, estadísticas y análisis de utilización del espacio, etc.

Comparación de rendimiento:

  • ptmalloc funciona bien en la mayoría de los casos, pero puede tener algunas condiciones de carrera en entornos multiproceso
  • tcmalloc utiliza almacenamiento en caché local de subprocesos y reduce la competencia de bloqueos, lo que lo hace adecuado para escenarios de alta concurrencia, especialmente aplicaciones de servidor de subprocesos múltiples.
  • jemalloc sobresale en escalabilidad y fragmentación, especialmente para grandes asignaciones de memoria y escenarios de alta carga

Resumir:

  • ptmalloc es adecuado para aplicaciones generales y está estrechamente integrado con la biblioteca GNU C
  • tcmalloc es adecuado para entornos multiproceso de alta concurrencia y reduce la competencia mediante el almacenamiento en caché local de subprocesos.
  • jemalloc es adecuado para escenarios con alta escalabilidad y requisitos de baja fragmentación, proporcionando funciones y estadísticas avanzadas.

Supongo que te gusta

Origin blog.csdn.net/GG_Bruse/article/details/131742920
Recomendado
Clasificación