1P by GN⁺ 2일전 | ★ favorite | 댓글 1개
  • Ghostty 터미널 에뮬레이터에서 장기간 실행 시 수십 GB의 메모리를 점유하는 심각한 누수 현상이 발견됨
  • 문제의 원인은 PageList 구조체의 비표준 메모리 페이지 재사용 로직에서 munmap이 호출되지 않아 해제되지 않은 메모리가 누적된 것
  • Claude Code CLI가 다중 코드포인트 그래프 출력을 자주 생성하면서, 비표준 페이지 사용 빈도가 높아져 누수가 대규모로 드러남
  • 수정은 비표준 페이지를 재사용하지 않고 즉시 해제하도록 변경되었으며, macOS의 VM 태그 기능을 활용해 누수 추적 및 검증이 이루어짐
  • 이 수정은 Ghostty의 가장 큰 누수 문제 해결로 평가되며, 향후 릴리스(1.3)에 포함될 예정

Ghostty의 메모리 누수 개요

  • 일부 사용자가 Ghostty가 장시간 실행 후 37GB 이상의 메모리를 사용하는 사례를 보고
    • 누수는 최소 버전 1.0부터 존재했으며, 최근 CLI 앱들이 특정 조건을 충족해 문제를 노출
  • 수정 사항은 이미 GitHub에 병합되었고, nightly 빌드 및 1.3 정식 릴리스에 포함 예정

PageList 구조와 메모리 관리 방식

  • Ghostty는 터미널 내용을 저장하기 위해 PageList라는 이중 연결 리스트 구조를 사용
    • 각 페이지는 문자, 스타일, 하이퍼링크 등의 데이터를 포함
  • 페이지는 mmap으로 할당되며, 표준 크기 페이지 풀(pool) 을 통해 재사용
    • 표준 크기 이하 페이지는 풀로 반환
    • 비표준 크기 페이지는 직접 munmap으로 해제해야 함
  • 이 구조 자체는 정상적이지만, 최적화 로직의 버그로 인해 누수가 발생

Scrollback 최적화와 버그 발생 원인

  • Ghostty는 scrollback-limit을 초과하면 가장 오래된 페이지를 재사용하는 최적화를 수행
    • 새 페이지 할당 없이 포인터만 조정해 성능 향상
  • 문제는 이 과정에서 비표준 페이지의 메타데이터만 표준 크기로 변경하고 실제 메모리는 그대로 둔 점
    • 이후 해제 시 표준 페이지로 오인되어 munmap이 호출되지 않음
  • 이로 인해 비표준 페이지가 해제되지 않고 누적, 장기 실행 시 대규모 메모리 누수 발생

Claude Code와 누수의 대규모 노출

  • Claude Code CLI가 다중 코드포인트 그래프 출력을 자주 생성해 비표준 페이지 사용 빈도 증가
    • 또한 스크롤백 출력이 많아 누수가 빠르게 누적
  • Ghostty의 설계상 비표준 페이지는 드물게 발생해야 하지만, Claude Code의 특성으로 인해 누수가 대량으로 재현
  • 개발자는 이 버그가 Claude Code의 문제가 아니라 Ghostty 내부 로직의 결함임을 명시

수정 내용

  • 해결책은 비표준 페이지를 재사용하지 않고 즉시 해제(munmap) 하는 방식
    • 스크롤백 중 비표준 페이지 발견 시 새 표준 페이지를 풀에서 재할당
  • 일부 사용자는 비표준 페이지 재사용 전략을 제안했으나, 현재는 단순하고 안전한 수정이 우선 적용
  • 수정 코드 예시:
    if (first.data.memory.len > std_size) {
        self.destroyNode(first);
        break :prune;
    }
    

VM 태그를 활용한 누수 추적

  • macOS의 Mach 커널 VM 태그 기능을 사용해 PageList 메모리 할당에 특정 태그를 부여
    • 디버깅 시 Ghostty의 메모리 영역을 명확히 식별 가능
    • 누수 원인 추적 및 수정 검증에 큰 도움을 줌
  • 이 기능으로 PageList 관련 메모리 해제 여부를 시각적으로 확인 가능

Ghostty의 메모리 누수 방지 체계

  • Ghostty는 다양한 방식으로 누수를 탐지 및 방지
    • 디버그 빌드와 단위 테스트에서 Zig의 누수 감지 할당자 사용
    • CI에서 valgrind로 전체 테스트 수행
    • macOS Instruments로 Swift 코드 누수 검사
    • GTK 관련 PR은 Valgrind GUI 테스트로 검증
  • 이번 누수는 특정 조건에서만 발생해 기존 테스트로는 재현되지 않았음
    • 새 테스트 케이스가 추가되어 회귀 방지 확보

결론

  • 이번 문제는 Ghostty에서 가장 큰 규모의 메모리 누수로 확인된 사례
  • 수정 후에도 사용자 보고와 재현 테스트를 통해 지속적인 모니터링 예정
  • 커뮤니티의 진단 데이터와 재현 사례 제공이 문제 해결에 결정적 역할
  • 재현 가능한 환경 확보가 메모리 누수 해결의 핵심임을 강조
