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

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

このコミットは、Go言語のランタイムパッケージ runtime/debug 内の SetGCPercent 関数に対する変更です。具体的には、GCPercent の設定後にガベージコレクション (GC) を強制的に実行することで、その設定が即座に反映されるように修正されています。

コミット

commit 0db71338ed23324015d5d0600d2670fb3ed440b4
Author: Shenghou Ma <minux.ma@gmail.com>
Date:   Tue Jan 14 19:23:36 2014 -0500

    runtime/debug: force GC after setting of GCPercent to make it effective.
    See also discussion in CL 51010045.
    
    R=golang-codereviews, r
    CC=golang-codereviews
    https://golang.org/cl/52230043

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

https://github.com/golang/go/commit/0db71338ed23324015d5d0600d2670fb3ed440b4

元コミット内容

runtime/debug: force GC after setting of GCPercent to make it effective. See also discussion in CL 51010045.

このコミットメッセージは、GCPercent の設定後すぐにガベージコレクションを強制実行することで、その設定が有効になるようにする変更であることを示しています。また、関連する議論が CL 51010045 にあることを示唆しています。

変更の背景

Goのガベージコレクタは、ヒープの使用量に基づいて次回のGCがいつ実行されるかを決定します。GCPercent は、前回のGCで到達可能なヒープサイズに対する、次回のGCがトリガーされるヒープサイズの割合を制御する設定です。例えば、GCPercent が100%の場合、前回のGC後に使用されたヒープメモリが100MBであれば、次回のGCはヒープサイズが200MBに達したときにトリガーされます。

このコミットが導入される前は、SetGCPercent 関数を呼び出して GCPercent の値を変更しても、その変更がすぐにガベージコレクタの動作に反映されない可能性がありました。これは、次回のGCトリガーポイントが、SetGCPercent が呼び出される前の GCPercent の値と現在のヒープ状態に基づいて既に計算されていたためです。

コミットメッセージに記載されている CL 51010045 は、この問題に関する議論が行われた変更リスト(Change List)を指しています。この議論では、GCPercent の変更が即座に反映されないことによる潜在的な問題、特にメモリ使用量の制御やGCのチューニングにおいて、ユーザーが期待する動作と実際の動作との間に乖離が生じる点が指摘されていました。

このコミットの目的は、SetGCPercent が呼び出された直後に新しい GCPercent の値がガベージコレクタに確実に適用されるようにすることです。これにより、ユーザーが GCPercent を変更した際に、その変更がすぐにGCの挙動に影響を与え、より予測可能で制御しやすいメモリ管理が可能になります。

前提知識の解説

Go言語のガベージコレクション (GC)

Go言語は、自動メモリ管理のためにトレース型ガベージコレクタを採用しています。これは、プログラムが動的に確保したメモリのうち、もはや到達不可能(参照されていない)なオブジェクトを自動的に解放する仕組みです。GoのGCは、並行(concurrent)かつ低遅延(low-latency)であることを目指して設計されており、プログラムの実行と並行してGC処理の一部を進めることで、アプリケーションの一時停止時間(stop-the-world pause)を最小限に抑えています。

GCトリガーとヒープサイズ

GoのGCは、主にヒープの使用量に基づいてトリガーされます。具体的には、前回のGCが完了した時点での「到達可能なヒープサイズ」(つまり、GCによって解放されずに残ったメモリの量)を基準として、次回のGCがいつ実行されるかを決定します。

GCPercent

GCPercent は、Goのガベージコレクタの動作を調整するための重要なパラメータです。これは、runtime/debug.SetGCPercent 関数を通じて設定できます。

  • 定義: GCPercent は、前回のGCで到達可能なヒープサイズに対する、次回のGCがトリガーされるヒープサイズの「追加の」割合をパーセンテージで指定します。
  • 計算: 次回のGCがトリガーされるヒープサイズは、前回の到達可能ヒープサイズ * (1 + GCPercent / 100) で計算されます。
  • :
    • GCPercent = 100 (デフォルト値): 前回のGCで到達可能なヒープサイズが100MBだった場合、次回のGCはヒープサイズが200MB(100MB + 100MB * 100%)に達したときにトリガーされます。つまり、ヒープサイズが2倍になったらGCが実行されます。
    • GCPercent = 50: 前回のGCで到達可能なヒープサイズが100MBだった場合、次回のGCはヒープサイズが150MB(100MB + 100MB * 50%)に達したときにトリガーされます。GCがより頻繁に実行され、メモリ使用量が抑えられますが、GCのオーバーヘッドが増加する可能性があります。
    • GCPercent = 200: 前回のGCで到達可能なヒープサイズが100MBだった場合、次回のGCはヒープサイズが300MB(100MB + 100MB * 200%)に達したときにトリガーされます。GCの頻度が減り、GCのオーバーヘッドは減少しますが、メモリ使用量が増加する可能性があります。
  • 負の値: GCPercent に負の値を設定すると、ガベージコレクションが無効になります。これは通常、デバッグや特定のパフォーマンスチューニングのシナリオでのみ使用されます。

