【要注意】Claude Codeの孤児プロセスがメモリを食い尽くす問題と対処法

技術メモ

Claude Codeを日常的に使っていると、気づかないうちにシステムリソースが圧迫されていることがある。本記事では、実際に遭遇した「孤児プロセス」問題の調査結果と対処法を共有する。

発見の経緯

きっかけ

日常業務でClaude Codeを複数のターミナルで使用していたところ、ふと「現在起動しているClaudeはどれだけあるか」と確認したことが発端だった。

確認コマンド

ps aux | grep -i claude | grep -v grep

驚きの結果

user     51459  1.2  3.9 75117368 311800 ?     Sl   Jan19  17:25 claude --resume
user     68508  1.0  3.7 75869720 291220 ?     Sl   Jan19  14:12 claude --resume
user    321852  103 10.3 75371668 816196 pts/11 Rl+ 09:16   1:40 claude --resume
user    322159 34.1  6.4 74698316 507716 pts/12 Rl+ 09:17   0:06 claude
user   3635391  1.2 39.2 93552724 3082900 pts/8 Sl+ Jan14  98:00 claude
user   3725562  0.4  2.4 75583064 193532 ?     Sl   Jan15  31:49 claude --resume

6つのClaudeプロセスが起動しており、合計で約5GBのメモリを消費していた。


問題の状況分析

プロセスの分類

詳細を調査すると、プロセスは2種類に分類できた。

ps -eo pid,ppid,stat,tty,cmd | grep -i claude | grep -v grep
PID PPID TTY 状態 分類
51459 1 ? Sl 孤児プロセス
68508 1 ? Sl 孤児プロセス
3725562 1 ? Sl 孤児プロセス
321852 316963 pts/11 Sl+ 正常(アクティブ)
322159 316964 pts/12 Rl+ 正常(現在のセッション)
3635391 1262863 pts/8 Sl+ 正常(長時間稼働)

PPID=1 は、親プロセス(ターミナル)が終了した後、initプロセス(systemd)に引き取られたことを意味する。TTY=? は、制御端末が存在しないことを示す。

リソース消費の内訳

プロセス 起動日 メモリ 状態
PID 3635391 1月14日 3.0GB 6日以上稼働
PID 321852 本日 816MB アクティブ
PID 322159 本日 508MB 現在使用中
PID 51459 1月19日 312MB 孤児
PID 68508 1月19日 291MB 孤児
PID 3725562 1月15日 194MB 孤児

システムは42日間稼働しており、その間にプロセスが蓄積していた。


技術的調査

ファイルディスクリプタの確認

孤児プロセスのファイルディスクリプタを調査した。

ls -la /proc/51459/fd
lrwx------ 1 user user 64 Jan 20 09:19 0 -> /dev/pts/20 (deleted)
lrwx------ 1 user user 64 Jan 20 09:19 1 -> /dev/pts/20 (deleted)
lrwx------ 1 user user 64 Jan 20 09:19 2 -> /dev/pts/20 (deleted)

stdin/stdout/stderrがすべて (deleted) となっている。これは、ターミナル(pts/20)が閉じられた後もプロセスが終了せずに残っていることを示す。

シグナルマスクの解析

プロセスがどのシグナルを処理しているか確認した。

cat /proc/51459/status | grep -E "^(SigIgn|SigCgt)"
SigIgn: 0000000000001000
SigCgt: 00000001280004c8

これをデコードすると:

種別 シグナル
無視 SIGPIPE (13)
キャッチ SIGILL, SIGBUS, SIGFPE, SIGSEGV, SIGWINCH, SIGPWR
未処理 SIGHUP (1), SIGINT, SIGTERM など

SIGHUP(ターミナル切断シグナル)が未処理であることがわかった。通常、SIGHUPを受信するとプロセスは終了するが、何らかの理由でシグナルが配信されていない。

セッション情報の確認

cat /proc/51459/stat | awk '{print "Session ID:", $6, "Process Group:", $5}'
Session ID: 51175 Process Group: 51459

プロセスは独自のプロセスグループリーダーになっており、元のセッション(51175)はすでに存在しない。この状態ではSIGHUPが配信されない可能性がある。


原因の考察

