**GhostTyの最大メモリリークを発見し修正する**

2026/01/11 3:58

**GhostTyの最大メモリリークを発見し修正する**

RSS: https://news.ycombinator.com/rss

要約

Japanese Translation:

Ghostty の長時間にわたるセッションは、

mmap
(スクロールバックバッファに使用される)で割り当てられたページが解放されないため、最大 37 GB の RAM をリークしていました。アプリはターミナルコンテンツを PageList に保存します。これは「標準」(プールから取得したもの)または「非標準」(
mmap
)のメモリページで構成される双方向リンクリストです。スクロールバックの削減時に、Ghostty は誤って最も古いページを新しいページとして再利用します:そのメタデータだけを「標準サイズ」にリサイズし、大きな
mmap
割り当てはそのまま残します。この再利用されたページが後で解放されると、Ghostty はそれを標準とみなし、
munmap
を呼び出す代わりにプールへ返却してしまい、メモリブロックがリークしたままとなります。
このバグは Ghostty 1.0 から存在しましたが、大量のスクロールバックバッファ(例:多くの絵文字とハイパーリンクを含む Claude Code など)を生成する CLI アプリでのみ顕在化し、非標準ページ割り当てをトリガーします。既存のリーク検出器は特定の実行時条件下で発生するため、検知できませんでした。
新しいテストが問題を再現しリークを確認しました。統合された修正では、削減中に 非標準ページを破棄
self.destroyNode(first)
)し、プールから新しい標準サイズのページで置き換えるようになっています。この修正は Ghostty 1.3(3 月)に組み込まれます。既に Nightly リリースにはパッチが含まれています。
さらに、macOS のメモリタグ付け(
mach.taggedPageAllocator(.application_specific_1)
)を追加し、PageList 割り当てにタグを付与して修正の検出と確認を簡素化しました。この更新により、長時間ターミナルセッションを実行するユーザー—特に重い CLI ワークロードを扱う開発者は――メモリ使用量が急増する問題が解消され、個人およびプロダクションでアプリに依存している組織の両方に対し、より安定かつ信頼性の高い Ghostty エクスペリエンスを提供します。

本文

数か月前、ユーザーから Ghostty が膨大なメモリを消費しているという報告が寄せられました。10 日間稼働した後に 37 GB を使用していたというケースです。
本日、修正が見つかりマージされたことをご報告します。本稿ではリークの原因、Ghostty の内部構造、および追跡方法を簡潔にまとめています。

このリークは Ghostty 1.0 以降に存在していましたが、最近になって特定の CLI アプリ(特に Claude Code)が発生させる条件で大規模に顕在化しました。トリガー条件が限定的だったため診断が難しくなっていた点が特徴です。
修正はマージ済みで nightly / tip リリースに反映されています。3 月のタグ付き 1.3 版にも含まれます。


PageList

バグを理解するにはまず Ghostty が端末メモリをどのように管理しているかを知る必要があります。Ghostty は PageList と呼ばれるデータ構造でターミナル内容(文字、スタイル、ハイパーリンク等)を保持します。

  • PageList – 端末コンテンツを格納するメモリページの双方向連結リスト。
Page 1   最も古いスクロールバック
Page 2
Page 3
Page 4   最新のアクティブ画面

「ページ」は単一の仮想メモリページではなく、ページ境界に揃えられた連続したメモリブロックです。サイズはシステムページ数の偶数倍で構成され、

mmap
で確保します。
mmap
は呼び出しが遅いため、頻繁なシステムコールを避けるためにメモリプールを利用しています。新しいページが必要になったらプールから取り出し、使い終わったら再度返却します。

プールは標準サイズのページのみを扱います。これは「一般的な配送箱」を購入するようなもので、ほとんどの荷物が同じ箱に収まるため効率化できます。ただし端末では時折標準ページより大きいメモリが必要になる場合があります。行数に絵文字やスタイル、ハイパーリンクが多く含まれると、大きなページを確保する必要があります。このようなケースではプールを経由せず

mmap
で直接非標準ページを割り当てます(稀なシナリオ)。

ページの割り当てタイプ

タイプサイズ再利用
標準ページ(プールから)固定破棄時にプールへ返却
非標準ページ (
mmap
直接)
可変(標準より大きい)
munmap
を呼び、再利用不可

ページを「解放」するときは次のような単純ロジックが適用されます。

if page <= 標準サイズ → プールへ返却
else                   → munmap で解放

これが Ghostty の端末メモリ管理の基本設計です。概念自体は正しく、最適化に伴うロジックバグがリークを生み出しました。


スクロールバックの削除

Ghostty にはスクロールバック制限設定があります。この上限に達すると古いページを削除してメモリを解放します。
大量データを高速で出力する際など、頻繁に発生するため

