CLAUDE LABEN
SUNSET — 旧 Workbench と実験的プロンプトツール API の廃止は明日8月17日です。残り1日となりましたLATEST — 最新は8月15日の v2.1.233 です。GitLab のマージリクエスト URL が --worktree と claude agents で扱えるようになり、MR は !N 形式で表示されますSECURITY — Windows で NT の \??\ デバイスプレフィックスを含むパスが UNC 検証をすり抜ける問題が直りました。NTLM 資格情報が漏れる経路が塞がれていますTODO — Opus 4.8・Sonnet 5・Fable 5・Mythos 5 以降では TodoWrite と Task 系ツールが既定で無効になりました。CLAUDE_CODE_ENABLE_TODO_TOOLS=1 で戻せますFORK — 8月14日の v2.1.232 で subagent_type: 'fork' が既定になり、会話全体とプロンプトキャッシュをそのまま引き継げるようになっていますMENTION — プロンプトで @ を打つと別のセッションを名前で呼び出せます。SendMessage がそのセッションへ直接届きますSUNSET — 旧 Workbench と実験的プロンプトツール API の廃止は明日8月17日です。残り1日となりましたLATEST — 最新は8月15日の v2.1.233 です。GitLab のマージリクエスト URL が --worktree と claude agents で扱えるようになり、MR は !N 形式で表示されますSECURITY — Windows で NT の \??\ デバイスプレフィックスを含むパスが UNC 検証をすり抜ける問題が直りました。NTLM 資格情報が漏れる経路が塞がれていますTODO — Opus 4.8・Sonnet 5・Fable 5・Mythos 5 以降では TodoWrite と Task 系ツールが既定で無効になりました。CLAUDE_CODE_ENABLE_TODO_TOOLS=1 で戻せますFORK — 8月14日の v2.1.232 で subagent_type: 'fork' が既定になり、会話全体とプロンプトキャッシュをそのまま引き継げるようになっていますMENTION — プロンプトで @ を打つと別のセッションを名前で呼び出せます。SendMessage がそのセッションへ直接届きます
記事一覧/Claude Code
Claude Code/2026-08-16中級

ビルドがメモリを食い尽くしたときの止まり方は、cgroup と ulimit で別物でした

Claude Code v2.1.233 で Bash ツールに memory cgroup 対応が入りました。上限を仮想アドレス空間に置く ulimit -v と、実メモリに置く cgroup では、止まり方も、そもそも止められるかどうかも変わります。Linux 上で実際に確かめた記録です。

Claude Code222Bash ツールメモリ7Linuxトラブルシューティング38

無人で回している定期タスクが、ビルドの途中で終わっていたことがあります。

真っ先に疑ったのはディスク不足でした。個人開発で App Store 向けのアプリと公開しているサイトを同じマシンでビルドしているため、ENOSPC は珍しい事故ではありません。けれどその時は空き容量が十分に残っていて、犯人はどうやら別にいました。

残る容疑者はメモリです。ビルドが一時的に数 GB を掴むと、同じマシンで動いている作業セッションごと巻き込まれます。

Claude Code v2.1.233 の変更履歴に、Bash ツールのコマンドを Linux の memory cgroup に入れる CLAUDE_CODE_TOOL_MEMORY_LIMIT が加わったと書かれていました。「暴走したビルドがセッションを止められないようにする」という説明です。

では自分の環境では上限をどこに置けばよいのか。そして上限を置けば本当にセッションを守れるのか。Ubuntu 22.04(カーネル 6.8)のサンドボックスで手を動かして確かめたところ、上限を置く場所によって「止まり方」がまったく別物になることが分かりました。

上限を「仮想アドレス空間」に置くか「実メモリ」に置くか

Linux でプロセスのメモリを縛る方法は、大きく2つに分かれます。

手段縛る対象超えたときの挙動設定の単位
ulimit -v(RLIMIT_AS)仮想アドレス空間の確保量確保の呼び出しが失敗する(プロセスは生きたまま)シェルとその子プロセス
cgroup v2 の memory.max実際に使っている物理メモリ回収しきれなければ OOM killer が SIGKILL を送るcgroup に入れたプロセス群

