Go GC の進化の歴史が 1 つの記事で理解できる、とても詳しい内容です。

最近、囲碁就職トレーニングキャンプの友人たちと囲碁 GC の問題について話し合っていたところ、Liu Danbing 先生がまとめた内容を見つけたので、とてもよく書かれていたのでシェアしたいと思います。

私たちの議論や考えはこの記事にもまとめられています。

ガベージ コレクション (GC) は、プログラミング言語で提供される自動メモリ管理メカニズムであり、不要なメモリ オブジェクトを自動的に解放し、メモリ リソースを放棄します。GC プロセス中にプログラマが手動で実行する必要はありません。GC メカニズムは多くの最新のプログラミング言語でサポートされており、GC 機能のパフォーマンスと利点は、異なる言語間の対照指標の 1 つでもあります。

Golang は、Go V1.3 より前のマーク アンド スイープ アルゴリズムなど、GC の進化において多くの変更を経験しました。Go V1.3 より前のマークとスイープの欠点。

次のバージョンの変更点に注目してください。

  • Go V1.5 の 3 色同時マーキング方法
  • Go V1.5 の 3 色のマーキングには STW が必要なのはなぜですか?
  • Go V1.5 の 3 色マークにバリア メカニズム (「強-弱」3 色不変式、挿入バリア、削除バリア) が必要な理由
  • Go V1.8 ハイブリッド ライト バリア メカニズム
  • Go V1.8 ハイブリッド ライト バリア メカニズムの完全なシナリオ分析

1. Go V1.3 より前のマーク アンド スイープ アルゴリズム

次に、Golang 1.3 以前に主に使用されていた一般的なマーク アンド クリア アルゴリズムを見てみましょう。このアルゴリズムには主に 2 つの主要なステップがあります。

  • マークフェーズ
  • スイープフェーズ
1. マーク除去アルゴリズムの具体的な手順

最初のステップは、プログラムのビジネス ロジックを一時停止し、到達可能なオブジェクトと到達不可能なオブジェクトを分類し、それらをマークすることです。

図はプログラムとオブジェクトの到達可能な関係を示しており、現在プログラムの到達可能なオブジェクトにはオブジェクト1-2-3とオブジェクト4-7の5つのオブジェクトが含まれています。

2 番目のステップはマークの開始であり、プログラムは到達可能なすべてのオブジェクトを見つけてマークします。以下に示すように:

したがって、オブジェクト 1-2-3 とオブジェクト 4-7 を含む 5 つのオブジェクトがマークされます。

3 番目のステップは、マークを付けた後にマークされていないオブジェクトを消去することで、結果は次のようになります。

操作は非常に簡単ですが、特別な注意が必要な点が 1 つあります。それは、マーク アンド スイープ アルゴリズムを実行するときに、プログラムを一時停止する必要があることです。つまりSTW(stop the world)、STW 処理中、CPU はユーザー コードを実行せず、完全にガベージ コレクションに使用されますが、この処理は大きな影響を与えるため、STW は一部のリサイクル機構にとって最大の問題であり、最適化が望まれる点でもあります。したがって、3 番目のステップの実行中、プログラムは一時的に作業を停止し、リサイクルが完了するまで待機したままになります。

ステップ 4 : 一時停止を停止し、プログラムの実行を継続します。このプロセスは、プロセス プログラムのライフ サイクルが終了するまでループで繰り返されます。

以上がマーク&スイープリサイクルのアルゴリズムです。

2 マークとスイープのデメリット

マークアンドクリアのアルゴリズムは明確で、プロセスは簡単ですが、非常に深刻な問題もあります。

  • STW、世界を止めてください。プログラムを一時停止すると、プログラムがスタックします(重要な問題です)
  • マーキングにはヒープ全体をスキャンする必要があります。
  • データを消去すると、ヒープの断片化が発生します。

上記は Go V1.3 より前に実装されていたもので、図に示すように、GC 実行の基本的なプロセスは、最初に STW 一時停止を開始し、次にマーキングを実行し、次にデータのリサイクルを実行し、最後に STW を停止します。

上の図から、すべての GC 時間が STW 範囲内に収まっており、プログラムの一時停止時間が長すぎるため、プログラムの実行パフォーマンスに影響を与えているようです。したがって、Go V1.3 では、STW のステップを進め、STW の一時停止の時間範囲を短縮するための簡単な最適化が行われました。次のように

上の図は主に STW ステップを 1 ステップ進めます。これは、スイープがクリアされると、これらのオブジェクトはすでに到達不可能なオブジェクトであるため、STW を停止する必要がなく、書き込み競合のリサイクルなどの問題が発生しないためです。