mmap
の呼び出しはコストが高くなります。そのため、上限に達したときは「最古のページをそのまま最新の位置へ再利用」する最適化を行っています。

スクロールバック削除:最古ページを再利用
前:上限に到達 → 先頭から削除し末尾へ再配置
後 :再利用されたページが末尾にある
Page 2 → 最古になる
Page 3
Page 4

この最適化は割り当てを行わず、ポインタ操作だけで済むため高速です。メタデータのクリア処理も行いますが、実際のメモリ領域はそのまま残します。


バグ

スクロールバック削除時にページサイズを標準サイズへ「再設定」していましたが、実際の

mmap
割り当ては変わっていませんでした。つまり、非標準(大きめ)メモリ領域を持つページを標準サイズと誤認し、PageList はそれをプールから取得したものだと考えていました。

非標準ページ確保
スクロールバックで再利用
BUG:メタデータは std_size に戻るが、mmap はそのまま
ページ解放時 → std_size とみなされ munmap が呼ばれない
結果:標準→非標準 → メモリリーク

最終的に端末を閉じた際などにページを破棄するとき、メモリは「標準サイズ」に見えるためプールに戻るとみなされ

munmap
が呼ばれません。これが典型的なリークです。

非標準ページは設計上稀であり、最適化の目的は標準ページを主流にすることです。そのため非標準ページが発生した場合には破棄して新しい標準ページを確保する方針でした。

Claude Code の影響

Claude Code の CLI は多くのマルチコードポイント文字(絵文字など)を出力し、Ghostty が頻繁に非標準ページを使用します。さらに主画面で大量スクロールバックが発生するため、リークが大きな量で顕在化しました。

このバグは Claude Code の設計上の問題ではなく、Ghostty に対して長年存在した欠陥を露呈させただけです。


修正

概念的には非常にシンプルです。非標準ページは再利用しないことです。
スクロールバック削除時に非標準ページが見つかったら、

munmap
で破棄し、プールから新しい標準サイズのページを割り当てます。

if (first.data.memory.len > std_size) {
    self.destroyNode(first);
    break :prune;
}

非標準ページを再利用して大きいメモリ領域を保持することも可能ですが、現時点では「標準ページが主流」という前提の下でシンプルに標準サイズへ戻す方針です。
より複雑な戦略(非標準ページ使用頻度の統計を取って動的に切り替える等)は検討中ですが、まずは確実にバグを修正することが最優先でした。


追加改善

修正作業と同時に macOS の Mach カーネル提供機能で仮想メモリタグをサポートしました。これにより PageList のメモリアロケーションに特定の識別子を付与でき、ツール上で簡単に可視化できます。

inline fn pageAllocator() Allocator {
    // テスト時は leak 検出用アロケータを使用
    if (builtin.is_test) return std.testing.allocator;

    // 非 macOS では標準 Zig ページアロケータを利用
    if (!builtin.target.os.tag.isDarwin()) return std.heap.page_allocator;

    // macOS ならメモリにタグ付けして core terminal 用に識別
    const mach = @import("../os/mach.zig");
    return mach.taggedPageAllocator(.application_specific_1);
}

macOS 上でメモリをデバッグすると、Ghostty の PageList メモリが特定のタグ付きとして表示されます。これによりリーク箇所の特定と修正後の動作確認が容易になりました。


Ghostty におけるリーク防止策

Ghostty では以下の手法でメモリリークを検出・回避しています。

  • デバッグビルドや単体テストではリーク検出 Zig アロケータを使用
  • CI は毎コミットごとに Valgrind をフルユニットテストスイートで実行し、リークだけでなく未定義メモリアクセスも検出
  • macOS GUI 版は Instruments で頻繁にリークチェック(特に Swift コードベース)
  • GTK 関連 PR は Valgrind(フル GUI)で実行し、単体テスト対象外の GTK コードパスのリークを確認

これまで有効でしたが、本件は非常に限定的な条件下でのみ発生したため既存テストでは検出できませんでした。マージされた PR には再現性のあるテストも追加され、将来の回帰防止になります。


結論

これまで Ghostty に報告された最大規模のメモリリークであり、複数ユーザーから確認された唯一のケースです。今後もメモリ関連レポートを監視し続けますが、再現性のあるテストこそが診断と修正への鍵となります。

@grishy さんには信頼できる再現環境を提供していただき、自身で解析できたことに感謝しています。彼らの分析も私の結論と一致し、再現実験により両者の理解が独立して確認できました。また、詳細な診断情報(フットプリント出力や VM リージョン数)を報告いただいた皆さんにも感謝します。コミュニティから得た手掛かりが PageList を特定する鍵となりました。

本稿は AI の支援なしで執筆されました。図表に関しては AI が一部補助しましたが、すべて人間がレビューし正確性を確認しています。テキスト内容は AI によって生成されたものではありません。

同じ日のほかのニュース

一覧に戻る →

2026/01/11 10:50

**Show HN:Ferrite – Rustで作られたマークダウンエディタ、ネイティブMermaid図描画機能付き**

## Japanese Translation: Ferrite は、egui で構築された軽量でネイティブな Rust テキストエディタで、Markdown、JSON、YAML、および TOML ファイルをサポートします。主な編集機能には、WYSIWYG Markdown 編集、ライブプレビュー、クリックで編集できる書式設定ボタン、40 以上の言語に対応した構文ハイライト、正規表現検索&置換、タブごとの Undo/Redo、およびインライン編集が可能な階層データ用トリービューがあります。 表示モードは Raw エディタ、レンダリングビュー、分割ビュー(可変サイズの区切り線付き)、Zen モード、Raw とレンダリングビュー間の双方向同期スクロールから構成されます。 MermaidJS ダイアグラム描画は完全に統合されており、11 種類のダイアグラムをサポートします;バージョン 0.2.1 では高度なシーケンス制御フローブロックとネストされた状態が追加されました。 ワークスペース機能:フォルダーをファイルツリーで開く、クイックスイッチャー(`Ctrl+P`)、検索‑イン‑ファイル(`Ctrl+Shift+F`)、Git 統合(ステータスアイコン、ステージング、コミット、プッシュ/プル、競合解決)およびセッション永続化により、タブ、カーソル位置、およびスクロールオフセットが再起動時に復元されます。 追加の UI オプションには、実行時切替可能なライト/ダークテーマ、ドキュメントアウトラインパネル、テーマ付き HTML へのエクスポートまたは HTML としてコピー、書式設定ツールバー、JSON/YAML をシェルコマンドでパイプするライブパイプライン、およびカスタム境界なしウィンドウモードがあります。 インストールはプリビルトバイナリ(Windows zip、macOS tar.gz、Linux .deb または tar.gz)またはソースビルド(`cargo build --release`)で利用可能です。Rust 1.70+ とプラットフォーム固有の依存関係が必要です。主なショートカット:ファイル操作は `Ctrl+N/O/S/W`、タブは `Ctrl+Tab/Shift+Tab`、クイックスイッチャーは `Ctrl+P`、フルスクリーンは `F11`、設定は `Ctrl+,` などです。 Ferrite は MIT ライセンスの下でオープンソースであり、Rust 1.70+、egui 0.28、comrak 0.22(Markdown パーシング)、syntect 5.1(構文ハイライト)、git2 0.19(Git 統合)に依存しています。

2026/01/11 1:56

**HNの投稿:** 「Claude Code を使って100冊の本との関連性を発見しました」

## Japanese Translation: **概要** 本文は、スタートアップのピボットが巧妙な洞察よりもむしろ絶望感から動かされることが多いと主張しています。後知恵バイアスがこれらの反応的シフトを事後的に戦略的計画として見せかけ、意図的な天才像を与える仕方を説明しています。代表例として、Odeo が新しいベンチャーへと変貌するケースが挙げられ、ピボット手法の実践的なテキストブック例として機能します。著者は将来のピボットも短期的圧力によって促される反応的なものに留まる可能性が高いと予測し、このパターンを認識することで、創業者・投資家・チームがスタートアップエコシステム内で戦略やリスクについて考える方法を再構築し、企業が方向転換する理由をより現実的に評価できるよう促すと述べています。

2026/01/11 8:45

Show HN:Librario――Google Books、ISBNDB などを統合する書籍メタデータ API ---

## Japanese Translation: > **Librario** は、Go で書かれた早期段階の AGPL ライセンス付き本メタデータ集約 API です。Google Books、ISBNDB、Hardcover からデータを取得し、Goodreads と Anna’s Archive を追加予定です。フィールド固有の戦略(例:タイトルスコアリング、表紙品質評価)で競合を解決し、マージ済み結果を PostgreSQL に保存します。パフォーマンス向上のためにキャッシュ層が追加されています。本サービスは現在プレ・アルファ段階で、小規模な VPS 上で稼働しており、サードパーティーの制限に達していません。バージョン 1.0 前にデータベーススキーマを再設計する作業が進行中で、元々 AI が生成した設計は不明瞭だったため SourceHut の開発者によって推進されています。Librario は著者の約 1,800 冊の本管理ニーズから始まり、現在は単一の JSON レスポンスに完全な本情報を提供します ― 例:`curl https://librario.example/api/9781328879943` がマージ済みメタデータを返します。コードは SourceHut (`https://sr.ht/~pagina394/librario/`) にホストされており、フィードバック・パッチ・貢献を歓迎しています。SourceHut コンサルティングとの 5 ヶ月間の関与により開発進捗は遅れましたが、コア機能は安定しています。

**GhostTyの最大メモリリークを発見し修正する** | そっか~ニュース