Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 13428] ファイルの概要

このコミットでは、Goランタイムのメモリ管理に関連する複数のファイルが変更されています。主な変更点は、freemcache() という新しい関数の追加と、既存の purgecachedstats() 関数の引数変更です。

  • src/pkg/runtime/malloc.goc: メモリ割り当てのコアロジックが含まれるファイルで、purgecachedstats の呼び出し元が更新され、freemcache 関数が追加されています。
  • src/pkg/runtime/malloc.h: メモリ割り当て関連の関数プロトタイプが定義されているヘッダーファイルで、freemcache の宣言と purgecachedstats の引数変更が反映されています。
  • src/pkg/runtime/mgc0.c: ガベージコレクション (GC) の統計処理に関連するファイルで、purgecachedstats の呼び出し元が更新されています。
  • src/pkg/runtime/mheap.c: グローバルヒープ (MHeap) の管理に関連するファイルで、purgecachedstats の呼び出し元が更新されています。
  • src/pkg/runtime/runtime.h: ランタイム全体の共通ヘッダーファイルで、freemcache の関数プロトタイプが追加されています。

コミット

runtime: add freemcache() function
It will be required for scheduler that maintains
GOMAXPROCS MCache's.

R=golang-dev, r
CC=golang-dev
https://golang.org/cl/6350062

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/ed516df4e43c5e3467bd6a39ffc9277157574788

元コミット内容

commit ed516df4e43c5e3467bd6a39ffc9277157574788
Author: Dmitriy Vyukov <dvyukov@google.com>
Date:   Sun Jul 1 13:10:01 2012 +0400

    runtime: add freemcache() function
    It will be required for scheduler that maintains
    GOMAXPROCS MCache's.
    
    R=golang-dev, r
    CC=golang-dev
    https://golang.org/cl/6350062
---
 src/pkg/runtime/malloc.goc | 18 +++++++++++++-----\n src/pkg/runtime/malloc.h   |  2 +-\n src/pkg/runtime/mgc0.c     |  4 ++--\n src/pkg/runtime/mheap.c    |  4 ++--\n src/pkg/runtime/runtime.h  |  1 +\n 5 files changed, 19 insertions(+), 10 deletions(-)\n

変更の背景

このコミットの主な目的は、Goランタイムに freemcache() 関数を追加することです。コミットメッセージには「スケジューラが GOMAXPROCSMCache を維持するために必要となる」と明記されています。

Goのランタイムスケジューラは、Goプログラムの並行実行を管理します。GOMAXPROCS は、同時に実行できるOSスレッドの最大数を制御する環境変数であり、Goランタイムが利用できる論理プロセッサ (P) の数に影響を与えます。各論理プロセッサ (P) には、スレッドローカルなメモリキャッシュである MCache が関連付けられています。

Goランタイムの進化の過程で、スケジューラは GOMAXPROCS の値の変更や、プロセッサ (P) の動的な割り当て・解放に応じて、関連する MCache のライフサイクルを適切に管理する必要が生じました。具体的には、不要になった MCache を安全かつ効率的に解放するメカニズムが求められていました。

freemcache() 関数は、この MCache の解放処理をカプセル化するために導入されました。これにより、スケジューラは必要に応じて MCache をクリーンアップし、メモリリソースを適切に管理できるようになります。また、purgecachedstats() 関数の引数が M* (OSスレッドを表す構造体) から MCache* へと変更されたのは、この統計パージ処理が MCache 自体に直接関連するものであり、より汎用的に利用できるようにするためのリファクタリングと考えられます。

前提知識の解説

このコミットを理解するためには、Goランタイムの以下の主要な概念を把握しておく必要があります。

1. Goランタイムの概要

Goランタイムは、Goプログラムの実行を管理するシステムです。これには、ガベージコレクション (GC)、スケジューラ、メモリ管理、プリミティブな同期メカニズムなどが含まれます。Goプログラムは、OSによって直接実行されるのではなく、Goランタイム上で動作します。

2. Goスケジューラ (M, P, Gモデル)

Goスケジューラは、Goの並行処理モデルの中核をなすものです。主に以下の3つの要素で構成されます。

  • G (Goroutine): Goにおける軽量な実行単位です。OSスレッドよりもはるかに軽量で、数百万個作成することも可能です。
  • M (Machine): OSスレッドを表します。Goランタイムは、Goコードを実行するためにOSスレッドをMとして利用します。Mは、Gを実行したり、システムコールを処理したりします。
  • P (Processor): 論理プロセッサを表します。PはMとGの間の仲介役であり、Gを実行するためのコンテキストを提供します。各Pは、実行可能なGのローカルキューを持ち、MはPにアタッチされてGを実行します。GOMAXPROCS 環境変数は、同時に実行できるPの最大数を決定します。