しかし、どんなに最適化されても、Go V1.3 は重要な問題に直面しています。それは、マーク アンド スイープ アルゴリズムによってプログラム全体が一時停止されてしまうということです。

Go はこの問題にどのように対処しますか? 次に、G V1.5 バージョンでは、3 色同時マーキング方式を使用して、この問題を最適化します。

3. Go V1.5の3色同時マーキング方法

Golang のガベージコレクションは主に 3 色マーキング方式を使用します GC プロセスと他のユーザーゴルーチンは並行して実行できますが、一定時間のSTW (ストップ ザ ワールド)が必要ですいわゆる3 色マーキング方式は実際に決定されています3段階のマーキングで透明な物体とは何ですか?具体的なプロセスを見てみましょう。

最初のステップでは、図に示すように、新しいオブジェクトが作成されるたびに、デフォルトの色が「白」とマークされます。

上の図に示すように、プログラムが到達できるメモリ オブジェクトの関係は左側に示されており、右側のタグ テーブルは各オブジェクトの現在のタグの色分類を記録するために使用されます。ここで注意しなければならないのは、いわゆる「プログラム」とは、いくつかのオブジェクトのルートノードの集合であるということです。したがって、「プログラム」を展開すると、図に示すように、次のような表現が得られます。

2 番目のステップでは、GC リサイクルが開始されるたびに、ルート ノードから始めてすべてのオブジェクトが走査され、図に示すように、走査されたオブジェクトは白いコレクションから「灰色」のコレクションに入れられます。

ここで注意すべき点は、この走査は非再帰形式での 1 回限りの走査であるということです。プログラムから到達できるオブジェクトの 1 レベルを走査します。上の図に示すように、現在到達可能なオブジェクトはオブジェクト 1 です。ラウンド トラバースの終了時に、オブジェクト 1 とオブジェクト 4 が灰色でマークされ、これら 2 つのオブジェクトが灰色のマーク テーブルに追加されます。

3 番目のステップは、図に示すように、グレーのコレクションを走査し、グレーのオブジェクトによって参照されるオブジェクトを白のコレクションからグレーのコレクションに配置し、次にグレーのオブジェクトを黒のコレクションに配置します。

この走査は灰色のオブジェクトのみをスキャンし、灰色のオブジェクトの最初のレイヤーにある到達可能なオブジェクトを白から灰色に変更します (オブジェクト 2、オブジェクト 7 など)。以前の灰色のオブジェクト 1 と 4 は黒としてマークされます。グレーマークテーブルからブラックマークテーブルへ。

ステップ 4 :図に示すように、灰色のオブジェクトがなくなるまでステップ 3を繰り返します。

到達可能なオブジェクトがすべてトラバースされると、グレー マーク テーブルにグレーのオブジェクトはなくなります。現在、すべてのメモリ内のデータには黒と白の 2 色しかありません。次に、黒いオブジェクトは、プログラム ロジックによって到達可能な (必要な) オブジェクトです。これらのデータは現在、プログラムの通常のビジネス操作をサポートしています。これらは合法で有用なデータであり、削除できません。白いオブジェクトはすべて到達不可能なオブジェクトです。現在のプログラム ロジックがそれらに依存していない場合、白いオブジェクトは現在メモリ内にあるジャンク データであり、クリアする必要があります。

ステップ 5 : 白いマークの付いたテーブル内のすべてのオブジェクトをリサイクルします (図に示すように、ゴミをリサイクルします)。

上記では、すべての白いオブジェクトを削除してリサイクルしており、残っているのはすべての依存する黒いオブジェクトです。

上記は三色并发标记法、理解するのが難しいことではありませんが、上で明確に反映された特徴です三色ただし、スキャンされる同時プロセスが多数ある可能性があり、同時実行プロセスのメモリが相互に依存する可能性があるため、GC プロセス中のデータのセキュリティを確保するために、3 色マーキングを開始する前に STW を追加します。スキャン後に白黒を判定し、オブジェクトを押してSTWを放します。しかし、そのような GC スキャンのパフォーマンスが低すぎることは明らかです。

では、Go はマーク アンド スイープ アルゴリズムにおけるスタッター (stw、ストップ ザ ワールド) 問題をどのように解決するのでしょうか?

4. STWを使用しない3色マーキング方式