GCPercent は、アプリケーションのメモリ使用量とGCのレイテンシのバランスを調整するために使用されます。値を小さくするとGCが頻繁に実行され、メモリ使用量は抑えられますが、GCによるCPUオーバーヘッドが増加します。値を大きくするとGCの頻度が減り、CPUオーバーヘッドは減少しますが、メモリ使用量が増加します。

runtime.GC()

runtime.GC() 関数は、Goのガベージコレクタを強制的に実行します。通常、GCはランタイムによって自動的にトリガーされますが、この関数を呼び出すことで、開発者は任意のタイミングでGCを実行させることができます。これは、メモリ使用量を即座に削減したい場合や、特定のテストシナリオでGCの動作を確認したい場合などに有用です。

技術的詳細

このコミットの技術的な核心は、SetGCPercent 関数が呼び出された際に、新しい GCPercent の値がガベージコレクタの次回のトリガーポイントの計算に即座に反映されるようにすることです。

変更前は、SetGCPercent が呼び出されても、ガベージコレクタは直ちに新しい GCPercent の値に基づいて次回のGCトリガーポイントを再計算しませんでした。次回のGCは、依然として古い GCPercent の値と、SetGCPercent が呼び出される前のヒープの状態に基づいてスケジュールされていました。これは、特に GCPercent を大幅に変更した場合に、ユーザーが期待するGCの挙動と実際の挙動との間に時間差が生じる原因となっていました。例えば、GCPercent を非常に小さい値に設定してメモリ使用量を厳しく制御しようとしても、すぐにGCが実行されず、メモリ使用量が意図せず増加し続ける可能性がありました。

この問題を解決するために、コミットは SetGCPercent 関数内で runtime.GC() を呼び出すように変更しました。

  1. setGCPercent(percent) の呼び出し: まず、内部関数 setGCPercent が呼び出され、新しい GCPercent の値がランタイムに設定されます。この関数は、古い GCPercent の値を返します。
  2. runtime.GC() の呼び出し: その後、runtime.GC() が呼び出され、ガベージコレクションが強制的に実行されます。この強制GCは、新しい GCPercent の値が既にランタイムに設定されている状態で実行されます。

この強制GCの実行により、以下の効果が得られます。

  • 即時反映: 新しい GCPercent の値が設定された直後にGCが実行されるため、次回のGCトリガーポイントの計算が、この新しい値と、強制GC後のヒープの状態に基づいて行われるようになります。これにより、GCPercent の変更が即座にガベージコレクタの動作に反映されることが保証されます。
  • 予測可能性の向上: 開発者は SetGCPercent を呼び出した際に、その変更がすぐにGCの挙動に影響を与えることを期待できるようになり、メモリ管理の予測可能性が向上します。
  • メモリ使用量の制御: 特に GCPercent を小さく設定してメモリ使用量を積極的に削減したい場合に、この変更は非常に有効です。設定後すぐにGCが実行されることで、不要なメモリが解放され、メモリ使用量が迅速に目標値に近づきます。

この変更は、Goランタイムのガベージコレクタの挙動をより直感的で制御しやすいものにするための、重要な改善と言えます。

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

変更は src/pkg/runtime/debug/garbage.go ファイルの SetGCPercent 関数にあります。

--- a/src/pkg/runtime/debug/garbage.go
+++ b/src/pkg/runtime/debug/garbage.go
@@ -91,7 +91,9 @@ func (x byDuration) Less(i, j int) bool { return x[i] < x[j] }\n // at startup, or 100 if the variable is not set.\n // A negative percentage disables garbage collection.\n func SetGCPercent(percent int) int {\n-\treturn setGCPercent(percent)\n+\told := setGCPercent(percent)\n+\truntime.GC()\n+\treturn old\n }\n \n // FreeOSMemory forces a garbage collection followed by an\n```

