Java仮想マシン-世代別GC
ほとんどのJVMは、ヒープを3世代に分割します- the young generation (YG), the old generation (OG) and permanent generation (also called tenured generation)。そのような考えの背後にある理由は何ですか?
経験的研究によると、作成されるオブジェクトのほとんどは非常に短い寿命を持っています-
ソース
https://www.oracle.com
ご覧のとおり、時間とともに割り当てられるオブジェクトが増えるにつれて、存続するバイト数は少なくなります(一般的に)。Javaオブジェクトは高い死亡率を持っています。
簡単な例を見てみましょう。JavaのStringクラスは不変です。つまり、Stringオブジェクトの内容を変更する必要があるたびに、新しいオブジェクトを作成する必要があります。次のコードに示すように、ループ内で文字列を1000回変更するとします。
String str = “G11 GC”;
for(int i = 0 ; i < 1000; i++) {
str = str + String.valueOf(i);
}
各ループで、新しい文字列オブジェクトを作成すると、前の反復で作成された文字列は役に立たなくなります(つまり、参照によって参照されません)。そのオブジェクトの存続期間は1回の反復でした。これらは、すぐにGCによって収集されます。このような短命のオブジェクトは、ヒープの若い世代の領域に保持されます。若い世代からオブジェクトを収集するプロセスはマイナーガベージコレクションと呼ばれ、常に「世界を止める」一時停止を引き起こします。
若い世代がいっぱいになると、GCはマイナーなガベージコレクションを実行します。デッドオブジェクトは破棄され、ライブオブジェクトは古い世代に移動されます。このプロセス中、アプリケーションスレッドは停止します。
ここでは、そのような世代の設計が提供する利点を見ることができます。若い世代はヒープのごく一部であり、すぐにいっぱいになります。ただし、処理にかかる時間は、ヒープ全体の処理にかかる時間よりもはるかに短くなります。したがって、この場合の「stop-theworld」の一時停止は、より頻繁ではありますが、はるかに短くなります。頻繁に発生する場合でも、長い一時停止よりも短い一時停止を常に目指す必要があります。これについては、このチュートリアルの後のセクションで詳しく説明します。
若い世代は2つのスペースに分かれています- eden and survivor space。エデンの収集中に生き残ったオブジェクトはサバイバースペースに移動され、サバイバースペースを生き残ったオブジェクトは古い世代に移動されます。若い世代は集められながら圧縮されます。
オブジェクトが古い世代に移動すると、最終的にはいっぱいになり、収集して圧縮する必要があります。アルゴリズムが異なれば、これに対するアプローチも異なります。それらのいくつかはアプリケーションスレッドを停止します(古い世代は若い世代と比較して非常に大きいため、長い「世界を止める」一時停止につながります)が、アプリケーションスレッドの実行中に同時に停止するものもあります。このプロセスはフルGCと呼ばれます。そのような2つのコレクターはCMS and G1。
これらのアルゴリズムを詳細に分析してみましょう。
シリアルGC
これは、クライアントクラスのマシン(シングルプロセッサマシンまたは32b JVM、Windows)のデフォルトのGCです。通常、GCは高度にマルチスレッド化されていますが、シリアルGCはそうではありません。ヒープを処理するための単一のスレッドがあり、マイナーGCまたはメジャーGCを実行しているときはいつでもアプリケーションスレッドを停止します。フラグを指定することにより、このGCを使用するようにJVMに命令できます。-XX:+UseSerialGC。別のアルゴリズムを使用する場合は、アルゴリズム名を指定します。旧世代は、主要なGC中に完全に圧縮されることに注意してください。
スループットGC
このGCは、64bJVMおよびマルチCPUマシンのデフォルトです。シリアルGCとは異なり、複数のスレッドを使用して若い世代と古い世代を処理します。このため、GCはparallel collector。フラグを使用して、このコレクターを使用するようにJVMに命令できます。-XX:+UseParallelOldGC または -XX:+UseParallelGC(JDK 8以降の場合)。メジャーまたはマイナーのガベージコレクションを実行している間、アプリケーションスレッドは停止します。シリアルコレクターのように、それは主要なGCの間に若い世代を完全に圧縮します。
スループットGCはYGとOGを収集します。エデンがいっぱいになると、コレクターは生きているオブジェクトをOGまたはサバイバースペースの1つ(下の図のSS0とSS1)に排出します。死んだオブジェクトは、それらが占めていたスペースを解放するために破棄されます。
YGのGC前
YGのGC後
フルGCの間、スループットコレクターはYG、SS0、およびSS1全体を空にします。操作後、OGにはライブオブジェクトのみが含まれます。上記のコレクターは両方とも、ヒープの処理中にアプリケーションスレッドを停止することに注意してください。これは、メジャーGC中に長い「stopthe-world」が一時停止することを意味します。次の2つのアルゴリズムは、より多くのハードウェアリソースを犠牲にして、それらを排除することを目的としています。
CMSコレクター
'concurrentmark-sweep'の略です。その機能は、いくつかのバックグラウンドスレッドを使用して古い世代を定期的にスキャンし、死んだオブジェクトを取り除くことです。ただし、マイナーGCの間、アプリケーションスレッドは停止します。ただし、一時停止は非常に小さいです。これにより、CMSは一時停止の少ないコレクターになります。
このコレクターは、アプリケーションスレッドの実行中にヒープをスキャンするために追加のCPU時間を必要とします。さらに、バックグラウンドスレッドはヒープを収集するだけで、圧縮は実行しません。ヒープが断片化する可能性があります。これが続くと、特定の時点の後、CMSはすべてのアプリケーションスレッドを停止し、単一のスレッドを使用してヒープを圧縮します。次のJVM引数を使用して、JVMにCMSコレクターを使用するように指示します-
“XX:+UseConcMarkSweepGC -XX:+UseParNewGC” CMSコレクターを使用するように指示するJVM引数として。
GC前
GC後
収集は同時に行われていることに注意してください。
G1 GC
このアルゴリズムは、ヒープをいくつかの領域に分割することで機能します。CMSコレクターと同様に、マイナーGCの実行中にアプリケーションスレッドを停止し、バックグラウンドスレッドを使用して、アプリケーションスレッドを続行しながら古い世代を処理します。古い世代をリージョンに分割したため、オブジェクトをあるリージョンから別のリージョンに移動しながら、それらを圧縮し続けます。したがって、断片化は最小限に抑えられます。フラグを使用できます:XX:+UseG1GCこのアルゴリズムを使用するようにJVMに指示します。CMSと同様に、ヒープの処理とアプリケーションスレッドの同時実行にもCPU時間が必要です。
このアルゴリズムは、いくつかの異なる領域に分割されたより大きなヒープ(> 4G)を処理するように設計されています。それらの地域のいくつかは若い世代を構成し、残りは古い世代を構成します。YGは、従来の方法でクリアされます。すべてのアプリケーションスレッドが停止され、古い世代またはサバイバースペースにまだ存在しているすべてのオブジェクトが停止されます。
すべてのGCアルゴリズムがヒープをYGとOGに分割し、STWPを使用してYGをクリアすることに注意してください。このプロセスは通常、非常に高速です。