このモデルにより、Goランタイムは少数のOSスレッド (M) で多数のGoroutine (G) を効率的に多重化し、並行処理を実現します。

3. Goのメモリ管理 (MCache, MHeap, FixAlloc)

Goランタイムは、独自のメモリ管理システムを持っています。

  • MHeap (グローバルヒープ): Goプログラムが割り当てるすべてのオブジェクトのメモリが最終的に格納される場所です。MHeapは、大きなメモリチャンク (スパン) を管理し、必要に応じてMCacheに提供します。
  • MCache (メモリキャッシュ): 各P (または古いバージョンではM) に関連付けられたスレッドローカルなメモリキャッシュです。Goプログラムが小さなオブジェクトを割り当てる際、まずMCacheからメモリを確保しようとします。これにより、グローバルなMHeapへのアクセスを減らし、ロックの競合を最小限に抑え、割り当てのパフォーマンスを向上させます。MCacheが枯渇すると、MHeapから新しいメモリを取得します。
  • FixAlloc: Goランタイム内部のデータ構造 (例えば、MCache自体やMHeapのスパン記述子など) を割り当てるために使用される、固定サイズのオブジェクトアロケータです。これは、特定のサイズのオブジェクトを効率的に割り当てるために設計されています。

4. purgecachedstats の役割

purgecachedstats 関数は、MCacheに蓄積されたメモリ割り当て統計情報 (例えば、割り当てられたオブジェクトの数やバイト数) を、グローバルなMHeapの統計情報にフラッシュする役割を担います。MCacheはスレッドローカルなため、正確な全体像を得るためには、定期的にその統計情報をグローバルな統計に統合する必要があります。これは、ガベージコレクションの判断やメモリプロファイリングの精度に影響します。

技術的詳細

このコミットにおける技術的な変更点は以下の通りです。

1. freemcache(MCache *c) 関数の導入

  • 目的: 不要になった MCache オブジェクトを適切に解放し、関連するリソースをクリーンアップするために導入されました。これは、スケジューラが GOMAXPROCS の変更やプロセッサの動的な管理に伴い、MCache のライフサイクルを制御する必要があるためです。
  • 処理内容:
    1. runtime·MCache_ReleaseAll(c): MCache 内に残っているすべてのメモリブロックをグローバルヒープに解放します。これにより、MCacheが保持していたメモリが再利用可能になります。
    2. runtime·lock(&runtime·mheap): グローバルヒープのロックを取得します。これは、ヒープ統計の更新や FixAlloc からのメモリ解放が競合しないようにするためです。
    3. runtime·purgecachedstats(c): MCache の統計情報をグローバルな統計にフラッシュします。これにより、解放される MCache の統計が失われることなく、全体のメモリ使用状況に反映されます。
    4. runtime·FixAlloc_Free(&runtime·mheap.cachealloc, c): FixAlloc を使用して割り当てられた MCache オブジェクト自体を解放します。runtime·mheap.cachealloc は、MCacheオブジェクトを管理するための FixAlloc インスタンスです。
    5. runtime·unlock(&runtime·mheap): グローバルヒープのロックを解放します。

2. purgecachedstats 関数の引数変更

  • 変更前: void runtime·purgecachedstats(M* m)
  • 変更後: void runtime·purgecachedstats(MCache* c)
  • 理由と影響: 以前は M (OSスレッド) オブジェクトを引数に取り、その M に関連付けられた MCache を内部で参照していました。この変更により、purgecachedstats は直接 MCache オブジェクトを引数として受け取るようになりました。これにより、関数がより汎用的になり、MCache の統計をパージする操作が M のコンテキストから独立して呼び出せるようになります。これは、関数の責務を明確にし、モジュール性を高める良いリファクタリングです。この変更に伴い、malloc.goc, mgc0.c, mheap.c 内の purgecachedstats の呼び出し箇所がすべて更新されています。

3. allocmcache における memclr の追加

  • runtime·allocmcache() 関数内で、新しく割り当てられた MCache オブジェクト c に対して runtime·memclr((byte*)c, sizeof(*c)); が追加されました。
  • 目的: これは、新しく確保された MCache のメモリ領域をゼロクリアすることを保証します。これにより、以前のメモリ内容が残っていることによる潜在的なバグ (例えば、古いカウンタ値やポインタが残っていること) を防ぎ、MCache が常にクリーンな状態で初期化されることを保証します。特に、カウンタやフラグなどの数値フィールドがゼロから始まることが期待される場合に重要です。

コアとなるコードの変更箇所

