[インデックス 11270] ファイルの概要
このコミットは、GoランタイムがDarwin(macOS)上でメモリを解放する際の挙動を改善するために、madvise
システムコールとMADV_FREE
フラグのサポートを追加するものです。具体的には、Goのガベージコレクタが不要と判断したメモリ領域をOSに通知し、OSがそのメモリを再利用できるようにするメカニズムを導入しています。
コミット
- Author: Dave Cheney dave@cheney.net
- Date: Thu Jan 19 15:51:29 2012 -0500
- Commit Message:
runtime: madvise and SysUnused for Darwin SysUnused is a direct call to madvise MADV_FREE. R=sebastien.paolacci, rsc, minux.ma CC=golang-dev https://golang.org/cl/5531073
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/c354f93b93294406f92fecdc9913b7c050472894
元コミット内容
runtime: madvise and SysUnused for Darwin
SysUnused is a direct call to madvise MADV_FREE.
R=sebastien.paolacci, rsc, minux.ma
CC=golang-dev
https://golang.org/cl/5531073
変更の背景
Go言語のランタイムは、ガベージコレクタ(GC)によって自動的にメモリ管理を行います。GCが不要になったメモリ領域を特定した後、そのメモリをOSに返却したり、再利用可能な状態にしたりするメカニズムが必要です。このプロセスは、特にメモリ使用量が変動するアプリケーションにおいて、システムのメモリ効率を向上させる上で重要です。
以前のGoランタイムのDarwin(macOS)実装では、runtime·SysUnused
関数(GoランタイムがOSにメモリが未使用であることを通知するために使用する関数)が、実際には何もしないスタブ(USED(v); USED(n);
)となっていました。これは、コメントに// TODO(rsc): call madvise MADV_DONTNEED
とあるように、将来的にmadvise
システムコールを呼び出す計画があったことを示唆しています。
このコミットの背景には、GoランタイムがDarwin上でより効率的にメモリをOSに返却し、システム全体のメモリフットプリントを削減したいという意図があります。特に、LinuxのMADV_DONTNEED
に相当する機能がDarwinにはMADV_FREE
として存在するため、これを利用することで、GoのGCが解放したメモリがOSによって適切に扱われるようになります。これにより、Goアプリケーションが使用する物理メモリ量が最適化され、他のプロセスやシステム全体のパフォーマンスに良い影響を与えることが期待されます。
前提知識の解説
1. madvise
システムコール
madvise
は、Unix系OS(Linux, macOS, BSDなど)で利用可能なシステムコールの一つで、プロセスが仮想メモリの特定の範囲について、カーネルに「アドバイス」(助言)を与えるために使用されます。このアドバイスは、カーネルがそのメモリ領域をどのように扱うべきかについてのヒントを提供し、メモリ管理の効率を向上させることを目的としています。madvise
は、メモリの解放を強制するものではなく、カーネルがそのアドバイスを考慮して最適な判断を下すためのものです。
madvise
システムコールは通常、以下のような形式で呼び出されます。
int madvise(void *addr, size_t length, int advice);
addr
: アドバイスを与えるメモリ領域の開始アドレス。length
: アドバイスを与えるメモリ領域の長さ(バイト単位)。advice
: カーネルへの具体的なアドバイスを示すフラグ。
2. MADV_DONTNEED
とMADV_FREE
advice
フラグにはいくつかの種類がありますが、このコミットで特に重要なのはMADV_DONTNEED
とMADV_FREE
です。
-
MADV_DONTNEED
(Linux): このフラグは、指定されたメモリ領域の内容がもはや必要ないことをカーネルに伝えます。カーネルは、このメモリ領域に対応する物理ページを解放し、その内容を破棄することができます。ただし、このメモリ領域はプロセスの仮想アドレス空間からは削除されず、後でアクセスがあった場合にはゼロフィルされたページが割り当てられるか、必要に応じてページインされます。これは、メモリを「解放」するが、仮想アドレス空間は保持したい場合に有用です。 -
MADV_FREE
(BSD/macOS):MADV_FREE
は、BSD系OS(macOSを含む)で提供されるmadvise
のフラグです。これはLinuxのMADV_DONTNEED
と似ていますが、微妙な違いがあります。MADV_FREE
は、指定されたメモリ領域がもはや必要ないことをカーネルに伝え、カーネルはその物理ページを解放することができます。しかし、MADV_FREE
の場合、そのメモリ領域に書き込みが行われるまで、そのページは解放されずに保持される可能性があります。書き込みが行われた時点で、そのページは解放され、新しいゼロフィルされたページが割り当てられます。これは、メモリを解放しつつも、もしすぐに再利用される可能性がある場合に、物理ページの再割り当てのオーバーヘッドを避けるための最適化として機能します。
このコミットのコメント// Linux's MADV_DONTNEED is like BSD's MADV_FREE.
は、GoランタイムがLinuxでMADV_DONTNEED
を使用しているのと同様の目的で、DarwinではMADV_FREE
を使用することを示しています。
3. Goランタイムのメモリ管理
Goランタイムは、独自のメモリ管理システム(ガベージコレクタとヒープアロケータ)を持っています。Goプログラムがメモリを要求すると、ランタイムはOSから大きなメモリブロック(アリーナ)を確保し、それをより小さなチャンクに分割してアプリケーションに割り当てます。ガベージコレクタが不要なオブジェクトを回収した後、そのメモリはGoランタイムのヒープ内で再利用可能になりますが、すぐにOSに返却されるわけではありません。SysUnused
のような関数は、GoランタイムがOSに対して、特定のメモリ領域がもはや使用されていないことを通知し、OSがその物理メモリを他の用途に再利用できるようにするためのインターフェースを提供します。
技術的詳細
このコミットの技術的詳細を掘り下げると、GoランタイムがDarwin上でどのようにメモリ管理の効率を向上させているかが明らかになります。
-
madvise
システムコールの導入: Goランタイムは、OSの機能を利用するためにシステムコールを直接呼び出す必要があります。このコミットでは、Darwin上でmadvise
システムコールを呼び出すためのアセンブリコードが追加されています。src/pkg/runtime/sys_darwin_386.s
およびsrc/pkg/runtime/sys_darwin_amd64.s
に、runtime·madvise
というGoランタイム内部関数が追加されました。- このアセンブリコードは、
madvise
システムコールの番号(Darwinでは75)をレジスタに設定し、必要な引数(メモリのアドレス、長さ、アドバイスフラグ)を適切なレジスタに配置した後、INT $0x80
(32-bit)またはSYSCALL
(64-bit)命令を実行してカーネルにシステムコールを要求します。 - システムコールが成功したかどうかのチェック(
JAE 2(PC)
またはJCC 2(PC)
)と、エラー時のruntime·notok
呼び出しも含まれています。
-
MADV_DONTNEED
とMADV_FREE
定数の追加:madvise
システムコールで使用するadvice
フラグの定数が、GoランタイムのDarwin固有の定義ファイルに追加されました。src/pkg/runtime/defs_darwin.go
src/pkg/runtime/defs_darwin_386.h
src/pkg/runtime/defs_darwin_amd64.h
これらのファイルにMADV_DONTNEED
とMADV_FREE
の定数値が追加され、Goコードからこれらのフラグをシンボリックに参照できるようになりました。
-
runtime·SysUnused
の実装変更: Goランタイムのruntime·SysUnused
関数は、Goのガベージコレクタがメモリ領域が不要になったことをOSに通知するために使用されます。このコミットにより、Darwin版のruntime·SysUnused
が実際に機能するようになりました。src/pkg/runtime/mem_darwin.c
内のruntime·SysUnused
関数が変更され、以前の何もしないスタブコード(USED(v); USED(n);
)が削除されました。- 代わりに、
runtime·madvise(v, n, MADV_FREE);
という呼び出しが追加されました。これにより、SysUnused
が呼び出されると、指定されたメモリ領域v
からn
バイトの範囲について、MADV_FREE
フラグを指定してmadvise
システムコールが実行されるようになりました。 - コメント
// Linux's MADV_DONTNEED is like BSD's MADV_FREE.
は、この変更の意図、すなわちLinuxでのMADV_DONTNEED
と同様のメモリ解放セマンティクスをDarwinでMADV_FREE
を使って実現することを示しています。
この変更により、GoランタイムはDarwin上で、ガベージコレクタが回収したメモリをOSに「ヒント」として通知し、OSがその物理メモリをより効率的に管理できるようになります。これにより、Goアプリケーションのメモリフットプリントが削減され、システム全体のメモリ使用効率が向上する可能性があります。
コアとなるコードの変更箇所
このコミットで変更された主要なファイルと、その変更の概要は以下の通りです。
-
src/pkg/runtime/defs_darwin.go
:MADV_DONTNEED
とMADV_FREE
の定数が追加されました。これらはC言語の定数をGoの定数として定義し直したものです。const ( // ... MADV_DONTNEED = C.MADV_DONTNEED MADV_FREE = C.MADV_FREE // ... )
-
src/pkg/runtime/defs_darwin_386.h
:- 32-bit Darwin(Intel x86)向けのヘッダファイルで、
MADV_DONTNEED
とMADV_FREE
の定数値が追加されました。enum { // ... MADV_DONTNEED = 0x4, MADV_FREE = 0x5, // ... };
- 32-bit Darwin(Intel x86)向けのヘッダファイルで、
-
src/pkg/runtime/defs_darwin_amd64.h
:- 64-bit Darwin(Intel x86-64)向けのヘッダファイルで、
MADV_DONTNEED
とMADV_FREE
の定数値が追加されました。enum { // ... MADV_DONTNEED = 0x4, MADV_FREE = 0x5, // ... };
- 64-bit Darwin(Intel x86-64)向けのヘッダファイルで、
-
src/pkg/runtime/mem_darwin.c
:runtime·SysUnused
関数の実装が変更されました。以前の何もしないコードが削除され、runtime·madvise
を呼び出すようになりました。void runtime·SysUnused(void *v, uintptr n) { // USED(v); // 削除 // USED(n); // 削除 // // TODO(rsc): call madvise MADV_DONTNEED // 削除 // Linux's MADV_DONTNEED is like BSD's MADV_FREE. // 追加 runtime·madvise(v, n, MADV_FREE); // 追加 }
-
src/pkg/runtime/sys_darwin_386.s
:- 32-bit Darwin向けのアセンブリファイルで、
runtime·madvise
関数の実装が追加されました。これはmadvise
システムコール(syscall番号75)を呼び出すためのものです。TEXT runtime·madvise(SB),7,$0 MOVL $75, AX // syscall number for madvise INT $0x80 JAE 2(PC) CALL runtime·notok(SB) RET
- 32-bit Darwin向けのアセンブリファイルで、
-
src/pkg/runtime/sys_darwin_amd64.s
:- 64-bit Darwin向けのアセンブリファイルで、
runtime·madvise
関数の実装が追加されました。これもmadvise
システムコール(syscall番号75)を呼び出すためのものです。引数の渡し方が32-bitと異なります(レジスタを使用)。TEXT runtime·madvise(SB), 7, $0 MOVQ 8(SP), DI // arg 1 addr MOVQ 16(SP), SI // arg 2 len MOVL 24(SP), DX // arg 3 advice MOVL $(0x2000000+75), AX // syscall entry madvise (0x2000000 is for 64-bit syscalls on Darwin) SYSCALL JCC 2(PC) CALL runtime·notok(SB) RET
- 64-bit Darwin向けのアセンブリファイルで、
コアとなるコードの解説
このコミットの核心は、GoランタイムがDarwin上でメモリをOSに返却する際の振る舞いを、madvise
システムコールとMADV_FREE
フラグを用いて具体的に実装した点にあります。
-
defs_darwin.go
およびdefs_darwin_*.h
における定数定義: これらのファイルは、GoランタイムがOS固有の定数や構造体を利用するためのインターフェースを提供します。MADV_DONTNEED
とMADV_FREE
の追加は、Goコードがこれらのメモリ管理アドバイスフラグをシンボリックに参照できるようにするために不可欠です。これにより、マジックナンバー(直接的な数値)を使うことなく、コードの可読性と保守性が向上します。 -
mem_darwin.c
におけるruntime·SysUnused
の変更:runtime·SysUnused
は、Goのガベージコレクタが、特定のメモリ領域がもはやGoランタイムによって使用されていないことをOSに通知するために呼び出す関数です。この関数が以前は空のスタブであったため、Goランタイムがメモリを解放しても、OSはそのメモリが未使用であることを認識せず、物理メモリが解放されないままでした。 変更後、runtime·SysUnused
はruntime·madvise(v, n, MADV_FREE);
を呼び出すようになりました。これは、アドレスv
から長さn
のメモリ領域について、MADV_FREE
というアドバイスをOSに送ることを意味します。MADV_FREE
は、Darwinにおいて、そのメモリ領域の物理ページを解放しても良いが、もしすぐに再利用される可能性がある場合は、書き込みが行われるまでページを保持するというセマンティクスを持ちます。これにより、GoランタイムはOSに対してメモリの再利用を促しつつ、過度なページングや再割り当てのオーバーヘッドを避けることができます。コメントにあるように、これはLinuxのMADV_DONTNEED
と同様の目的を果たします。 -
sys_darwin_386.s
およびsys_darwin_amd64.s
におけるruntime·madvise
の実装: これらのアセンブリファイルは、Goランタイムが直接OSのシステムコールを呼び出すための低レベルなインターフェースを提供します。- 32-bit (
sys_darwin_386.s
):MOVL $75, AX
は、システムコール番号75(madvise
)をAX
レジスタにロードします。INT $0x80
は、ソフトウェア割り込みを発生させ、カーネルにシステムコールを実行させます。引数はスタックから読み取られます。 - 64-bit (
sys_darwin_amd64.s
):MOVQ 8(SP), DI
,MOVQ 16(SP), SI
,MOVL 24(SP), DX
は、スタックから引数(アドレス、長さ、アドバイス)をそれぞれDI
,SI
,DX
レジスタにロードします。これらはx86-64アーキテクチャにおけるシステムコール規約で、最初の3つの引数がこれらのレジスタに渡されることを意味します。MOVL $(0x2000000+75), AX
は、64-bitシステムコール番号をAX
レジスタにロードします。Darwinの64-bitシステムコールは、通常のシステムコール番号に0x2000000
を加算した値を使用します。SYSCALL
命令は、直接カーネルにシステムコールを要求します。 どちらのバージョンも、システムコールが成功したかどうかをチェックし(JAE
またはJCC
)、失敗した場合はruntime·notok
を呼び出してランタイムエラーを報告します。
- 32-bit (
これらの変更により、GoランタイムはDarwin上で、ガベージコレクタが不要と判断したメモリ領域をOSに効率的に通知し、OSがその物理メモリを再利用できるようにする、より洗練されたメモリ管理戦略を採用できるようになりました。これは、Goアプリケーションのメモリフットプリントを削減し、システム全体のパフォーマンスを向上させる上で重要な改善です。
関連リンク
- Go CL (Change List) 5531073: https://golang.org/cl/5531073
参考にした情報源リンク
madvise(2)
man page (Linux): https://man7.org/linux/man-pages/man2/madvise.2.htmlmadvise(2)
man page (macOS/FreeBSD): https://www.freebsd.org/cgi/man.cgi?query=madvise&sektion=2- Go runtime source code (relevant files from the commit)
- Understanding Go's Memory Management: https://go.dev/doc/gc-guide (General Go GC information)
- x86-64 calling conventions (for syscalls): https://en.wikipedia.org/wiki/X86_calling_conventions#System_V_AMD64_ABI
- Darwin System Call Numbers: (Specific link not found easily, but general knowledge of syscalls on Darwin)
- Go Assembly Language: https://go.dev/doc/asm (General Go assembly information)
- Go runtime memory management (general concepts): https://go.dev/src/runtime/README.md (Though this is a general README, it provides context for the runtime)
- Linux
MADV_DONTNEED
vs BSDMADV_FREE
: https://stackoverflow.com/questions/10670099/madv-dontneed-vs-madv-free (Stack Overflow discussion on the differences)