Loment
ES

Sistema

LomentGC

Cuando un programa Loment libera memoria, no le pasa todo el trabajo a un recolector. Cada lugar que asigna queda asignado, en tiempo de compilación, a una de cuatro estrategias — y cuánto se deja al recolector del runtime es un número escrito en el binario, y no una afirmación en un documento.

Leer el artículo

La escala

De la más fuerte a la más débil: el compilador toma el nivel más fuerte que puede demostrar. Demostrar más, pagar menos. El precio de cada nivel se escribe a su lado en lugar de dejarlo en una nota al pie.

L0 — estático

La asignación es de tamaño literal, y el puntero solo se pasa entero como primer argumento de una carga, un almacenamiento o una operación atómica. Nunca llega al montículo, así que no hay nada que recuperar. El precio: un marco de pila más grande.

L1 — último uso

La misma forma, declarada en el nivel superior del cuerpo de una función, así que el último uso es contable. El compilador inserta un free antes del primer retorno posterior a él. El precio: en una vía de retorno temprano el punto de inserción puede pasarse por alto, y esa vía fuga una vez. Una fuga, no un bug.

L2 — épocas de bloque

Dentro del cuerpo de un bucle, cada asignación es un local que nunca sale del cuerpo. El final del cuerpo devuelve toda la región de una vez, rebobinando el frente del asignador — sin lista, sin barrido. La época se infiere de la estructura de bloque y del escape, no se escribe a mano. El precio: si una asignación del cuerpo escapa, todo el bloque debe permanecer.

L3 — runtime

Todo lo que el compilador no pudo demostrar. El coste pleno del recolector.

Tres niveles

Un programa selecciona hasta qué punto de la escala está dispuesto a bajar, con una línea en el archivo del que parte.

choose gc_manual

El valor por defecto. Nada se recupera automáticamente; el programa gestiona su propia memoria. No escribir ninguna línea da esto — el valor por defecto es el comportamiento que existía antes de que el ajuste existiera, que es la regla que sigue cada ajuste.

choose gc_auto

Un recolector en tiempo de ejecución recupera: marcado y barrido con raíces precisas, disparado por el número de bytes asignados.

choose gc_auto_alpha

Los cuatro peldaños están activos, así que el recolector solo ve lo que los peldaños de tiempo de compilación no pudieron demostrar — y es adaptativo: el umbral sigue al conjunto vivo, con un disparador por espacio, así que el techo no es una fracción fija del arena.

Un límite que se enuncia en lugar de descubrirse. Bajo gc_auto el disparador es de 32 KiB fijos mientras el arena es de 64 KiB, así que un programa cuyo conjunto vivo supera aproximadamente 32 KB se queda sin memoria igualmente. Cerrar esa brecha es para lo que sirven el recolector adaptativo, y el generacional bajo gc_auto_alpha.

Dentro de L3

El nivel de runtime tampoco es un único algoritmo. Creció en tres pasos, cada uno de los cuales tuvo que superar al anterior en un programa que el proyecto podía medir.

Marcado y barrido

Las raíces son precisas — la tabla de raíces guarda direcciones de ranura — mientras que el interior de un bloque se trata de forma conservadora, porque alloc no está tipado y nada registra qué contiene un bloque.

Disparo adaptativo

Un umbral fijo tiene que adivinar cuánto crecerá el conjunto vivo. Uno adaptativo lo lee: recolecta cuando el conjunto vivo se ha duplicado, o cuando escasea el espacio.

Generacional

Dos generaciones, cortadas por dirección en lugar de por edad del objeto — un recolector que no mueve promueve al final de cada recolección menor avanzando una frontera. Una tabla de tarjetas y una barrera de escritura capturan los punteros de viejo a joven. Lo que ahorra es la pasada de marcado y propagación, que entonces cubre solo la generación joven.

Por qué queda en el binario

Cada unidad exporta un resumen legible por máquina — un objeto de forma — que lleva gc_ladder: cuántos sitios de asignación tomó cada nivel, más el total. Los cuatro recuentos deben sumar el total, y dos programas independientes comprueban esa aritmética sin leer una línea de código fuente. Así, «cómo se gestiona la memoria de este programa» es un número en la salida de compilación en lugar de una afirmación en un documento.

Lo que se afirma, y lo que no. La escala está implementada dos veces — el compilador de referencia y el autoalojado — y los dos deben coincidir byte por byte. Las mediciones detrás de los peldaños vienen de los propios criterios del proyecto, que son programas pequeños; el recolector no se ha ejecutado contra una carga de trabajo real, y eso vale la pena saberlo antes de leer nada de esto como una afirmación de rendimiento.