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

[インデックス 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プログラムで makenew を使ってメモリを割り当てる場合、最終的にはこの runtime·mallocgc が呼び出されます。この関数は、割り当てるメモリのサイズ、ポインタを含むかどうかを示すフラグ、アラインメント、およびプロファイリングの対象とするかどうかを示すフラグなど、様々な引数を取ります。

メモリプロファイリング

Go言語には、pprof ツールを通じて利用できるメモリプロファイリング機能があります。これにより、アプリケーションがどの場所でどれくらいのメモリを割り当てているかを詳細に分析できます。メモリリークの特定やメモリ使用量の最適化に非常に役立ちます。プロファイリングは、特定の期間におけるメモリ割り当てのスナップショットを記録することで行われます。

FlagNoPointersFlagNoProfiling

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のメモリプロファイラによって記録されなくなります。これにより、プロファイリングのオーバーヘッドが削減され、プロファイリングデータがよりアプリケーションの挙動に特化したものになります。

sysalloctrue の場合は 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つの変更を示しています。

  1. 最初の変更 (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 (|) で結合されています。これにより、割り当てられるメモリはポインタを含まず、かつメモリプロファイリングの対象から除外されるようになります。
  2. 二番目の変更 (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トラッカーの一般的な構造に関する知識