## コアとなるコードの解説

変更された `SetGCPercent` 関数は以下のようになります。

```go
func SetGCPercent(percent int) int {
	old := setGCPercent(percent) // 1. 新しいGCPercent値を設定し、古い値をoldに格納
	runtime.GC()                 // 2. ガベージコレクションを強制実行
	return old                   // 3. 古いGCPercent値を返す
}
  1. old := setGCPercent(percent):

    • この行は、引数 percent で指定された新しい GCPercent の値をGoランタイムに設定します。
    • setGCPercent は内部関数であり、GoランタイムのGCパラメータを実際に更新する役割を担っています。
    • この関数は、変更前の古い GCPercent の値を返します。この古い値は old 変数に格納されます。
  2. runtime.GC():

    • この行がこのコミットの主要な変更点です。
    • runtime.GC() を呼び出すことで、Goのガベージコレクタが直ちに実行されます。
    • この強制GCは、直前の setGCPercent(percent) の呼び出しによって既に更新された新しい GCPercent の値に基づいて動作します。これにより、新しい GCPercent の設定が即座にGCの挙動に反映されることが保証されます。
  3. return old:

    • 関数は、GCPercent が変更される前の古い値を返します。これは、SetGCPercent の呼び出し元が以前のGC設定を知る必要がある場合に有用です。

この変更により、SetGCPercent を呼び出すと、単に設定値を変更するだけでなく、その変更がすぐにGCの動作に影響を与えるようにGCが強制的に実行されるようになりました。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント (runtime/debugパッケージ): https://pkg.go.dev/runtime/debug
  • Go言語のガベージコレクションに関するブログ記事やドキュメント (一般的な情報源、特定のURLは省略)
  • GoのChange Listシステムに関する情報 (一般的な情報源、特定のURLは省略)
  • Goのソースコード (runtime/debug/garbage.go): https://github.com/golang/go/blob/master/src/runtime/debug/garbage.go
  • Goのガベージコレクションの内部動作に関する情報 (一般的な情報源、特定のURLは省略)

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

このコミットは、Go言語のランタイムパッケージ runtime/debug 内の SetGCPercent 関数に対する変更です。具体的には、GCPercent の設定後にガベージコレクション (GC) を強制的に実行することで、その設定が即座に反映されるように修正されています。

コミット

commit 0db71338ed23324015d5d0600d2670fb3ed440b4
Author: Shenghou Ma <minux.ma@gmail.com>
Date:   Tue Jan 14 19:23:36 2014 -0500

    runtime/debug: force GC after setting of GCPercent to make it effective.
    See also discussion in CL 51010045.
    
    R=golang-codereviews, r
    CC=golang-codereviews
    https://golang.org/cl/52230043

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

https://github.com/golang/go/commit/0db71338ed23324015d5d0600d2670fb3ed440b4

元コミット内容

runtime/debug: force GC after setting of GCPercent to make it effective. See also discussion in CL 51010045.

このコミットメッセージは、GCPercent の設定後すぐにガベージコレクションを強制実行することで、その設定が有効になるようにする変更であることを示しています。また、関連する議論が CL 51010045 にあることを示唆しています。

変更の背景

Goのガベージコレクタは、ヒープの使用量に基づいて次回のGCがいつ実行されるかを決定します。GCPercent は、前回のGCで到達可能なヒープサイズに対する、次回のGCがトリガーされるヒープサイズの割合を制御する設定です。例えば、GCPercent が100%の場合、前回のGC後に使用されたヒープメモリが100MBであれば、次回のGCはヒープサイズが200MBに達したときにトリガーされます。

このコミットが導入される前は、SetGCPercent 関数を呼び出して GCPercent の値を変更しても、その変更がすぐにガベージコレクタの動作に反映されない可能性がありました。これは、次回のGCトリガーポイントが、SetGCPercent が呼び出される前の GCPercent の値と現在のヒープ状態に基づいて既に計算されていたためです。

コミットメッセージに記載されている CL 51010045 は、この問題に関する議論が行われた変更リスト(Change List)を指しています。この議論では、GCPercent の変更が即座に反映されないことによる潜在的な問題、特にメモリ使用量の制御やGCのチューニングにおいて、ユーザーが期待する動作と実際の動作との間に乖離が生じる点が指摘されていました。

このコミットの目的は、SetGCPercent が呼び出された直後に新しい GCPercent の値がガベージコレクタに確実に適用されるようにすることです。これにより、ユーザーが GCPercent を変更した際に、その変更がすぐにGCの挙動に影響を与え、より予測可能で制御しやすいメモリ管理が可能になります。

前提知識の解説

Go言語のガベージコレクション (GC)

Go言語は、自動メモリ管理のためにトレース型ガベージコレクタを採用しています。これは、プログラムが動的に確保したメモリのうち、もはや到達不可能(参照されていない)なオブジェクトを自動的に解放する仕組みです。GoのGCは、並行(concurrent)かつ低遅延(low-latency)であることを目指して設計されており、プログラムの実行と並行してGC処理の一部を進めることで、アプリケーションの一時停止時間(stop-the-world pause)を最小限に抑えています。

GCトリガーとヒープサイズ

GoのGCは、主にヒープの使用量に基づいてトリガーされます。具体的には、前回のGCが完了した時点での「到達可能なヒープサイズ」(つまり、GCによって解放されずに残ったメモリの量)を基準として、次回のGCがいつ実行されるかを決定します。

GCPercent

GCPercent は、Goのガベージコレクタの動作を調整するための重要なパラメータです。これは、runtime/debug.SetGCPercent 関数を通じて設定できます。

  • 定義: GCPercent は、前回のGCで到達可能なヒープサイズに対する、次回のGCがトリガーされるヒープサイズの「追加の」割合をパーセンテージで指定します。
  • 計算: 次回のGCがトリガーされるヒープサイズは、前回の到達可能ヒープサイズ * (1 + GCPercent / 100) で計算されます。
  • :
    • GCPercent = 100 (デフォルト値): 前回のGCで到達可能なヒープサイズが100MBだった場合、次回のGCはヒープサイズが200MB(100MB + 100MB * 100%)に達したときにトリガーされます。つまり、ヒープサイズが2倍になったらGCが実行されます。
    • GCPercent = 50: 前回のGCで到達可能なヒープサイズが100MBだった場合、次回のGCはヒープサイズが150MB(100MB + 100MB * 50%)に達したときにトリガーされます。GCがより頻繁に実行され、メモリ使用量が抑えられますが、GCのオーバーヘッドが増加する可能性があります。
    • GCPercent = 200: 前回のGCで到達可能なヒープサイズが100MBだった場合、次回のGCはヒープサイズが300MB(100MB + 100MB * 200%)に達したときにトリガーされます。GCの頻度が減り、GCのオーバーヘッドは減少しますが、メモリ使用量が増加する可能性があります。
  • 負の値: GCPercent に負の値を設定すると、ガベージコレクションが無効になります。これは通常、デバッグや特定のパフォーマンスチューニングのシナリオでのみ使用されます。

GCPercent は、アプリケーションのメモリ使用量とGCのレイテンシのバランスを調整するために使用されます。値を小さくするとGCが頻繁に実行され、メモリ使用量は抑えられますが、GCによるCPUオーバーヘッドが増加します。値を大きくするとGCの頻度が減り、CPUオーバーヘッドは減少しますが、メモリ使用量が増加します。

runtime.GC()

runtime.GC() 関数は、Goのガベージコレクタを強制的に実行します。通常、GCはランタイムによって自動的にトリガーされますが、この関数を呼び出すことで、開発者は任意のタイミングでGCを実行させることができます。これは、メモリ使用量を即座に削減したい場合や、特定のテストシナリオでGCの動作を確認したい場合などに有用です。

技術的詳細

このコミットの技術的な核心は、SetGCPercent 関数が呼び出された際に、新しい GCPercent の値がガベージコレクタの次回のトリガーポイントの計算に即座に反映されるようにすることです。

変更前は、SetGCPercent が呼び出されても、ガベージコレクタは直ちに新しい GCPercent の値に基づいて次回のGCトリガーポイントを再計算しませんでした。次回のGCは、依然として古い GCPercent の値と、SetGCPercent が呼び出される前のヒープの状態に基づいてスケジュールされていました。これは、特に GCPercent を大幅に変更した場合に、ユーザーが期待するGCの挙動と実際の挙動との間に時間差が生じる原因となっていました。例えば、GCPercent を非常に小さい値に設定してメモリ使用量を厳しく制御しようとしても、すぐにGCが実行されず、メモリ使用量が意図せず増加し続ける可能性がありました。

この問題を解決するために、コミットは SetGCPercent 関数内で runtime.GC() を呼び出すように変更しました。

  1. setGCPercent(percent) の呼び出し: まず、内部関数 setGCPercent が呼び出され、新しい GCPercent の値がランタイムに設定されます。この関数は、古い GCPercent の値を返します。
  2. runtime.GC() の呼び出し: その後、runtime.GC() が呼び出され、ガベージコレクションが強制的に実行されます。この強制GCは、新しい GCPercent の値が既にランタイムに設定されている状態で実行されます。

この強制GCの実行により、以下の効果が得られます。

  • 即時反映: 新しい GCPercent の値が設定された直後にGCが実行されるため、次回のGCトリガーポイントの計算が、この新しい値と、強制GC後のヒープの状態に基づいて行われるようになります。これにより、GCPercent の変更が即座にガベージコレクタの動作に反映されることが保証されます。
  • 予測可能性の向上: 開発者は SetGCPercent を呼び出した際に、その変更がすぐにGCの挙動に影響を与えることを期待できるようになり、メモリ管理の予測可能性が向上します。
  • メモリ使用量の制御: 特に GCPercent を小さく設定してメモリ使用量を積極的に削減したい場合に、この変更は非常に有効です。設定後すぐにGCが実行されることで、不要なメモリが解放され、メモリ使用量が迅速に目標値に近づきます。

この変更は、Goランタイムのガベージコレクタの挙動をより直感的で制御しやすいものにするための、重要な改善と言えます。

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

変更は src/pkg/runtime/debug/garbage.go ファイルの SetGCPercent 関数にあります。

--- a/src/pkg/runtime/debug/garbage.go
+++ b/src/pkg/runtime/debug/garbage.go
@@ -91,7 +91,9 @@ func (x byDuration) Less(i, j int) bool { return x[i] < x[j] }\n // at startup, or 100 if the variable is not set.\n // A negative percentage disables garbage collection.\n func SetGCPercent(percent int) int {\n-\treturn setGCPercent(percent)\n+\told := setGCPercent(percent)\n+\truntime.GC()\n+\treturn old\n }\n \n // FreeOSMemory forces a garbage collection followed by an\n```