興味深いことから始めましょう。STW なしで STW を追加した場合、パフォーマンスに問題はありません。次に、3 色マーキング方法に STW を追加しなかった場合はどうなるかを考えてみましょう。
私たちは依然として上記の 3 色同時マーキング方法に基づいていますが、これは STW に依存する必要があります。プログラムが一時停止されていない場合、プログラムのロジックによってオブジェクト参照関係が変更されるためです。マーキング段階でこのアクションが変更されると、マーキング結果に影響します。正確を期すために、シナリオを見てみましょう。3 色マーキング方法がマーキング プロセスで STW を使用しない場合、何が起こるでしょうか?

初期状態として 1 回目のスキャンが完了したものとします。現在、黒のオブジェクト 1 とオブジェクト 4、灰色のオブジェクト 2 とオブジェクト 7、その他は白いオブジェクトで、オブジェクト 2 はオブジェクト 3 を経由してオブジェクト 3 を指しています。写真が示すように、ポインター p. 。

ここで、3 色マーキング プロセスが STW を開始しない場合、GC スキャン プロセス中に、任意のオブジェクトに対して読み取りおよび書き込み操作が発生する可能性があります。図に示すように、オブジェクト 2 がスキャンされる前に、オブジェクト 4 は黒としてマークされています。この時点で、ポインタ q が作成され、白いオブジェクト 3 を指します。

同時に、灰色の物体2がポインタpを取り除くので、図に示すように、実際には、白い物体3が、スキャンされた黒い物体4の下にぶら下がることになる。

次に、通常の 3 色マーキングのアルゴリズム ロジックを指定し、すべての灰色のオブジェクトを黒としてマークし、図に示すように、オブジェクト 2 とオブジェクト 7 が黒としてマークされます。

次に、3 色マーキングの最後のステップが実行され、図に示すように、白いオブジェクトはすべてゴミとしてリサイクルされます。

しかし最終的に、オブジェクト 3 は、もともとオブジェクト 4 への正当な参照であったが、GC によって「誤って」リサイクルされたことが判明しました。

3 色マーキング方法では発生すると予想されない 2 つの状況が存在することがわかります。

  • 条件 1: 白いオブジェクトが黒いオブジェクトによって参照されている(白が黒の下にぶら下がっている)
  • 条件 2: 到達可能な関係にある灰色のオブジェクトと白いオブジェクトが破壊される(灰色は同時に白いオブジェクトを失う)
    上記 2 つの条件が同時に満たされると、オブジェクトの損失が発生します。

また、図のようなシーンでは、例の白いオブジェクト3の下流にオブジェクトがたくさんある場合は、それらもクリーンアップされます。

この現象の発生を防ぐために、他のユーザープログラムによるオブジェクト参照関係への干渉を直接禁止するSTWが最も簡単な方法ですが、STW処理は明らかなリソースの無駄であり、ユーザープログラム全体に多大な影響を与えますそれでは、オブジェクトが失われないようにしながら、GC 効率を合理的に向上させ、STW 時間を短縮することは可能でしょうか? 答えは「はい」です。上記の 2 つの必要な条件を破壊するメカニズムを使用する必要があるだけです。

5. バリア機構

次の 2 つの条件のいずれかが満たされた場合に、GC コレクターにオブジェクトが失われないようにします。これら 2 つの方法は、「強い 3 色不変式」と「弱い 3 色不変式」です。

(1) 「強弱」三色不変式
  • 強力な三色不変条件

黒いオブジェクトから白いオブジェクトへのポインタはありません。

強力な 3 色の変色は実際には必須であり、白いオブジェクトが誤って削除されないように、黒いオブジェクトが白いオブジェクトを参照することを許可しません。

  • 弱い三色不変量

黒いオブジェクトによって参照されるすべての白いオブジェクトは、灰色の保護状態になります。

弱い 3 色不変式は、黒いオブジェクトが白いオブジェクトを参照できることを強調しますが、この白いオブジェクトにはそれを参照する他の灰色のオブジェクトが存在するか、そのオブジェクトに到達できるリンクの上流に灰色のオブジェクトが存在する必要があります。このように、黒いオブジェクトは白いオブジェクトを参照しており、白いオブジェクトは削除される危険な状態にありますが、上流の灰色のオブジェクトを参照することで白いオブジェクトを保護し安全にすることができます。

上記 2 つの方法を踏襲するために、GC アルゴリズムは「バリアの挿入」と「バリアの削除」の 2 つのバリア方法に進化しました。

(2) インサートバリア

具体操作: オブジェクト A がオブジェクト B を参照している場合、オブジェクト B は灰色でマークされます。(B を A の下流に吊り下げます。B は灰色でマークする必要があります)

满足:強力な 3 色不変式(白は強制的に灰色になるため、黒いオブジェクトが白いオブジェクトを参照する状況はなくなりました)

