1 puntos por GN⁺ 2026-01-11 | 1 comentarios | Compartir por WhatsApp
  • En el emulador de terminal Ghostty se descubrió una fuga grave que, al ejecutarse durante mucho tiempo, podía consumir decenas de GB de memoria
  • La causa del problema fue que, en la lógica no estándar de reutilización de páginas de memoria de la estructura PageList, no se llamaba a munmap, por lo que la memoria no liberada se iba acumulando
  • Como Claude Code CLI genera con frecuencia salidas de gráficos con múltiples code points, aumentó la frecuencia de uso de páginas no estándar y la fuga se hizo visible a gran escala
  • La corrección cambió el comportamiento para no reutilizar las páginas no estándar y liberarlas de inmediato, y se usó la función de etiquetas VM de macOS para rastrear y verificar la fuga
  • Esta corrección se considera la solución al mayor problema de fuga en Ghostty y está prevista para incluirse en una próxima versión (1.3)

Resumen de la fuga de memoria en Ghostty

  • Algunos usuarios reportaron casos en los que Ghostty usaba más de 37 GB de memoria tras permanecer en ejecución por largos periodos
    • La fuga existía al menos desde la versión 1.0, y recientemente ciertas apps CLI cumplieron condiciones específicas que dejaron el problema al descubierto
  • La corrección ya fue fusionada en GitHub y estará incluida en las builds nightly y en la versión estable 1.3

Estructura de PageList y manejo de memoria

  • Ghostty usa una estructura de lista doblemente enlazada llamada PageList para almacenar el contenido de la terminal
    • Cada página incluye datos como caracteres, estilos e hipervínculos
  • Las páginas se asignan con mmap y se reutilizan mediante un pool de páginas de tamaño estándar
    • Las páginas iguales o menores al tamaño estándar regresan al pool
    • Las páginas de tamaño no estándar deben liberarse directamente con munmap
  • La estructura en sí funcionaba bien, pero una falla en la lógica de optimización provocó la fuga

Optimización de scrollback y causa del bug

  • Cuando Ghostty supera scrollback-limit, aplica una optimización para reutilizar la página más antigua
    • Esto mejora el rendimiento porque solo ajusta punteros sin asignar una página nueva
  • El problema fue que, en ese proceso, solo se cambiaban los metadatos de la página no estándar al tamaño estándar, mientras la memoria real permanecía igual
    • Después, al liberarla, se confundía con una página estándar y no se llamaba a munmap
  • Como resultado, las páginas no estándar no se liberaban y se acumulaban, causando una fuga masiva de memoria en ejecuciones prolongadas

Claude Code y la exposición masiva de la fuga

  • Claude Code CLI genera con frecuencia salidas de gráficos con múltiples code points, lo que incrementó el uso de páginas no estándar
    • Además, como produce mucho contenido de scrollback, la fuga se acumulaba rápidamente
  • En el diseño de Ghostty, las páginas no estándar deberían aparecer rara vez, pero por las características de Claude Code la fuga pudo reproducirse en gran volumen
  • El desarrollador aclaró que este bug no era un problema de Claude Code, sino una falla en la lógica interna de Ghostty

Qué se corrigió

  • La solución fue no reutilizar las páginas no estándar y liberarlas inmediatamente con munmap
    • Si durante el scrollback se encuentra una página no estándar, se reasigna una nueva página estándar desde el pool
  • Algunos usuarios propusieron una estrategia para reutilizar páginas no estándar, pero por ahora se priorizó una corrección simple y segura
  • Ejemplo del código corregido:
    if (first.data.memory.len > std_size) {
        self.destroyNode(first);
        break :prune;
    }
    

Rastreo de la fuga con etiquetas VM

  • Se usó la función de etiquetas VM del kernel Mach en macOS para asignar una etiqueta específica a las reservas de memoria de PageList
    • Esto permite identificar claramente las regiones de memoria de Ghostty durante la depuración
    • También ayudó mucho a rastrear la causa de la fuga y validar la corrección
  • Con esta función fue posible confirmar visualmente si la memoria relacionada con PageList se liberaba o no

Sistema de prevención de fugas de memoria en Ghostty

  • Ghostty detecta y previene fugas de varias maneras
    • En builds de depuración y pruebas unitarias usa el allocator de detección de fugas de Zig
    • En CI ejecuta todas las pruebas con valgrind
    • Usa macOS Instruments para inspeccionar fugas en código Swift
    • Los PR relacionados con GTK se validan con pruebas GUI de Valgrind
  • Esta fuga solo ocurría bajo condiciones específicas, por lo que no pudo reproducirse con las pruebas existentes
    • Se añadió un nuevo caso de prueba para evitar regresiones

Conclusión

  • Este problema fue identificado como el caso de fuga de memoria más grande detectado en Ghostty
  • Incluso después de la corrección, se seguirá monitoreando mediante reportes de usuarios y pruebas de reproducción
  • Los datos de diagnóstico y casos reproducibles aportados por la comunidad fueron decisivos para resolver el problema
  • Se enfatiza que contar con un entorno reproducible es clave para solucionar fugas de memoria