## コアとなるコードの解説

変更された `SetGCPercent` 関数は以下のようになります。

```go
func SetGCPercent(percent int) int {
	old := setGCPercent(percent) // 1. 新しいGCPercent値を設定し、古い値をoldに格納
	runtime.GC()                 // 2. ガベージコレクションを強制実行
	return old                   // 3. 古いGCPercent値を返す
}
  1. old := setGCPercent(percent):

    • この行は、引数 percent で指定された新しい GCPercent の値をGoランタイムに設定します。
    • setGCPercent は内部関数であり、GoランタイムのGCパラメータを実際に更新する役割を担っています。
    • この関数は、変更前の古い GCPercent の値を返します。この古い値は old 変数に格納されます。
  2. runtime.GC():

    • この行がこのコミットの主要な変更点です。
    • runtime.GC() を呼び出すことで、Goのガベージコレクタが直ちに実行されます。
    • この強制GCは、直前の setGCPercent(percent) の呼び出しによって既に更新された新しい GCPercent の値に基づいて動作します。これにより、新しい GCPercent の設定が即座にGCの挙動に反映されることが保証されます。
  3. return old:

    • 関数は、GCPercent が変更される前の古い値を返します。これは、SetGCPercent の呼び出し元が以前のGC設定を知る必要がある場合に有用です。

この変更により、SetGCPercent を呼び出すと、単に設定値を変更するだけでなく、その変更がすぐにGCの動作に影響を与えるようにGCが強制的に実行されるようになりました。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント (runtime/debugパッケージ): https://pkg.go.dev/runtime/debug
  • Go言語のガベージコレクションに関するブログ記事やドキュメント (一般的な情報源、特定のURLは省略)
  • GoのChange Listシステムに関する情報 (一般的な情報源、特定のURLは省略)
  • Goのソースコード (runtime/debug/garbage.go): https://github.com/golang/go/blob/master/src/runtime/debug/garbage.go
  • Goのガベージコレクションの内部動作に関する情報 (一般的な情報源、特定のURLは省略)