Generational Garbage Collector

Archivo: src/gc.rs (1000 líneas)

¿Por qué existe?

Implementa un garbage collector generacional diseñado para reemplazar Arc en ForjaFast. En lugar de contar referencias y liberar memoria individualmente, el GC administra el heap por generaciones: los objetos nuevos viven en la young generation (nursery) y los que sobreviven suficientes colecciones se promueven a la old generation. La idea es que la mayoría de los objetos mueren jóvenes, así que la colección frecuente opera sobre un espacio pequeño y barato.

Arquitectura

El GC separa la memoria en tres bump allocators: nursery (young generation), survivor (espacio intermedio de copia) y old_memory (old generation). Cada objeto comienza su vida en el nursery con bump allocation (sin free list), y las colecciones copian los objetos marcados entre espacios aplicando promoción por edad: un objeto se vuelve Generation::Old cuando su edad alcanza el promotion_age (por defecto 3).

ComponenteDescripción
GcHeaderHeader de cada objeto: size, alloc_size (header + data + padding), bit de marked, age (colecciones young sobrevividas) y generation
GenerationEnumeración Young / Old que clasifica a cada objeto
GcRefPuntero seguro (copy) a un objeto del heap GC, con acceso a header() y data_ptr()
BumpAllocatorAsignador por bump sin free list: alloc, reset, for_each_object y copy_marked_to con promoción
GenerationalGCEl GC completo: nursery + survivor + old, raíces, remembered set, colecciones young/full y métricas
GcStatsEstadísticas: conteos de colecciones, bytes usados por generación, raíces, remembered set, total_allocated y total_freed

Write Barriers

El GC generacional requiere write barriers para rastrear referencias old→young. Cuando un objeto de la old generation es mutado para apuntar a un objeto young, la VM debe llamar a write_barrier (o write_barrier_raw) para registrar el objeto en el remembered set. Sin esto, la young collection no encontraría esos objetos young y los liberaría prematuramente (use-after-free).

Colecciones

ColecciónEstrategia
young_collection()Mark desde raíces + remembered set, copia los marcados del nursery y del survivor al survivor temporal (con promoción por edad), resetea el nursery. Se dispara cuando el nursery llega al 80% (should_collect_young).
full_collection()Mark desde raíces + remembered set, promueve los supervivientes del survivor a old, compacta la old generation, resetea nursery y survivor, y limpia el remembered set (los write barriers de la VM lo re-registran).
mark()Tracing conservador: escanea el área de datos de cada objeto palabra por palabra buscando valores que parezcan punteros al heap GC y los empuja al mark stack.

Cómo se integra

La VM ForjaFast lo usa para la gestión de memoria del runtime. La integración exige que la VM llame a write_barrier en cualquier SetField, SetIndex, asignación de variables que contengan referencias GC, y en retornos de punteros GC desde FFI. El patrón típico es mutar primero y notificar después:

set_field(obj, offset, new_value); // mutar primero
gc.write_barrier(&obj, offset);    // notificar al GC
⚠️ Sin write barriers, una referencia old→young que se cree durante la ejecución hace que la young collection libere objetos vivos: use-after-free.
💡 Los umbrales se calculan al 80% del tamaño de cada generación (nursery_threshold y old_threshold), y los tamaños por defecto son 4 MB de nursery, 4 MB de survivor y 16 MB de old (with_sizes permite personalizarlos).