- 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内部ロジックの欠陥であると明示している
修正内容
VMタグを活用したリーク追跡
- macOSのMachカーネルVMタグ機能を使い、PageListメモリ割り当てに特定タグを付与
- デバッグ時にGhosttyのメモリ領域を明確に識別可能
- リーク原因の追跡と修正検証に大きく役立った
- この機能により、PageList関連メモリが解放されたかどうかを視覚的に確認できる
Ghosttyのメモリリーク防止体制
- Ghosttyはさまざまな方法でリークを検知・防止している
- デバッグビルドと単体テストでZigのリーク検知アロケータを使用
- CIでvalgrindによる全体テストを実施
- macOS InstrumentsでSwiftコードのリーク検査
- GTK関連PRはValgrind GUIテストで検証
- 今回のリークは特定条件でのみ発生したため、既存テストでは再現されなかった
- 新しいテストケースが追加され、回帰防止が確保された
結論
- 今回の問題は、Ghosttyでこれまでで最大規模のメモリリークと確認された事例
- 修正後もユーザー報告と再現テストを通じて継続的に監視する予定
- コミュニティによる診断データと再現事例の提供が問題解決に決定的な役割を果たした
- 再現可能な環境の確保がメモリリーク解決の核心であることを強調している
1件のコメント
Hacker News の意見
本当にうれしいニュース。問題解決に関わったすべての人に拍手を送りたい
先週すでにこのスレッドでも言及されていたバグだった
Claude Code がこのバグをより多くのユーザーの目に触れさせるきっかけになったようだが、私のように Claude Code をまったく使っていなくても同じ問題に遭遇した人もいた
ページが「非標準(non-standard)」に分類される基準は、思ったほど白黒はっきりしたものではない
また、
scrollback-limit = 0のような設定を使っていた人には、リークがより頻繁に発生していた可能性もあると思う修正方法によっては不要に非標準ページを削除して再生成してしまうこともありそうで、すでに非標準になっている古いページを再利用できなかったのか、という惜しさはある
PageList の動作方式は以前から同じで、バグがあったときも容量調整中に誤ったサイズを見ていただけだった
体感できるような性能変化はないはず
提案された代替案も検討したが、現在のアプローチはベンチマークデータで十分に裏づけられている
私も考えを変える余地はあるが、今回は世界観そのものを変えるよりリーク修正に集中した
実際、segfault を引き起こす再現可能なバグだった
この20年で何よりも CLI を新鮮に見せてくれた
素晴らしい記事だった。Ghostty を作ってくれた mitchellh に感謝
去年乗り換えたが、一度も後悔していない
ただ、修正が数か月後の機能リリースに含まれるというのは少し意外だった
バグ修正リリースに入るものだと思っていた
ページの話が出た瞬間に「なるほど、メモリプーリングか」と思い、「リングバッファだな」と予想したが、やはり scrollback の再利用だった
バグの場所もすぐ見当がついた —— ページメモリを正しく解放していない箇所だった
メモリアラインメントの図も見事だった
新しいことを試すたびにリークの可能性が生まれると改めて思い知らされる
今週 Ghostty に移行したが、ターミナル UI アプリの開発中に OOM クラッシュ を経験した
タブバーで UTF8 アイコンを使う構成だったが、ターミナルのサイズを変更するとすぐにクラッシュした
再現が簡単だったのでバグレポートを準備していたが、ブログ記事で説明されている問題と非常によく似ているように見える
解決されることを期待している
@mitchellh に質問した — メモリ可視化はどのツールで作ったのか、そしてウェブサイトがモバイルでもよく動いていたので、スタック構成が気になる
可視化用コードは使い捨てなので、品質よりも正確性だけを確認した
ブログ記事ごとに名前空間を分けており、再利用はしていない
実装が妙なこと(たとえばビットコインのマイニングや秘密情報の流出など)をしないかだけ確認した
核心は情報を伝えることであり、こうした図は内容をずっと理解しやすくしてくれる
Ghostty の開発はずっと追っている
少しオーバーエンジニアリングっぽさはあるが、こういうバグのポストモーテムはクラフトマンシップを愛する人にとって非常に価値のある資料だ
Rust ベースのターミナルなら、こういう実装を性能低下なしでどう処理するのか気になる
Ghostty やターミナルエミュレータに詳しくない立場でも理解しやすい記事だった
とっつきやすく親切な説明が印象的だった
再現可能なバグレポートの重要性を改めて感じた
誰かが「Rust を使っていればこんなことは起きなかった」と言い出すのを待っている
Rust は**「メモリリーク安全性(leak safety)」を言語レベルで保証していない
安全な Rust コードでもメモリリークは起こせる —— ただし、それは安全性の問題ではない
標準 API にも Box::leak のように明示的にリークを許すものがある
Rust は単に意図しないリーク**を起こしにくくするだけで、完全に防ぐわけではない