[インデックス 15958] ファイルの概要
このコミットは、Goランタイムにおけるメモリ管理の挙動を一時的に変更するものです。具体的には、ヒープ割り当て時にruntime.memlimit
のチェックを無視するように修正しています。これは、Go 1.1のリリースに向けて発生した特定のメモリ割り当ての問題(Go issue 5049)に対応するための暫定的な措置です。
コミット
commit 515353a290be7718783ce31987b74e8e515e6c2d
Author: Ian Lance Taylor <iant@golang.org>
Date: Tue Mar 26 14:01:12 2013 -0700
pkg/runtime: ignore runtime.memlimit when allocating heap
For Go 1.1, stop checking the rlimit, because it broke now
that mheap is allocated using SysAlloc. See issue 5049.
R=r
CC=golang-dev
https://golang.org/cl/7741050
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/515353a290be7718783ce31987b74e8e515e6c2d
元コミット内容
pkg/runtime: ignore runtime.memlimit when allocating heap
For Go 1.1, stop checking the rlimit, because it broke now
that mheap is allocated using SysAlloc. See issue 5049.
R=r
CC=golang-dev
https://golang.org/cl/7741050
変更の背景
このコミットは、Go 1.1のリリースを控えた時期に発生した、Goランタイムのメモリ管理に関する重要な問題(Go issue 5049)に対処するために導入されました。
Goランタイムは、プログラムが利用できるメモリ量を管理するために、オペレーティングシステム(OS)のrlimit
(リソース制限)設定を考慮していました。特に、runtime.memlimit()
関数は、このrlimit
からメモリ制限を取得し、Goのヒープ割り当ての最大サイズを決定するために使用されていました。
しかし、Go 1.1の変更により、Goのメモリヒープ(mheap
)の割り当て方法が変更され、SysAlloc
というシステムコールを通じてメモリが確保されるようになりました。この新しいSysAlloc
によるmheap
の割り当てが、既存のruntime.memlimit
のチェックと競合し、問題を引き起こすことが判明しました。具体的には、mheap
が仮想メモリを大量に(例えば256MB)予約しようとすると、厳格なulimit
設定下でGoプログラムがクラッシュする可能性がありました。
この問題は、Go 1.1の安定性とリリースを阻害する可能性があったため、一時的な回避策として、ヒープ割り当て時にruntime.memlimit
のチェックを無効にすることが決定されました。コミットメッセージにある「TODO(rsc): Fix after 1.1.」というコメントは、この変更が暫定的なものであり、Go 1.1リリース後に適切な解決策が検討されるべきであることを示しています。
前提知識の解説
このコミットを理解するためには、以下のGoランタイムとOSのメモリ管理に関する概念を理解しておく必要があります。
-
rlimit
(Resource Limit): Unix系OSにおけるリソース制限のメカニズムです。プロセスが利用できるCPU時間、ファイルサイズ、メモリ量などのシステムリソースに上限を設定するために使用されます。ulimit
コマンドを通じてユーザーが設定することも可能です。メモリ関連では、RLIMIT_AS
(アドレス空間の最大サイズ)やRLIMIT_DATA
(データセグメントの最大サイズ)などが関連します。Goランタイムは、これらのrlimit
設定を読み取り、自身のメモリ管理に反映させようとします。 -
mheap
(Memory Heap): Goランタイムの主要なメモリ管理コンポーネントの一つです。Goプログラムが動的に確保するメモリ(例えば、make
やnew
で作成されるオブジェクト)は、このmheap
から割り当てられます。mheap
は、メモリをページ単位で管理し、OSから仮想メモリを要求し、それをGoのヒープとして利用可能な状態に保ちます。 -
SysAlloc
: GoランタイムがOSから直接メモリを要求するために使用する低レベルの関数です。これは通常、mmap
やVirtualAlloc
といったOS固有のシステムコールをラップしており、仮想メモリ空間を確保する役割を担います。Go 1.1でmheap
がSysAlloc
を使ってメモリを割り当てるようになったことが、今回の問題の直接的な原因となりました。 -
runtime.memlimit()
: Goランタイム内部の関数で、OSのrlimit
設定に基づいて、Goプログラムが利用できるメモリの最大量を計算しようとします。この関数が返す値は、Goのヒープサイズの上限として利用されることを意図していました。
技術的詳細
Go 1.1より前のバージョンでは、Goランタイムはruntime.memlimit()
を呼び出してOSのrlimit
設定を考慮し、ヒープの最大サイズを決定していました。この制限は、プログラムがOSによって強制されるメモリ制限を超過しないようにするための安全策として機能していました。
しかし、Go 1.1でmheap
のメモリ割り当てロジックが変更され、SysAlloc
を介して直接OSから仮想メモリを要求するようになりました。この変更により、mheap
は起動時に比較的大きな仮想メモリ領域(例えば256MB)を予約するようになりました。
問題は、このmheap
による仮想メモリの予約が、runtime.memlimit()
が返すrlimit
に基づく制限と衝突した点にあります。特に、rlimit
が厳しく設定されている環境(例えば、ulimit -v
で仮想メモリが制限されている場合)では、mheap
が予約しようとするメモリ量がrlimit
を超過し、プログラムの起動に失敗したり、予期せぬクラッシュを引き起こしたりするようになりました。
このコミットでは、この問題を回避するために、src/pkg/runtime/malloc.goc
内のruntime·mallocinit
関数において、runtime·memlimit()
の呼び出し結果を無視し、代わりにlimit
変数を0
に設定しています。
// limit = runtime·memlimit();
// See https://code.google.com/p/go/issues/detail?id=5049
// TODO(rsc): Fix after 1.1.
limit = 0;
limit = 0
と設定することで、Goランタイムはヒープの最大サイズをrlimit
に基づいて制限するのをやめ、実質的にOSが許す限りメモリを割り当てられるようになります。これは、rlimit
による制限がGoのメモリ管理と正しく連携しないという当時の状況に対する、実用的な回避策でした。
ただし、この変更は「TODO(rsc): Fix after 1.1.」と明記されている通り、暫定的なものです。これは、rlimit
を完全に無視することが常に望ましいわけではなく、将来的にrlimit
を適切に尊重しつつ、mheap
のSysAlloc
による割り当てと競合しないような、より洗練された解決策が必要であるという認識があったことを示しています。Go 1.1リリース後のバージョンでは、この問題に対するより恒久的な修正が導入されることになります。
コアとなるコードの変更箇所
変更はsrc/pkg/runtime/malloc.goc
ファイルにあります。
diff --git a/src/pkg/runtime/malloc.goc b/src/pkg/runtime/malloc.goc
index fa28e2b738..a30129ffc1 100644
--- a/src/pkg/runtime/malloc.goc
+++ b/src/pkg/runtime/malloc.goc
@@ -318,7 +318,10 @@ runtime·mallocinit(void)
runtime·InitSizes();
- limit = runtime·memlimit();
+ // limit = runtime·memlimit();
+ // See https://code.google.com/p/go/issues/detail?id=5049
+ // TODO(rsc): Fix after 1.1.
+ limit = 0;
// Set up the allocation arena, a contiguous area of memory where
// allocated data will be found. The arena begins with a bitmap large
コアとなるコードの解説
変更はruntime·mallocinit
関数内で行われています。この関数は、Goランタイムのメモリ割り当てシステムが初期化される際に呼び出されます。
元のコードでは、以下の行でlimit
変数がruntime·memlimit()
の戻り値で初期化されていました。
limit = runtime·memlimit();
このlimit
変数は、Goのヒープが利用できる最大メモリ量を示すために使用されていました。
今回のコミットでは、この行がコメントアウトされ、代わりに以下の3行が追加されています。
// limit = runtime·memlimit();
// See https://code.google.com/p/go/issues/detail?id=5049
// TODO(rsc): Fix after 1.1.
limit = 0;
// limit = runtime·memlimit();
: 元の行をコメントアウトすることで、runtime·memlimit()
の呼び出し結果がlimit
変数に代入されなくなります。// See https://code.google.com/p/go/issues/detail?id=5049
: この変更がGo issue 5049に関連していることを示すコメントです。// TODO(rsc): Fix after 1.1.
: この変更がGo 1.1リリース後の将来の修正を必要とする暫定的なものであることを示すコメントです。rsc
はおそらくGoの主要開発者であるRuss Cox氏を指します。limit = 0;
:limit
変数を明示的に0
に設定します。これにより、ヒープの最大サイズに関するrlimit
に基づく制限が実質的に無効化され、GoランタイムはOSが許す限りメモリを割り当てられるようになります。
この変更により、Go 1.1におけるmheap
のSysAlloc
によるメモリ予約とrlimit
の間の競合が回避され、プログラムがクラッシュすることなく起動できるようになりました。
関連リンク
- Go issue 5049: https://code.google.com/p/go/issues/detail?id=5049
- Go CL 7741050: https://golang.org/cl/7741050