疑似コードは次のとおりです。

添加下游对象(当前下游对象slot, 新下游对象ptr) {   
  //1
  标记灰色(新下游对象ptr)   
  
  //2
  当前下游对象slot = 新下游对象ptr  				  
}

シーン:

A.添加下游对象(nil, B)   //A 之前没有下游, 新添加一个下游对象B, B被标记为灰色
A.添加下游对象(C, B)     //A 将下游对象C 更换为B,  B被标记为灰色

この疑似コードのロジックはバリアを記述することです。黒いオブジェクトのメモリ スロットには と の2 つの場所があることがわかります。スタック領域は容量が小さいという特徴がありますが、応答速度が要求されます。関数呼び出しが頻繁に使用されるため、"バリアの挿入" "メカニズムはスタック スペース オブジェクトの操作では使用されません。ヒープ スペース オブジェクトの操作でのみ使用されます。

次に、プロセス全体をより明確に理解できるように、いくつかの画像を使用して詳細なプロセス全体をシミュレーションします。







ただし、スタックが追加されない場合、すべての 3 色マーカーがスキャンされた後でも、スタック上で参照されている白いオブジェクト (上の図のオブジェクト 9 など) がまだ存在する可能性があるため、スタックを 3 色用に再度スキャンする必要があります。マーカー、ただし今回はオブジェクトです。紛失していない場合は、スタック スペースの 3 色のマークが終了するまで、このマーク スキャンの STW 一時停止を開始する必要があります。





最後に、スタックに残っているすべての白いノードとヒープ領域のスキャンがクリアされますが、このときの STW のおおよその時間は 10 ~ 100ms です。


(3) バリアの削除

具体操作: 削除されたオブジェクトがグレーまたは白の場合、グレーとしてマークされます。

满足:弱い 3 色不変式(灰色のオブジェクトから白色のオブジェクトへのパスが壊れないように保護します)

疑似コード:

添加下游对象(当前下游对象slot, 新下游对象ptr) {
  //1
  if (当前下游对象slot是灰色 || 当前下游对象slot是白色) {
  		标记灰色(当前下游对象slot)     //slot为被删除对象, 标记为灰色
  }
  
  //2
  当前下游对象slot = 新下游对象ptr
}

シーン:

A.添加下游对象(B, nil)   //A对象,删除B对象的引用。  B被A删除,被标记为灰(如果B之前为白)
A.添加下游对象(B, C)		 //A对象,更换下游B变成C。   B被A删除,被标记为灰(如果B之前为白)

次に、プロセス全体をより明確に理解できるように、いくつかの画像を使用して詳細なプロセス全体をシミュレーションします。

このメソッドのリサイクル精度は低く、オブジェクトが削除され、それを指す最後のポインターが削除された場合でも、そのオブジェクトはこのラウンドで存続し、次の GC ラウンドでクリーンアップされる可能性があります。

6. Go V1.8のハイブリッドライトバリア機構

書き込みバリアの挿入と削除の欠点:

  • 書き込みバリアの挿入: STW は、スタック上で参照されている白いオブジェクトの生存をマークするために、最後にスタックを再スキャンする必要があります。
  • 書き込みバリアの削除: リサイクルの精度は低くなります。GC が開始されると、STW はスタックをスキャンして初期スナップショットを記録します。このプロセスにより、最初に残っているすべてのオブジェクトが保護されます。

Go V1.8 ではハイブリッド ライト バリア メカニズムが導入されており、これによりスタックの再スキャンのプロセスが回避され、STW 時間が大幅に短縮されます。両方の長所を組み合わせます。


(1) ミックスライトバリアルール

具体操作:

1. GC はスタック上のすべてのオブジェクトのスキャンを開始し、到達可能なすべてのオブジェクトを黒としてマークします (その後 2 回目の繰り返しスキャンは実行されず、STW は必要ありません)。

2. GC 中、スタック上に作成された新しいオブジェクトは黒になります。

3. 削除されたオブジェクトは灰色でマークされます。

4. 追加されたオブジェクトは灰色でマークされます。

满足: 変形された弱い三色不変量

疑似コード:

添加下游对象(当前下游对象slot, 新下游对象ptr) {
  	//1 
		标记灰色(当前下游对象slot)    //只要当前下游对象被移走,就标记灰色
  	
  	//2 
  	标记灰色(新下游对象ptr)
  		
  	//3
  	当前下游对象slot = 新下游对象ptr
}

ここで、スタックの動作効率を確保する必要があるため、スタックにはバリア技術が適用されていないことに注意してください。

