
2025/12/17 19:45
Vm.overcommit_memory=2 is the right setting for servers
RSS: https://news.ycombinator.com/rss
要約▶
Japanese Translation:
Linux カーネルの
vm.overcommit_memory sysctl は、メモリ割り当てが保証されるか単なる約束にすぎないかを決定します。overcommit が有効になっている場合(vm.overcommit_memory=1、デフォルト設定)、brk(2) や mmap(2) などの呼び出しは常に成功しますが、実際の物理ページは最初のページフォルト時にのみ確保されます。この遅延は、成功した割り当てを後で OOM キラーを起動する可能性のある延期された約束へと変え、実際の割り当て失敗を隠蔽し、スタックトレースなしに静かに SIGKILL が発生する原因となります。Redis は overcommit が無効の場合に「Memory overcommit must be enabled! …」という警告を出しますが、この警告は根本的な問題(例:jemalloc issue #1328)を隠す可能性があります。この記事では、overcommit に依存するコードは責任を開発者からカーネルへ移転し、メモリ集約型サービスでの静かなクラッシュを招くと主張しています。
overcommit を有効にするには、
/etc/sysctl.conf に vm.overcommit_memory = 1 を追加して再起動するか、あるいは sysctl vm.overcommit_memory=1 を実行します。適切なエンジニアリングでは、低メモリ状態を明示的に検出し、overcommit 警告に頼らずに割り当て時に開発者へ即座にフィードバックするべきです。本文
Linux カーネルには、メモリ割り当ての挙動を調整できる機能があります。
それは
vm.overcommit_memory という sysctl パラメータです。
overcommit を有効にすると(残念ながらデフォルトでこの設定になっています)、カーネルはプログラムがヒープサイズを増やすために
brk(2) や mmap(2) を呼び出した際、実際にメモリが利用可能かどうかに関わらずマッピングを返します。一見すると便利そうです。
しかし実際にはそうではありません。overcommit はアプリケーション開発者にとって手軽ですが、メモリ割り当ての契約そのものを根本的に変えてしまいます。成功した割り当てはもはや「実際のリソースを原子操作で確保した」ことを意味しなくなります。返されるマッピングは「遅延保証」のようなもので、ページフォルトハンドラがメモリに初めてアクセスされたときにだけ実際に満たされます。この違いは重要です。overcommit は失敗を即座に検出するトランザクションモデル(fail‑fast)から、失敗が発生した時点ではなく後でのみ捕捉される best‑effort モデルへと置き換えるためです。
実際にどのように遅延が働くかを理解するには、プログラムが
malloc(3) を呼び出して新しいメモリ領域を確保しようとしたときに何が起こるかを考えてみましょう。高レベルでは、アロケータはカーネルに対して追加の仮想アドレス空間(VMA: Virtual Memory Area)を要求するため brk(2) または mmap(2) を呼び出します。
overcommit が無効になっているシステムでは、カーネルは割り当てが成功する前に十分なバックングメモリ(物理メモリ)が確保できることを保証します。対照的に overcommit が有効の場合、カーネルは VMA オブジェクトだけを作成し、バックングメモリの利用可能性は保証しません。その結果、マッピングは即座に成功したように見えますが、実際にはその要求が最終的に満たせるかどうかは分からない状態です。
「成功」と「バックングメモリの確保」が切り離されると、割り当て失敗を正しく処理することが不可能になります。プログラムはカーネルが実際に要求を満たせるかどうか判断する前に、割り当てが成功したものとして扱わざるを得ません。overcommit を無効にすれば、割り当て時に受け入れ制御(admission control)が行われます。これにより、割り当ては即座に失敗するか、バックングメモリが保証された状態で成功します。
失敗の局所性はデバッグにとって重要
割り当てが早期に失敗すると、その失敗は要求と同期しているため、デバッグが格段に楽になります。もしプログラムが割り当て失敗でクラッシュした場合、失敗時点のコンテキスト(要求サイズ、割り当てを行っているサブシステム、必要な基礎操作)がすべて保持されます。
overcommit では、この局所性は設計上失われます。割り当てが成功したように見え、プログラムはメモリが利用可能だと仮定して進みます。しかし実際にアクセスされた時点でカーネルは OOM キラーを起動し、プロセスを即座に終了させることがあります。プログラム側から見ると「割り当て失敗」を処理する手段がなく、単なる
SIGKILL しか発生しません。運用者の観点ではスタックトレースが失われ、後で確認できるログだけが残ります。これらは多くの場合、何が起きたかを明確に描写していないことがあります。
「割り当てサイトでクラッシュをデバッグしたほうがいいですか?それとも非同期 OOM キルによって発生した障害を再構築する方がよいですか?」
overcommit は割り当て失敗を回復可能にしません。むしろ「報告できない」状態にしてしまいます。
ちょっとだけ注意すべき例:Redis
それでは、なぜこのことを書いているのか? overcommit のコストは技術的側面だけでなく、悪いエンジニアリング文化をも示しています。すなわち「正しさに対する責任」をアプリケーション開発者からカーネルへ移転してしまっているのです。
例として、overcommit を無効にした状態で Redis を起動すると、以下のような警告が出ます:
WARNING Memory overcommit must be enabled! Without it, a background save or replication may fail under low memory condition. Being disabled, it can also cause failures without low memory condition, see https://github.com/jemalloc/jemalloc/issues/1328. To fix this issue add ‘vm.overcommit_memory = 1’ to /etc/sysctl.conf and then reboot or run the command ‘sysctl vm.overcommit_memory=1’ for this to take effect.
この警告は、overcommit が無効になっていることを知らせるだけで、実際に必要なメモリ割り当てエラーを正しく処理できていないコードがあることを示唆しています。対策としては「overcommit を有効にする」よりも、「低メモリ状態を明示的に表現し、システム管理者がそれを理解・解決できるようにする」の方が適切です。