ヒープ
Javaヒープは、すべてのスレッドで共有されるメモリ領域で、仮想マシンの起動時に作成されます。このメモリ領域の唯一の目的は、オブジェクト・インスタンスを保持することであり、ほとんどすべてのオブジェクト・インスタンスがここにメモリを割り当てられます。
ヒープはゴミ収集器が管理する主要な領域なので、しばしば「GC ヒープ」と呼ばれます。メモリ回収の観点からは、最近のコレクタは基本的に世代回収アルゴリズムを採用しているため、Javaヒープは新世代と旧世代に細分化され、より詳細には、Eden空間、From Survivor空間、To Survivor空間などがあります。メモリ割り当ての観点からは、スレッド共有Javaヒープは複数のスレッドプライベート割り当てバッファに分割される可能性があります。
Javaのヒープは、論理的に連続している限り、物理的に連続していないメモリ空間にあることができ、現在の主流の仮想マシンはスケーラブルとして実装されています。インスタンスの割り当てを完了するためにヒープにメモリが残っておらず、ヒープを拡張できなくなった場合、OutOfMemoryError例外がスローされます。
ゴミ収集アルゴリズム
マーク・クリア
方法論のステップ
- まず、リサイクルが必要なものに印をつけます。
- タグ付け完了後、すべてのタグ付けされたオブジェクトを一律に復元
不十分
- 効率性の問題、採点と清算の両方のプロセスが効率的ではありません;
- スペースの問題、削除のためのマーク付けは、大量の不連続なメモリ断片を作成します。スペースの断片化が多すぎると、プログラム実行の後半でより大きなオブジェクトを割り当てる必要があるときに、十分な連続メモリが見つからない場合、早期に別のゴミ収集アクションを起動しなければならなくなる可能性があります。
コピー
方法論のステップ
- 利用可能なメモリーを容量ごとに2つの同じ大きさのチャンクに分け、一度にどちらか一方だけを使用します。
- このメモリがなくなると、生き残ったオブジェクトが別のメモリにコピーされ、使用済みのメモリ領域が1つずつ掃除されます。
長所
- メモリ確保も、メモリの断片化などの複雑なことを考慮する必要がなく、トップオブヒープのポインタを移動して順番にメモリを確保するだけなので、実装が簡単で実行効率も高いです。
不十分
- メモリを元の半分のサイズに縮小するのはコストがかかりすぎます。
ビジネス仮想マシンでの応用
IBM:新世代のオブジェクトの98%は「生まれて死ぬ」。
メモリを大きなエデンスペースと小さなサバイバースペース2つに分け、エデンとサバイバーの1つを一度に使用します。 再生が完了したら、エデンとサバイバーにまだ生きているオブジェクトをもう1つのサバイバースペースに一気にコピーし、最後に先ほど使用したエデンとサバイバースペースをクリーンアップします。.
HotSpot VM のデフォルトの Eden と Survivor のサイズ比は 8:1 で、これは各新世代で使用可能なメモリの 90% が新世代全体に使用され、メモリの 10% だけが「無駄」になることを意味します。
サバイバー空間が十分でない場合、割り当て保証のために他のメモリに依存する必要があります。別のサバイバー空間が、最後の新しい世代から収集された生存オブジェクトを保持するのに十分な空間を持っていない場合、これらのオブジェクトは割り当て保証メカニズムを通じて古い世代に直接行きます。
パラメータ設定:
実行時に-Xms20M、-Xmx20M、-Xmn10Mを使用する。
これら3つのパラメーターは、Javaのヒープ・サイズを拡張不可の20MBに制限し、10MBを新世代に、残りの10MBを旧世代に割り当てる。
-XX:SurvivorRatio=8新世代のエデンゾーンとサバイバーゾーンのスペース比率は8:1と決定された。
マーカー組織
方法論のステップ
- まず、リサイクルが必要なものに印をつけます。
- 生存しているすべてのオブジェクトを一方の端に移動させ、その後、端の境界の外側でメモリをクリーンアップします。
分割世代コレクション
メモリはオブジェクトのライフサイクルによってチャンクに分割されます。
Javaのヒープは新世代と旧世代に分けるのが一般的です。
新しい世代では、各ゴミ収集は、多数のオブジェクトが死亡し、少数の生存者だけが見つかり、その後、少数の生存オブジェクトのみを複製するコストで収集を完了するために複製アルゴリズムが選択されます。
昔は、オブジェクトは生存率が高く、そのために保証を割り当てる余分なスペースがなかったため、マーク・アンド・クリーンやマーク・アンド・ソートのアルゴリズムを使ってオブジェクトを取り戻さなければなりませんでした。
オブジェクトへのメモリ割り当て
オブジェクトのメモリ割り当ては、一般的な方向ではヒープ上にあり、新しい世代では主にEden領域上にオブジェクトが割り当てられ、ローカルのスレッド割り当てバッファが開始された場合は、まずスレッド単位でTLAB上に割り当てられます。少数のケースでは、古い世代で直接割り当てられることもあります。
オブジェクトは優先的に Eden
ほとんどの場合、オブジェクトは新世代のエデンゾーンに割り当てられます。Edenゾーンに割り当てに十分な領域がない場合、VMはマイナーGCを開始します。
マイナーGC:新しい世代で発生するゴミ収集アクションのことで、Javaオブジェクトの多くは一晩で生まれて死ぬという特徴があるため、マイナーGCは非常に頻繁に行われ、一般的なリサイクル速度も比較的速いです。
老年期GC:老年期に発生したGCで、大GCを伴い、多くの場合、少なくとも1つの小GCを伴うもの。
メジャーGCは通常、マイナーGCの10倍以上遅い。
老年期に直接入る大きなもの
いわゆるラージ・オブジェクトとは、連続した大量のメモリ空間を必要とするJavaオブジェクトのことで、最も典型的なラージ・オブジェクトは、非常に長い文字列や配列です。
多くの場合、大きなオブジェクトは、それを "配置 "するのに十分な連続領域を確保するために、メモリにまだ多くの領域が残っているときに、早い段階でゴミ収集が開始される傾向があります。
パラメータ設定:
XX:PretenureSizeThresholdこの設定よりも大きなオブジェクトが古い時代に直接割り当てられるように、HandlePromotionFailureパラメータを設定する。
これは、Edenゾーンと2つのSurvivorゾーン間での大量のメモリコピーを避けるためである。
長生きするものは老境に入る
メモリ再生は、どのオブジェクトを新しい世代に置き、どのオブジェクトを古い世代に置くべきかを認識できなければなりません。
仮想マシンは各オブジェクトにオブジェクト年齢カウンターを定義します。オブジェクトがSurvivorゾーンでMinor GCを "生き残る "たびに、その年齢は1年ずつ増加し、ある年齢に達すると古い年齢に昇格します。
-XX:MaxTenuringThresholdオブジェクトがオールドエイジに昇格する年齢のしきい値を設定するパラメータ。
動的物体年齢決定
さまざまなプログラムのメモリ条件によりよく適応するために、VM は、オブジェクトの年齢がオールドエイジに昇格する前に MaxTenuringThreshold に達していなければならないとは限りません。 Survivor 空間内の同じ年齢のすべてのオブジェクトのサイズの合計が Survivor 空間の半分より大きい場合、その年齢以上の年齢のオブジェクトは、MaxTenuringThreshold で要求される年齢を待たずに、直接オールドエイジに昇格できます。Survivor 空間の同じ年齢のオブジェクトの合計が Survivor 空間の半分より大きい場合、その年齢以上の年齢のオブジェクトは、MaxTenuringThreshold で要求される年齢を待たずに、直接古い年齢に入ることができます。
Minor GC とFull GC
ほとんどの場合、オブジェクトは新世代のエデンゾーンに割り当てられます。Edenゾーンに割り当てに十分な領域がない場合、VMはマイナーGCを開始します。
Minor GCの場合、VMはまず、旧世代の最大利用可能連続空間が新世代の全オブジェクトの合計空間より大きいかどうかをチェックし、この条件が保持される場合、Minor GCは安全であることが保証されます。この条件が保持されない場合、VMはHandlePromotionFailureの設定値が保証の失敗を許可するかどうかをチェックします。もしそれが許されるのであれば、古い世代で利用可能な最大連続領域が、古い世代に昇格したオブジェクトの平均サイズよりも大きいかどうかをチェックし続け、もしそれが大きければ、このGCがリスキーであってもMinor GCを試みます。フルGCに変更されます。
前述したように、新世代は複製されたコレクション・アルゴリズムを使用しますが、メモリ利用のための回転バックアップとしてSurvivorスペースの1つだけを使用するため、Minor GC後に多数のオブジェクトがまだ生きている場合、旧世代はそれらを割り当てる必要があります!保証:Survivorが保持できないオブジェクトを直接旧世代に取り込みます。人生におけるローン保証と同様に、旧世代がこのような保証をしなければならないのは、旧世代自体にこれらのオブジェクトを収容できる残容量が残っていることが前提であり、実際にメモリリカバリが完了した段階で、全体でどれだけのオブジェクトが生き残るかは明確には分からないので、各リカバリで旧世代に昇格したオブジェクトの容量の値の平均的な大きさを経験値として、旧世代の残容量と比較して、GCを実行するかどうかを決定する必要があります。フルGCを実行して旧世代の空き容量を増やすかどうかを決定します。
平均値を取って比較することは、実際にはまだ動的な確率論的装置であり、平均値よりもはるかに高いオブジェクトの急激な増加でマイナーGCが生き残る場合、それはまだ保証失敗になることを意味します。HandlePromotionFailureの失敗があった場合、失敗の後にFull GCを再度開始する必要があります。保証が失敗したときに円が最も大きくなりますが、ほとんどのケースでは、FullGCが頻繁に発生するのを避けるために、HandlePromotionFailureスイッチをオンにしたままにしています。