(2) ハイブリッドライトバリアの具体的なシナリオ分析

次に、プロセス全体をより明確に理解できるように、いくつかの画像を使用して詳細なプロセス全体をシミュレーションします。

混合書き込みバリアは Gc のバリア メカニズムであるため、このメカニズムはプログラムが GC を実行する場合にのみトリガーされることに注意してください。

GC の開始: スタック領域をスキャンし、到達可能なすべてのオブジェクトを黒としてマークします。


シナリオ 1: オブジェクトはヒープ オブジェクトによって逆参照され、スタック オブジェクトの下流になります

疑似コード

//前提:堆对象4->对象7 = 对象7;  //对象7 被 对象4引用
栈对象1->对象7 = 堆对象7;  //将堆对象7 挂在 栈对象1 下游
堆对象4->对象7 = null;    //对象4 删除引用 对象7

シナリオ 2: オブジェクトはスタック オブジェクトから削除され、別のスタック オブジェクトの下流になります。

疑似コード

new 栈对象9;
对象8->对象3 = 对象3;      //将栈对象3 挂在 栈对象9 下游
对象2->对象3 = null;      //对象2 删除引用 对象3

内線: 質問してください

上の図に示すように、オブジェクト 9 がオブジェクト 5 を参照しており、スタックにバリアがない場合、オブジェクト 5 は最終的に白のままですが、これにより誤って削除されないでしょうか?
ハイブリッド ライト バリアはヒープ上では使用されますが、スタック上では使用されません。スタック内の黒いオブジェクトが白いオブジェクトを参照していて、ライト バリアがない場合、最終的に白いオブジェクトがリサイクルされるため、問題が発生します。

調査し、教師 Liu Danbing と相談した結果、私たちは次の結論に達しました。

このような状況は起こりません。オブジェクト 9 はオブジェクト 5 を認識できないため、到達できません。オブジェクト 5 が到達可能なオブジェクトである場合、オブジェクト 5 は白くなりません。

白は、リンクが壊れていて参照できないことを意味します。それ以外の場合、STW トラバーサル中に白としてマークされません。

もう一度考えてみて:

オブジェクト 2 がオブジェクト 3 への参照を削除し、新しいオブジェクトが 3 を再参照しない場合、オブジェクト 3 はこの GC ラウンドでリサイクルされますか?

バリアメカニズムはスタックに適用されないため、このラウンドではリサイクルされず、次のスキャンまで白としてマークされます。

シナリオ 3: オブジェクトは 1 つのヒープ オブジェクトによって逆参照され、別のヒープ オブジェクトの下流になります

疑似コード

堆对象10->对象7 = 堆对象7;       //将堆对象7 挂在 堆对象10 下游
堆对象4->对象7 = null;         //对象4 删除引用 对象7

シナリオ 4: オブジェクトがスタック オブジェクトから参照を削除し、別のヒープ オブジェクトの下流になる

疑似コード

堆对象10->对象7 = 堆对象7;       //将堆对象7 挂在 堆对象10 下游
堆对象4->对象7 = null;         //对象4 删除引用 对象7

Golang のハイブリッド ライト バリアは満たされており弱三色不变式、ライト バリアの削除とライト バリアの挿入の利点を組み合わせています。最初に各ゴルーチンのスタックを同時にスキャンして黒にして保持するだけで済みます。このプロセスには STW は必要ありません。マーキングが完了すると、スキャン後のスタックは常に黒になるため、再スキャン操作を実行する必要がなく、STW 時間が短縮されます。

7. まとめ

上記は、Golang の GC のマーキングクリアロジックとシナリオのデモンストレーションプロセス全体です。

GoV1.3 - 通常のマークとクリアの方法では、プロセス全体で STW を開始する必要があり、非常に非効率です。

GoV1.5 - 3 色マーキング方式、ヒープ領域は書き込みバリアをアクティブにしますが、スタック領域はアクティブにしません、すべてのスキャン後にスタックを再スキャンする必要があります (STW が必要)、効率は平均的です

GoV1.8 - 3 色マーキング方式、混合書き込みバリア機構、スタック領域はアクティブ化されず、ヒープ領域はアクティブ化されます。プロセス全体では STW がほとんど必要なく、非常に効率的です。

著作権に関する声明

この記事の内容は著者の許可を得て転載しています

元のリンク: https://www.yuque.com/aceld/golang/zhzanb

一緒に勉強する

私の記事は、最初に同じ名前の公開アカウントに公開されます。フォロー歓迎: Wang Zhongyang Go

おすすめ

転載: blog.csdn.net/w425772719/article/details/135283921