src/pkg/runtime/malloc.goc における freemcache の追加

--- a/src/pkg/runtime/malloc.goc
+++ b/src/pkg/runtime/malloc.goc
@@ -246,12 +247,19 @@ runtime·allocmcache(void)
 }
 
 void
-runtime·purgecachedstats(M* m)
+runtime·freemcache(MCache *c)
 {
-\tMCache *c;\n+\truntime·MCache_ReleaseAll(c);\n+\truntime·lock(&runtime·mheap);\n+\truntime·purgecachedstats(c);\n+\truntime·FixAlloc_Free(&runtime·mheap.cachealloc, c);\n+\truntime·unlock(&runtime·mheap);\n+}\n \n+void\n+runtime·purgecachedstats(MCache *c)
+{\n \t// Protected by either heap or GC lock.\n-\tc = m->mcache;\n \tmstats.heap_alloc += c->local_cachealloc;\n \tc->local_cachealloc = 0;\n \tmstats.heap_objects += c->local_objects;\

src/pkg/runtime/malloc.h における purgecachedstats の引数変更と freemcache の宣言

--- a/src/pkg/runtime/malloc.h
+++ b/src/pkg/runtime/malloc.h
@@ -401,7 +401,7 @@ void	runtime·markspan(void *v, uintptr size, uintptr n, bool leftover);\n void	runtime·unmarkspan(void *v, uintptr size);\n bool	runtime·blockspecial(void*);\n void	runtime·setblockspecial(void*, bool);\n-void	runtime·purgecachedstats(M*);\n+void	runtime·purgecachedstats(MCache*);\n 
 enum
 {

src/pkg/runtime/runtime.h における freemcache の宣言

--- a/src/pkg/runtime/runtime.h
+++ b/src/pkg/runtime/runtime.h
@@ -590,6 +590,7 @@ int32	runtime·funcline(Func*, uintptr);\n void*	runtime·stackalloc(uint32);\n void	runtime·stackfree(void*, uintptr);\n MCache*	runtime·allocmcache(void);\n+void	runtime·freemcache(MCache*);\n void	runtime·mallocinit(void);\n bool	runtime·ifaceeq_c(Iface, Iface);\n bool	runtime·efaceeq_c(Eface, Eface);\

コアとなるコードの解説

runtime·freemcache(MCache *c)

この関数は、引数として渡された MCache オブジェクト c を解放する責任を持ちます。

  1. runtime·MCache_ReleaseAll(c);: MCache が保持しているすべてのメモリブロック (スパン) をグローバルヒープに返却します。これにより、これらのメモリは他の割り当てのために再利用可能になります。
  2. runtime·lock(&runtime·mheap);runtime·unlock(&runtime·mheap);: グローバルヒープ (runtime·mheap) へのアクセスを保護するためのロックです。purgecachedstats の呼び出しと FixAlloc_Free によるメモリ解放がアトミックに行われることを保証します。
  3. runtime·purgecachedstats(c);: 解放される MCache のローカル統計情報をグローバルな統計にフラッシュします。これにより、この MCache が使用していた期間の正確な統計がシステム全体に反映されます。
  4. runtime·FixAlloc_Free(&runtime·mheap.cachealloc, c);: MCache オブジェクト自体が FixAlloc を使って割り当てられているため、その FixAlloc インスタンス (runtime·mheap.cachealloc) を通じて MCache オブジェクトのメモリを解放します。

runtime·purgecachedstats の引数変更

runtime·purgecachedstats の関数シグネチャが M* から MCache* に変更されました。

  • 変更前は、M (OSスレッド) オブジェクトから m->mcache を介して MCache を取得していました。
  • 変更後は、直接 MCache オブジェクトを受け取るため、この関数は MCache の統計をパージするという単一の責務に集中できるようになりました。これにより、コードの可読性と再利用性が向上します。

runtime·allocmcache における memclr の追加

runtime·allocmcache 関数内で、新しく割り当てられた MCache オブジェクト c のメモリ領域全体が runtime·memclr((byte*)c, sizeof(*c)); によってゼロクリアされます。これは、確保されたメモリが不定な値を含まないことを保証し、特にカウンタやポインタなどのフィールドが初期状態でゼロであることを期待するGoランタイムの内部ロジックにとって重要です。これにより、潜在的なバグや予期せぬ動作を防ぎます。

関連リンク

参考にした情報源リンク

  • Goの公式ドキュメントやソースコード (Goランタイムの内部構造に関する一般的な知識)
  • Goのメモリ管理とスケジューラに関する一般的な技術記事やブログポスト (MCache, MHeap, M-P-Gモデルの概念理解のため)