ヒープオブジェクト割り当てプロセス
概念
新しいオブジェクトへのメモリの割り当ては、非常に厳密で複雑なタスクです。JVM設計者は、メモリを割り当てる方法と場所を考慮する必要があるだけでなく、メモリ割り当てアルゴリズムがメモリ回復アルゴリズムと密接に関連しているため、GCも考慮する必要があります。メモリの再利用が実行された後、メモリの断片化がメモリ空間で生成されるかどうか。
以下の点について説明します。
- 新しいオブジェクトは最初にエデンパークに配置されます。このエリアにはサイズ制限があります。
- エデンの園のスペースがいっぱいになると、プログラムはオブジェクトを再度作成する必要があります。JVMのガベージコレクターは、エデンの園でガベージコレクション(MinorGC)を実行し、エデンの園で使用されなくなったオブジェクトを破棄します。他のオブジェクトによって参照され、新しいオブジェクトをロードします。対象をエデンパークに配置します。
- 次に、エデンの園の残りのオブジェクトをサバイバーゾーン0に移動します。
- ガベージコレクションが再度トリガーされると、前回生き残ったものはサバイバー0エリアに配置され、収集されない場合はサバイバー1エリアに配置されます(エリア0とエリア1は互いに交換されます)。
- 再度ガベージコレクションを行うと、この時点でサバイバー0エリアに戻され、サバイバー1エリアに移動します。
- いつリタイアメントエリアに行けますか?回数を設定できます。デフォルトは15回です。
- リタイアメントエリアでは、比較的のんびりしています。老朽化したエリアのメモリが不足すると、GCが再度トリガーされます。メジャーGCは、老朽化したエリアのメモリをクリーンアップします。
- 高齢者ケアエリアでメジャーGCを実行した後もオブジェクトを保存できないことが判明した場合、OOM例外が発生します。
パラメータ(回数)を設定できます:-Xx:MaxTenuringThreshold = N設定します。
グラフィカルなプロセス
-
作成するオブジェクトは通常、Eden領域に格納されます。Eden領域がいっぱいになると、GC操作がトリガーされます。これは一般にYGC /マイナーGC操作と呼ばれます。
-
ガベージコレクションを行うと、赤いものはリサイクルされ、緑のものはS0(Survivor From)エリアに占有されて保管されます。同時に、オブジェクトごとに年齢カウンターが設定されます。これは、1回のコレクションで1になります。
-
同時に、エデンエリアは引き続きオブジェクトを保存します。エデンエリアが再びいっぱいになると、MinorGC操作がトリガーされます。このとき、GCはエデンとサバイバーのオブジェクトを一度から収集し、生き残ったオブジェクトを配置します。 Survivor Toエリアで。同時に、年齢+1とします。
-
オブジェクトの生成とガベージコレクションを継続します。サバイバーのオブジェクトの年齢が15歳に達すると、昇格操作がトリガーされます。つまり、若い世代のオブジェクトが古い世代に昇格されます。サバイバー
エリアがいっぱい?
- MinorGCは、Edenエリアがいっぱいになったときにのみトリガーされ、MinorGC操作はサバイバーエリアがいっぱいになったときにトリガーされないことに特に注意してください。
- サバイバーエリアがいっぱいになると、いくつかの特別なルールがトリガーされます。つまり、古い世代に直接昇格する場合があります。
例:軍隊を例にとると、普通の人の昇進は次のようになります:新兵->分隊長->小隊長->中隊長。
しかし、一部の人々が新兵から直接非常に大きな貢献をした可能性もあります->小隊長。
オブジェクト割り当ての特殊なケースの
コードは、オブジェクト割り当てプロセスを示しています
サンプルプログラム:常に大きなオブジェクトを作成し、リストに追加します。
public class HeapInstanceTest {
byte [] buffer = new byte[new Random().nextInt(1024 * 200)];
public static void main(String[] args) throws InterruptedException {
ArrayList<HeapInstanceTest> list = new ArrayList<>();
while (true) {
list.add(new HeapInstanceTest());
Thread.sleep(10);
}
}
}
次に、JVMパラメータを設定します。
-Xms600m -Xmx600m
次に、VisualVMツールを開き、上記のコードを実行してVisualGCを動的に表示します。
最後に、新旧の世代がいっぱいになると、OOMが表示されます。
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at com.heu.heap.HeapInstanceTest.<init>(HeapInstanceTest.java:13)
at com.heu.heap.HeapInstanceTest.main(HeapInstanceTest.java:17)
総括する
- 生存者の要約s0、s1領域:コピー後に交換があり、空の人は誰でも(s0、s1は固定されていません)。
- ガベージコレクションについて:新生児地域で頻繁に収集され、旧世代ではめったに収集されず、恒久的な世代やメタスペースではほとんど収集されません。
- 新世代のレプリケーションアルゴリズムの目的:内部の断片化を減らすこと。
マイナーGC、メジャーGC、フルGC
-
マイナーGC:新世代のGC
-
メジャーGC:老後のGC
-
フルGC:ヒープ全体のコレクション、Javaヒープ全体とメソッド領域を収集するガベージコレクション
-
JVMチューニングの一部がガベージコレクションであることは誰もが知っています。ガベージコレクションの過程でSTW(文章題)の問題が発生しやすく、メジャーGCとフルGCがあるため、ガベージコレクションはできるだけ避ける必要があります。 STWが表示される時間は、マイナーGCの10倍以上です。
-
JVMがGCを実行しているとき、常に上記の3つのメモリ領域を一緒に再利用するとは限りません。ほとんどの場合、再利用は新世代を指します。Hotspot VMの実装では、その中のGCは、リカバリ領域に応じて2つのタイプに分けられます。1つは部分収集(Partial GC)で、もう1つは完全収集(FullGC)です。
部分的なコレクション:ガベージコレクションは、Javaヒープ全体の完全なコレクションではありません。これは次のように分けられます。
- 若い世代のコレクション(MinorGC / YoungGC):若い世代のガベージコレクションのみ。
- 旧世代のコレクション(MajorGC / o1dGC):旧世代のガベージコレクションのみ。
-現在、CMSGCのみが古い世代を個別に収集する動作を持っています;-
多くの場合、メジャーGCはフルGCと混同されるため、古い世代のコレクションかヒープ全体のコレクションかを明確に区別する必要があることに注意してください。 - 混合コレクション(MixedGC):若い世代全体と古い世代の一部のガベージコレクションを収集します。
-現在、G1GCのみがこの動作をします。
ヒープ全体のコレクション(FullGC):Javaヒープ全体とメソッド領域のガベージコレクションを収集します。
マイナーGC
- 若い世代のスペースが不足している場合、MinorGCがトリガーされます。ここでの若い世代のフルとは、Eden世代のフルを指し、SurvivorのフルはGCをトリガーしません(マイナーGCが若い世代のメモリをクリーンアップするたびに) ;
- ほとんどのJavaオブジェクトにはChaoshengのイブニングオフ機能があるため、マイナーGCは非常に頻繁に発生し、通常は比較的高速に回復します。
- マイナーGCは、STWをトリガーし(ワードを停止し)、他のユーザーのスレッドを一時停止し、ガベージコレクションが終了するまで待ってから、ユーザースレッドの実行を再開します。
メジャーGC
マジョイGCとは、老年期に発生したGCのことで、老年期からオブジェクトが消えると、「メジャーGc」または「フルGC」が発生したと言われます。
- メジャーGCの出現には、少なくとも1つのマイナーGCが伴うことがよくあります(絶対ではありませんが、Paralle1 Scavengeコレクターのコレクション戦略には直接メジャーGC戦略選択プロセスがあります)。つまり、旧世代のスペースが不十分な場合、最初にマイナーGCをトリガーしようとします。その後、十分なスペースがない場合、メジャーGCがトリガーされます。
- メジャーGCの速度は一般にマイナーGcの10倍以上遅く、STWの時間は長くなります。メジャーGCの後でメモリが不足している場合は、OOMとして報告されます。
フルGC
フルGCの実行をトリガーする5つの状況があります。
- System.gc()を呼び出すとき、システムはフルGCを実行することを推奨しますが、必ずしも実行されるとは限りません。
- 老後の不十分なスペース;
- メソッド領域のスペースが不十分です。
- マイナーGCを通過した後の古い世代の平均サイズは、古い世代の使用可能なメモリよりも大きくなっています。
- Edenエリアとsurvivorspacee(From Space)エリアからsurvivor spacel(To Space)エリアにコピーするときに、オブジェクトのサイズがTo Spaceの使用可能なメモリよりも大きい場合、オブジェクトは古いものに転送されます。年齢、および古い時代の利用可能なメモリはオブジェクトのサイズよりも小さいです(つまり、若い世代と古い世代のメモリサイズはオブジェクトに適合できません)。
注:開発中またはチューニング中は、完全なGCをできるだけ避ける必要があります。これにより、一時的な時間が短くなります。
GCの例
OOM例外を記述し、常に文字列の例を作成します。
public class GCTest {
public static void main(String[] args) {
int i = 0;
try {
List<String> list = new ArrayList<>();
String a = "mogu blog";
while(true) {
list.add(a);
a = a + a;
i++;
}
}catch (Exception e) {
e.getStackTrace();
}
}
}
JVM起動パラメータを設定します。
-Xms10m -Xmx10m -XX:+PrintGCDetails
印刷されたログ:
[GC (Allocation Failure) [PSYoungGen: 2038K->500K(2560K)] 2038K->797K(9728K), 0.3532002 secs] [Times: user=0.01 sys=0.00, real=0.36 secs]
[GC (Allocation Failure) [PSYoungGen: 2108K->480K(2560K)] 2405K->1565K(9728K), 0.0014069 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (Ergonomics) [PSYoungGen: 2288K->0K(2560K)] [ParOldGen: 6845K->5281K(7168K)] 9133K->5281K(9728K), [Metaspace: 3482K->3482K(1056768K)], 0.0058675 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
[GC (Allocation Failure) [PSYoungGen: 0K->0K(2560K)] 5281K->5281K(9728K), 0.0002857 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (Allocation Failure) [PSYoungGen: 0K->0K(2560K)] [ParOldGen: 5281K->5263K(7168K)] 5281K->5263K(9728K), [Metaspace: 3482K->3482K(1056768K)], 0.0058564 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
Heap
PSYoungGen total 2560K, used 60K [0x00000000ffd00000, 0x0000000100000000, 0x0000000100000000)
eden space 2048K, 2% used [0x00000000ffd00000,0x00000000ffd0f138,0x00000000fff00000)
from space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000)
to space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)
ParOldGen total 7168K, used 5263K [0x00000000ff600000, 0x00000000ffd00000, 0x00000000ffd00000)
object space 7168K, 73% used [0x00000000ff600000,0x00000000ffb23cf0,0x00000000ffd00000)
Metaspace used 3514K, capacity 4498K, committed 4864K, reserved 1056768K
class space used 388K, capacity 390K, committed 512K, reserved 1048576K
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOfRange(Arrays.java:3664)
at java.lang.String.<init>(String.java:207)
at java.lang.StringBuilder.toString(StringBuilder.java:407)
at com.heu.heap.GCTest.main(GCTest.java:20)
[GC (Allocation Failure) [PSYoungGen: 2038K->500K(2560K)] 2038K->797K(9728K), 0.3532002 secs]
- [PSYoungGen:2038K-> 500K(2560K)]:若い世代の合計スペースは2560Kで、現在2038Kを占めており、ガベージコレクション後の残りの500Kです。
- 2038K-> 797K(9728K):ヒープメモリの合計スペースは9728Kで、現在2038Kを占めており、ガベージコレクション後も797Kが残っています。
OOM例外は、旧世代のスペースが不足している場合にのみ公開されるため、OOMがトリガーされると、フルGCを実行する必要があります。
ヒープスペース生成のアイデア
Javaヒープを世代に分割するのはなぜですか?世代を問わず正常に動作しませんか?
調査後、オブジェクトごとにライフサイクルが異なります。オブジェクトの70%〜99%は一時オブジェクトです。
- 新生代:エデンと同じサイズの2人の生存者(from / toまたはs0 / s1とも呼ばれます)で構成され、toは常に空です。
- 旧世代:多くのGCを生き残ったオブジェクトを新世代に保存します。
実際、世代を分割しないことは完全に可能です。世代の唯一の理由は、GCのパフォーマンスを最適化することです。
- 世代がない場合、すべてのオブジェクトは同じ場所にあります。これは、学校のすべての人を教室に閉じ込めるようなものです。GC中に、ヒープのすべての領域がスキャンされるように、どのオブジェクトが役に立たないかを見つける必要があります。(低性能)
- 多くのオブジェクトが生きて死ぬ。世代を生成する場合は、新しく作成したオブジェクトを特定の場所に配置します。GCの場合は、最初に「生きて死ぬ」オブジェクトが保存されている領域を再利用して、無料にします。スペースの。(若い世代のリサイクルを増やし、古い世代のリサイクルを減らすと、パフォーマンスが大幅に向上します)
オブジェクトメモリ割り当て戦略
- オブジェクトがエデンエリアで生まれ、最初のマイナーGCを生き延び、サバイバーが収容できる場合、オブジェクトはサバイバースペースに移動され、オブジェクトの年齢は1に設定されます。
- オブジェクトがサバイバーエリアのマイナーGCを生き残るたびに、その年齢は1年増加します。その年齢が特定のレベルに増加すると(デフォルトは15歳で、実際には、各JVMと各GCは異なります)、老後への昇進。
- 古い年齢に昇格するオブジェクトの年齢しきい値は、オプション**-XX:MaxTenuringThreshold **で設定できます。
さまざまな年齢層のオブジェクト割り当ての原則は次のとおりです。
- エデンへの優先順位の割り当て:開発中のより長い文字列または配列は古い時代に直接存在しますが、新しく作成されたオブジェクトは昼夜を問わず存在するため、この大きなオブジェクトもすぐにリサイクルされる可能性がありますが、古い時代のメジャーGCによってトリガーされますマイナーGCよりも回数が少ないため、収集に時間がかかる場合があります。
- 大きなオブジェクトは古い世代に直接割り当てられます。プログラム内の大きなオブジェクトが多すぎないようにしてください。
- 寿命の長いオブジェクトは、古い世代に割り当てられます。
- 動的オブジェクト年齢判定:サバイバーエリア内の同じ年齢のすべてのオブジェクトの合計サイズがサバイバースペースの半分より大きい場合、この年齢以上のオブジェクトは、待つことなく直接古い年齢に入ることができます。 MaxTenuringThresholdで必要な年齢。
- スペース割り当ての保証:-XX:HandlePromotionFailure。