1 comentarios

 
GN⁺ 2026-01-11
Comentarios de Hacker News
  • De verdad es una noticia muy bienvenida. Aplausos para todas las personas que participaron en resolver el problema
    Ya era un bug que se había mencionado la semana pasada en este hilo
    Parece que Claude Code fue el detonante para que este bug quedara expuesto a más usuarios, pero también hubo personas como yo que nunca usaron Claude Code y aun así tuvieron el mismo problema
    El criterio para que una página sea clasificada como "no estándar (non-standard)" no es tan blanco o negro como uno pensaría
    Además, creo que para quienes usaban configuraciones como scrollback-limit = 0, la fuga pudo ocurrir con más frecuencia
    También queda la duda de si, con la forma en que se corrigió, no se estarán eliminando y recreando páginas no estándar innecesariamente, cuando quizá se podrían haber reutilizado páginas antiguas que ya eran no estándar

    • Eso ya se abordó en la publicación del blog
      La forma en que funciona PageList siempre fue la misma, y aun cuando existía el bug, durante el ajuste de capacidad solo se estaba viendo un tamaño incorrecto
      No debería haber cambios perceptibles en el rendimiento
      También se consideró la alternativa que propones, pero el enfoque actual está suficientemente respaldado por datos de benchmarks
      Yo también podría cambiar de opinión, pero esta vez me enfoqué en corregir la fuga en lugar de replantear por completo el modelo
    • Fue una suerte haber podido detectar el problema y reportarlo durante la etapa beta
      De hecho, era un bug reproducible que provocaba segfault
    • Por cierto, gracias a Claude Code la CLI vuelve a sentirse atractiva
      Ha hecho que la CLI se sienta más nueva que cualquier otra cosa en los últimos 20 años
    • El hilo sobre la fuga de memoria está aquí
  • Fue un gran artículo. Gracias a mitchellh por crear Ghostty
    Me cambié el año pasado y no me he arrepentido ni una vez
    Eso sí, me sorprendió un poco que la corrección vaya a incluirse en una versión de funciones dentro de varios meses
    Pensé que entraría en una versión de corrección de errores

    • Ya está incluida en la nightly build más reciente
  • En cuanto empezaron a hablar de páginas pensé: “ah, es memory pooling”, y luego: “seguro es un ring buffer”, y efectivamente era reutilización de scrollback
    También imaginé de inmediato dónde estaba el bug: en la parte donde no se liberaba correctamente la memoria de las páginas
    El diagrama de alineación de memoria también estuvo genial
    Me hizo recordar otra vez que cada intento nuevo trae consigo la posibilidad de fugas

  • Esta semana me cambié a Ghostty y, mientras desarrollaba una app de UI para terminal, sufrí un crash por OOM
    La estructura usaba íconos UTF8 en la barra de pestañas, y al redimensionar la terminal el crash ocurría de inmediato
    Era tan fácil de reproducir que ya estaba preparando un reporte de bug, pero se ve muy similar al problema explicado en la publicación del blog
    Espero que se resuelva

  • Le pregunté a @mitchellh qué herramienta usó para hacer la visualización de memoria y, como el sitio web también funcionaba bien en móvil, me dio curiosidad saber qué stack usa

    • Usé HTML/CSS estático generado con Opus 4.5
      El código para las visualizaciones era de un solo uso, así que validé la precisión más que la calidad
      Separé los namespaces por publicación del blog y no los reutilicé
      Solo verifiqué que la implementación no hiciera cosas raras (por ejemplo, minar bitcoin, filtrar secretos, etc.)
      La clave es transmitir la información, y este tipo de diagramas hace que el contenido sea mucho más fácil de entender
  • He seguido de cerca el desarrollo de Ghostty
    Tiene un ligero aire de sobreingeniería, pero este tipo de postmortems de bugs son material muy valioso para quienes aman la artesanía del software

    • Me da curiosidad saber en qué sentido te parece sobreingeniería
  • Me da curiosidad cómo se implementaría algo así en un terminal basado en Rust sin perder rendimiento

  • Incluso sin conocer bien Ghostty o los emuladores de terminal, fue un artículo fácil de entender
    Me impresionó lo accesible que era y lo amable de la explicación

  • Me recordó la importancia de los reportes de bugs reproducibles

  • Estoy esperando a que alguien diga: “si hubieran usado Rust, esto no habría pasado”

    • Probablemente vas a tener que esperar bastante
      Rust no garantiza a nivel de lenguaje la "seguridad ante fugas de memoria (leak safety)"
      Incluso código Rust seguro puede filtrar memoria — solo que eso no es un problema de seguridad
      La API estándar incluso permite fugas de forma explícita, como con Box::leak
      Rust simplemente hace que sea más difícil crear fugas no intencionales, pero no las impide por completo