なぜプロセスが終了しないのか

  1. ターミナルの閉じ方: ターミナルウィンドウを直接閉じると、SIGHUPの配信タイミングによってはプロセスが終了しない場合がある
  2. プロセスグループの分離: Claude Codeが独自のプロセスグループを作成しているため、親シェルからのシグナルが届かない
  3. ネイティブ実行ファイルの問題: Claude CodeはELF形式のネイティブ実行ファイルだが、内部的なシグナル処理に問題がある可能性

既知の問題(コミュニティ報告)

GitHubで複数の関連イシューが報告されている。

MCPサーバーの孤児プロセス

Claude Code does not properly clean up MCP server processes when exiting, leading to orphaned processes that continue consuming system resources indefinitely.

プロセス終了の未処理

メンテナーからの回答:

We’re shifting towards a native executable that does not run via node entry point, which should prevent this sort of issue.

プロセスグループの問題

The issue appears to be that Claude Code and its spawned background processes share the same process group. When Claude sends a signal to kill the process group, it kills itself too.


対処法

即時クリーンアップ

Linux環境の場合:

# TTYが「?」の孤児プロセスを検出して終了
ps aux | grep '[c]laude' | awk '$7 == "?" {print $2}' | xargs kill -9 2>/dev/null

確認コマンド:

# 孤児プロセスの数を確認
ps aux | grep '[c]laude' | awk '$7 == "?" {print $2}' | wc -l

# 全Claudeプロセスの一覧
ps aux | grep '[c]laude' | awk '{printf "PID: %s, TTY: %s, MEM: %s MB\n", $2, $7, $6/1024}'

定期クリーンアップの設定

crontabに追加(1時間ごと):

crontab -e
# 以下を追加
0 * * * * ps aux | grep '[c]laude' | awk '$7 == "?" {print $2}' | xargs kill -9 2>/dev/null

systemd timerを使用する場合:

~/.config/systemd/user/claude-cleanup.service:

[Unit]
Description=Cleanup orphaned Claude processes

[Service]
Type=oneshot
ExecStart=/bin/bash -c "ps aux | grep '[c]laude' | awk '$7 == \"?\" {print $2}' | xargs kill -9 2>/dev/null || true"

~/.config/systemd/user/claude-cleanup.timer:

[Unit]
Description=Run Claude cleanup hourly

[Timer]
OnCalendar=hourly
Persistent=true

[Install]
WantedBy=timers.target

有効化:

systemctl --user enable --now claude-cleanup.timer

予防策

1. 正しい終了方法を習慣化

  • /exit コマンドを使用
  • Ctrl+C で中断
  • ターミナルを閉じる前にClaudeが終了していることを確認

2. シェルのexit時にクリーンアップ

.bashrc または .zshrc に追加:

trap 'pkill -9 -P $$ claude 2>/dev/null' EXIT

3. 便利なエイリアスの設定

alias claude-cleanup='ps aux | grep "[c]laude" | awk "\$7 == \"?\" {print \$2}" | xargs kill -9 2>/dev/null; echo "Cleanup complete"'
alias claude-status='ps aux | grep "[c]laude" | awk "{printf \"PID: %s, TTY: %s, MEM: %.0f MB, TIME: %s\\n\", \$2, \$7, \$6/1024, \$10}"'

まとめ

項目 内容
問題 Claude Codeのプロセスがターミナル終了後も残り続ける
影響 メモリリーク(本環境では5GB消費)
原因 シグナルハンドリングとプロセスグループ管理の問題
ステータス 既知のバグとしてGitHubで報告済み
公式対応 ネイティブ実行ファイルへの移行で対応予定
ユーザー対応 定期クリーンアップスクリプトの導入

Claude Codeは強力なツールだが、長時間使用する環境では孤児プロセスの蓄積に注意が必要である。開発元は問題を認識しており、将来のバージョンで根本的な修正が行われる予定だ。それまでは、本記事で紹介したクリーンアップ手法を活用してリソースを管理することを推奨する。


調査環境:
– OS: Linux (RHEL系) 5.14.0
– Claude Code: v2.1.12
– システム稼働時間: 42日

コメント

タイトルとURLをコピーしました