Hacker News 의견들
  • 정말 반가운 소식임. 문제 해결에 참여한 모든 사람들에게 박수를 보냄
    지난주에 이미 이 스레드에서 언급된 버그였음
    Claude Code가 이 버그를 더 많은 사용자에게 노출시킨 계기가 된 것 같지만, 나처럼 Claude Code를 전혀 쓰지 않았는데도 같은 문제를 겪은 사람도 있었음
    페이지가 ‘비표준(non-standard)’으로 분류되는 기준이 생각보다 흑백논리적이지 않음
    scrollback-limit = 0 같은 설정을 쓴 사람들에게는 누수가 더 자주 발생했을 수도 있다고 생각함
    수정된 방식이 불필요하게 비표준 페이지를 삭제 후 재생성할 수도 있어서, 이미 비표준인 오래된 페이지를 재활용할 수 있지 않았을까 하는 아쉬움이 있음

    • 그 부분은 블로그 포스트에서 이미 다뤄졌음
      PageList의 동작 방식은 예전부터 같았고, 버그가 있을 때도 용량 조정 중 잘못된 크기를 봤을 뿐임
      성능 체감에는 변화가 없을 것임
      제안한 대안도 고려했지만, 현재의 접근 방식이 벤치마크 데이터로 충분히 뒷받침되고 있음
      나도 생각을 바꿀 여지는 있지만, 이번엔 세계관 자체를 바꾸기보단 누수 수정에 집중했음
    • 베타 단계에서 이슈를 발견해 제보할 수 있었던 게 행운이었음
      실제로 segfault를 유발하는 재현 가능한 버그였음
    • 참고로, Claude Code 덕분에 CLI가 다시 매력적으로 느껴짐
      지난 20년간 그 어떤 것보다 CLI를 새롭게 보이게 함
    • 메모리 누수 관련 스레드는 여기에 있음
  • 훌륭한 글이었음. mitchellh에게 Ghostty를 만들어줘서 감사함
    작년에 전환했는데 후회한 적이 없음
    다만 수정이 몇 달 뒤의 기능 릴리스에 포함된다는 게 조금 의외였음
    버그 수정 릴리스에 포함될 줄 알았음

    • 이미 최신 nightly build에 포함되어 있음
  • 페이지 이야기가 나오자마자 “아, 메모리 풀링이구나” 싶었고, “링 버퍼겠네”라고 생각했는데 역시 scrollback 재사용이었음
    버그 위치도 바로 짐작했음 — 페이지 메모리를 제대로 해제하지 않은 부분이었음
    메모리 정렬 다이어그램도 멋졌음
    새로운 시도를 할 때마다 누수 가능성이 생긴다는 걸 다시 상기하게 됨

  • 이번 주에 Ghostty로 옮겼는데, 터미널 UI 앱 개발 중 OOM 크래시를 겪었음
    탭바에 UTF8 아이콘을 쓰는 구조였는데, 터미널 크기 조정 시 바로 크래시가 발생했음
    재현이 쉬워서 버그 리포트를 준비 중이었는데, 블로그 포스트에서 설명한 문제와 매우 유사해 보임
    해결되길 기대 중임

  • @mitchellh에게 질문함 — 메모리 시각화는 어떤 도구로 만들었는지, 그리고 웹사이트가 모바일에서도 잘 작동하던데 스택 구성이 궁금함

    • Opus 4.5로 생성된 정적 HTML/CSS를 사용했음
      시각화용 코드는 일회성이라 품질보다 정확성만 검증함
      블로그 포스트별로 네임스페이스를 분리해 재사용하지 않음
      구현이 이상한 짓(예: 비트코인 채굴, 비밀 유출 등)을 하지 않는지만 확인함
      핵심은 정보 전달이며, 이런 다이어그램이 내용을 훨씬 이해하기 쉽게 만들어줌
  • Ghostty 개발을 계속 지켜보고 있음
    약간의 오버엔지니어링 느낌이 있긴 하지만, 이런 버그 포스트모템은 장인정신을 사랑하는 사람에게 매우 가치 있는 자료임

    • 어떤 면에서 오버엔지니어링이라고 느꼈는지 궁금함
  • Rust 기반 터미널이라면 이런 구현을 어떻게 성능 손실 없이 처리할지 궁금함

  • Ghostty나 터미널 에뮬레이터를 잘 모르는 입장에서도 이해하기 쉬운 글이었음
    접근성이 높고 친절한 설명이 인상적이었음

  • 재현 가능한 버그 리포트의 중요성을 다시 느꼈음

  • 누군가 “Rust를 썼으면 이런 일은 없었을 것”이라고 말할 것 같아 기다리는 중임

    • 오래 기다릴 듯함
      Rust는 ‘메모리 누수 안전성(leak safety)’ 을 언어 차원에서 보장하지 않음
      안전한 Rust 코드도 메모리를 누수시킬 수 있음 — 단, 이는 안전성 문제는 아님
      표준 API에서도 Box::leak처럼 명시적으로 누수를 허용함
      Rust는 단지 의도치 않은 누수를 만들기 어렵게 할 뿐, 완전히 막지는 않음