[インデックス 15766] ファイルの概要
このコミットは、Go言語のランタイムにおけるメモリ管理に関連するファイル src/pkg/runtime/malloc.goc
に変更を加えています。malloc.goc
は、Goランタイムのメモリ割り当て(アロケーション)の低レベルな実装を担う重要なファイルです。具体的には、ガベージコレクタ(GC)と連携してヒープメモリの管理を行い、オブジェクトの割り当てや解放、メモリプロファイリングに関する処理が含まれています。
コミット
このコミットは、runtime·settype_flush
関数におけるメモリプロファイリングの挙動を修正することを目的としています。具体的には、runtime·settype_flush
内で行われる runtime·mallocgc
呼び出しにおいて、メモリプロファイリングの対象から除外するフラグ FlagNoProfiling
を追加しています。これにより、特定の内部的なメモリ割り当てがプロファイリングのオーバーヘッドを引き起こすことを防ぎ、パフォーマンスの改善を図っています。
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/76959e2cc6841fddcca21987c6fc0d47181fd789
元コミット内容
runtime: do not memprofile settype_flush
Fixes #4850.
R=golang-dev, bradfitz, rsc
CC=golang-dev
https://golang.org/cl/7745044
変更の背景
Go言語のランタイムには、アプリケーションのメモリ使用状況を詳細に分析するためのメモリプロファイリング機能が備わっています。しかし、プロファイリングは少なからずオーバーヘッドを伴います。runtime·settype_flush
関数は、Goランタイムの内部で型情報などをフラッシュする際にメモリを割り当てる処理を行っています。この内部的な割り当てがメモリプロファイリングの対象となると、プロファイリングの精度に影響を与えたり、不必要なオーバーヘッドを発生させたりする可能性があります。
このコミットは、runtime·settype_flush
内のメモリ割り当てが、ユーザーが関心を持つアプリケーションレベルのメモリ使用とは異なる性質を持つため、プロファイリングの対象から除外することで、プロファイリングの効率性と正確性を向上させることを目的としています。コミットメッセージにある Fixes #4850
は、この変更が特定のバグや問題(Go issue 4850)を解決することを示唆しています。
前提知識の解説
Go言語のメモリ管理とガベージコレクション (GC)
Go言語は、自動メモリ管理(ガベージコレクション)を採用しています。開発者は手動でメモリを解放する必要がなく、ランタイムが不要になったメモリを自動的に回収します。GoのGCは、並行マーク&スイープ方式をベースにしており、アプリケーションの実行と並行して動作することで、STW(Stop-The-World)時間を最小限に抑えるように設計されています。
runtime·mallocgc
runtime·mallocgc
は、Goランタイムの内部でメモリを割り当てるための低レベルな関数です。Goプログラムで make
や new
を使ってメモリを割り当てる場合、最終的にはこの runtime·mallocgc
が呼び出されます。この関数は、割り当てるメモリのサイズ、ポインタを含むかどうかを示すフラグ、アラインメント、およびプロファイリングの対象とするかどうかを示すフラグなど、様々な引数を取ります。
メモリプロファイリング
Go言語には、pprof
ツールを通じて利用できるメモリプロファイリング機能があります。これにより、アプリケーションがどの場所でどれくらいのメモリを割り当てているかを詳細に分析できます。メモリリークの特定やメモリ使用量の最適化に非常に役立ちます。プロファイリングは、特定の期間におけるメモリ割り当てのスナップショットを記録することで行われます。
FlagNoPointers
と FlagNoProfiling
runtime·mallocgc
に渡されるフラグは、割り当てられるメモリの特性やランタイムの挙動を制御します。
FlagNoPointers
: このフラグは、割り当てられるメモリブロックがポインタを含まないことを示します。ポインタを含まないメモリは、GCがスキャンする必要がないため、GCの効率を向上させることができます。例えば、純粋なバイト配列や数値の配列などがこれに該当します。FlagNoProfiling
: このフラグは、割り当てられるメモリブロックをメモリプロファイリングの対象から除外することを示します。ランタイムの内部的な処理で発生する一時的なメモリ割り当てなど、ユーザーが関心を持つアプリケーションレベルのプロファイリングには不要な割り当てに対してこのフラグを使用することで、プロファイリングのオーバーヘッドを削減し、プロファイリングデータのノイズを減らすことができます。
runtime·SysAlloc
runtime·SysAlloc
は、Goランタイムがオペレーティングシステムから直接メモリを要求するための低レベルな関数です。これは、Goのヒープ管理とは独立して、より大きなメモリブロックを確保する場合などに使用されます。runtime·mallocgc
とは異なり、runtime·SysAlloc
で割り当てられたメモリはGoのGCの管理下にはありません。
技術的詳細
このコミットは、src/pkg/runtime/malloc.goc
ファイル内の runtime·settype_flush
関数に焦点を当てています。この関数は、Goランタイムが型情報を管理するために使用する内部的なデータ構造をフラッシュする際に呼び出されます。この処理の中で、runtime·mallocgc
を使用して新しいメモリを割り当てています。
変更前は、runtime·mallocgc
の呼び出しで FlagNoPointers
フラグのみが使用されていました。
data3 = runtime·mallocgc(nbytes3, FlagNoPointers, 0, 1);
変更後は、FlagNoProfiling
フラグが追加されています。
data3 = runtime·mallocgc(nbytes3, FlagNoProfiling|FlagNoPointers, 0, 1);
これは、runtime·settype_flush
内で行われるメモリ割り当てが、メモリプロファイリングの対象から除外されることを意味します。FlagNoProfiling
はビットマスクとして FlagNoPointers
とOR演算子 (|
) で結合されており、両方のフラグが有効になります。
同様の変更が、runtime·settype_flush
内の別の runtime·mallocgc
呼び出しにも適用されています。
// 変更前
data2 = runtime·mallocgc(nbytes2, FlagNoPointers, 0, 1);
// 変更後
data2 = runtime·mallocgc(nbytes2, FlagNoProfiling|FlagNoPointers, 0, 1);
この変更により、runtime·settype_flush
が内部的に割り当てるメモリが、Goのメモリプロファイラによって記録されなくなります。これにより、プロファイリングのオーバーヘッドが削減され、プロファイリングデータがよりアプリケーションの挙動に特化したものになります。
sysalloc
が true
の場合は runtime·SysAlloc
が使用されており、これは元々プロファイリングの対象外であるため、変更は !sysalloc
の場合にのみ適用されています。
コアとなるコードの変更箇所
--- a/src/pkg/runtime/malloc.goc
+++ b/src/pkg/runtime/malloc.goc
@@ -516,7 +516,7 @@ runtime·settype_flush(M *mp, bool sysalloc)
nbytes3 = 8*sizeof(uintptr) + 1*ntypes;
if(!sysalloc) {
- data3 = runtime·mallocgc(nbytes3, FlagNoPointers, 0, 1);
+ data3 = runtime·mallocgc(nbytes3, FlagNoProfiling|FlagNoPointers, 0, 1);
} else {
data3 = runtime·SysAlloc(nbytes3);
if(data3 == nil)
@@ -554,7 +554,7 @@ runtime·settype_flush(M *mp, bool sysalloc)
nbytes2 = ntypes * sizeof(uintptr);
if(!sysalloc) {
- data2 = runtime·mallocgc(nbytes2, FlagNoPointers, 0, 1);
+ data2 = runtime·mallocgc(nbytes2, FlagNoProfiling|FlagNoPointers, 0, 1);
} else {
data2 = runtime·SysAlloc(nbytes2);
if(data2 == nil)
コアとなるコードの解説
上記のdiffは、src/pkg/runtime/malloc.goc
ファイル内の runtime·settype_flush
関数における2つの変更を示しています。
-
最初の変更 (
data3
の割り当て):- data3 = runtime·mallocgc(nbytes3, FlagNoPointers, 0, 1);
- これは変更前のコードです。
runtime·mallocgc
を呼び出してnbytes3
バイトのメモリを割り当てています。この割り当てはポインタを含まない (FlagNoPointers
) とマークされています。最後の1
は、この割り当てがプロファイリングの対象となることを示唆しています(ただし、この引数はGoのバージョンによって意味合いが変わる可能性がありますが、ここではプロファイリングに関連する変更点に注目します)。
- これは変更前のコードです。
+ data3 = runtime·mallocgc(nbytes3, FlagNoProfiling|FlagNoPointers, 0, 1);
- これが変更後のコードです。
FlagNoProfiling
フラグがFlagNoPointers
とビットOR (|
) で結合されています。これにより、割り当てられるメモリはポインタを含まず、かつメモリプロファイリングの対象から除外されるようになります。
- これが変更後のコードです。
-
二番目の変更 (
data2
の割り当て):- data2 = runtime·mallocgc(nbytes2, FlagNoPointers, 0, 1);
- これも同様に、変更前のコードで
nbytes2
バイトのメモリをポインタなしで割り当てています。
- これも同様に、変更前のコードで
+ data2 = runtime·mallocgc(nbytes2, FlagNoProfiling|FlagNoPointers, 0, 1);
- 変更後のコードでは、ここでも
FlagNoProfiling
フラグが追加され、この割り当てもプロファイリングの対象外となります。
- 変更後のコードでは、ここでも
これらの変更は、runtime·settype_flush
が内部的に行うメモリ割り当てが、Goのメモリプロファイラによって記録されないようにすることで、プロファイリングのオーバーヘッドを削減し、より関連性の高いプロファイリングデータを提供することを目的としています。
関連リンク
- Go issue #4850 (このコミットが修正したとされる問題): 残念ながら、Goの公式リポジトリで直接この番号のIssueを見つけることはできませんでした。しかし、コミットメッセージに記載されていることから、過去に存在した、または内部的なIssueトラッカーで管理されていた問題である可能性があります。
- Go言語のメモリプロファイリングに関するドキュメント: https://go.dev/doc/diagnostics#profiling
- Go言語のソースコード (runtimeパッケージ): https://github.com/comemo/go/tree/master/src/runtime
参考にした情報源リンク
- Go言語のソースコード (
src/pkg/runtime/malloc.goc
) - Go言語のドキュメント (特にメモリプロファイリングに関するセクション)
- Go言語のガベージコレクションに関する一般的な知識
runtime·mallocgc
および関連するフラグに関するGoランタイムの内部実装に関する情報 (Goのソースコードを直接参照)- GoのIssueトラッカーの一般的な構造に関する知識