言葉にすると近く見えますが、動かすと差がはっきり出ます。前者は「これ以上は確保させない」と申し出を断る仕組みで、後者は「使いすぎたら殺す」仕組みです。

断られたプログラムがどう振る舞うかは、そのプログラム次第です。ここが今回いちばん腑に落ちた点でした。

ulimit -v で止めた Python は、例外として捕まえられた

まず ulimit -v を使い、少しずつメモリを掴む Python を走らせました。

( ulimit -v 524288; python3 -c "
b = []
n = 0
try:
    while True:
        b.append(bytearray(4 * 1024 * 1024))
        n += 4
except MemoryError:
    print(n)
" )

ulimit -v の単位は KiB なので、524288 は 512 MiB です。結果は3回とも同じでした。

492
492
492

上限 512 MiB に対して、実際に確保できたのは 492 MiB。差の 20 MiB ほどはインタプリタ自身が先に使っている分です。上限を 256 MiB にすると 232 MiB まで、1024 MiB にすると 1000 MiB まで確保できたので、この目減り分はおおむね一定でした。

大事なのは数字よりも、MemoryError として捕まえられたことです。プロセスは殺されず、except に入って自分で後始末できました。終了コードも自分で決められます。

つまり、Python で書いた処理スクリプトなら、ulimit -v は「行儀のよい失敗」を作れる手段になります。

同じ制限で、Node は起動すらできませんでした

同じ 512 MiB を Node.js(v22.22.3)に掛けたところ、様子がまったく違いました。

( ulimit -v 524288; node -e "console.log('started')" )

started は表示されず、終了コードは 133。標準エラー出力の先頭にはこう書かれていました。

# Fatal process out of memory: Failed to reserve virtual memory for CodeRange

try / catch は一切通っていません。V8 が起動時にまとめて仮想アドレス空間を予約するため、ビルドが始まる前に Node 自体が落ちていたのです。

どこが境目なのか、刻んで探しました。

ulimit -vnode -e "0" の終了コード
512 MiB133(起動失敗)
640 MiB133(起動失敗)
704 MiB133(起動失敗)
768 MiB0(起動する)
896 MiB0(起動する)

この環境では 768 MiB が起動の下限でした。空の node -e "0" を動かすだけで、これだけの仮想アドレス空間が要ります。

なお上限を 2 GiB まで上げると、今度は確保の失敗をきちんと捕まえられました。

caught@1056MiB Array buffer allocation failed

起動さえ通れば、実行中の確保失敗は例外になります。落ちていたのは起動時の予約だけでした。

ここから引き出せる結論は、実務的にはかなり強いものです。Node のツールチェーン(npm・Vite・webpack・Metro など)に ulimit -v で低い上限を掛けると、暴走を止めるどころか、正常なビルドまで起動時に殺してしまいます。 「メモリを 512 MB に制限してビルドさせよう」という素朴な発想は、この時点で成立しません。

CLAUDE_CODE_TOOL_MEMORY_LIMITulimit ではなく cgroup を選んでいるのは、この差を踏まえれば自然な設計だと感じました。

cgroup で止めると、失敗は「例外」ではなく SIGKILL になります

cgroup v2 の memory.max は物理メモリの使用量を見ます。仮想アドレス空間の予約は素通りするので、Node は普通に起動します。そのうえで、実際に使いすぎたときだけ OOM killer が働きます。

このとき送られるのは SIGKILL です。捕まえられません。シェルから見た終了コードは 137(128 + 9)になります。

bash -c 'sleep 20 & P=$!; kill -9 $P; wait $P'
echo $?
137

運用する側から見ると、この違いはログの読み方に直結します。

  • 終了コード 137 が出たら、まずメモリを疑う
  • 133 のような起動時の即死は、仮想アドレス空間の制限やコンテナ設定を疑う
  • MemoryError などの例外がログに残っているなら、プロセス自身は生きていた

再試行の設計も変わります。SIGKILL された処理は途中経過を残せないので、そのまま再実行すると同じ場所でもう一度殺されるだけです。上限を上げるか、並列数を下げるか、どちらかを選ぶ判断が先に要ります。

ひとつ正直に書いておきます。今回のサンドボックスは非特権ユーザーで動いており、/sys/fs/cgroup 配下にディレクトリを作ろうとすると Permission denied になりました。systemd-run --user もユーザーバスがなく使えません。そのため cgroup 側の停止は、終了コードの規約を確認するところまでに留めています。自分のマシンで試す場合は、systemd-run --user --scope -p MemoryMax=2G -- npm run build のように、まず捨ててよいビルドで一度殺されてみるのが確実です。

もうひとつ、2026年8月16日時点の状況として。CLAUDE_CODE_TOOL_MEMORY_LIMIT は変更履歴には記載がありますが、環境変数のリファレンスページにはまだ載っていません。値の書式は変わる可能性があるため、設定する前に一次情報を確認してください。この記事で断定しないのはそのためです。

自分の環境で最初に決める2つのこと

上限を入れると決めたら、決めるべきことは2つだけでした。

1つめは、上限値そのものです。 単体のビルドが平常時にどれだけ使うかを一度測り、その 1.5〜2 倍を置くのが出発点になります。加えて、同時に走りうるビルドの本数を掛けた合計が、マシンの実メモリを超えないことを確かめます。無人タスクを複数並べている環境では、この「掛け算」を忘れると、上限を入れたのにマシン全体が苦しくなります。

2つめは、失敗の見え方です。 137 で終わったコマンドを、ログのどこに、どう残すか。無人で回している場合、ここを決めていないと「なぜか終わっていた」という朝を繰り返します。無音の失敗そのものを止める設計は「成功」と記録されたのに成果がゼロだったで扱っています。Bash ツール側のエラーを切り分けたい場合はClaude Code の Bash ツール実行エラーの原因と対処が近い話です。

今日からできる最初の一歩としては、いちばん重いビルドを一度だけ /usr/bin/time -v 付きで走らせ、Maximum resident set size を控えておくことをおすすめします。上限値の議論は、その1行がないと始まりません。

私自身、ディスクの方ばかり見ていてメモリを測っていませんでした。同じ抜けをお持ちの方の役に立てば嬉しいです。お読みいただきありがとうございました。

シェア

お読みいただきありがとうございます

Claude Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • コピー&ペーストで使える実装コード付き
  • 毎日新しい上級ガイドを追加
  • ¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

もしこの記事がお役に立ちましたら、チップ(¥150)で応援いただけると大変励みになります。広告なしでの運営を続けるため、皆さまのご支援が大きな力になっています。

関連記事

Claude Code2026-07-16
夜間セッションの常駐メモリが朝には数GB — 蓄積の四つの出どころと、RSS を刻んで切り分ける
夜間に回した Claude Code のプロセスを朝に見ると、常駐メモリが数GBまで膨らんでいました。2.1.209 で塞がれた四つの蓄積源と、自分の構成に残る分を RSS の傾きで切り分ける方法、しきい値で畳む運用までを個人開発の目線で整理します。
Claude Code2026-07-01
Claude Code をアップデートしたら hook が発火しなくなった — ハイフン入り matcher の厳密一致化(v2.1.195)
Claude Code v2.1.195 でハイフンを含む hook matcher が部分一致から厳密一致に変わり、既存の PreToolUse フックが静かに発火しなくなりました。原因の切り分けと、壊れない matcher の書き方をまとめます。
Claude Code2026-06-27
無人で回し続けても重くならないか — Claude Code 長時間セッションのメモリ使用量を観測して頭打ちにする
長時間の無人セッションで Claude Code が徐々に重くなる問題を、ps による RSS 定点観測・ローリング基準値のウォッチドッグ・セグメント分割の3点で頭打ちにする運用設計を、観測スクリプトと before/after で具体的にまとめます。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →