JVMメモリ割り当てとガベージコレクションアルゴリズムの詳細

1.JVMメモリの割り当てとリカバリ

1.1. 概要

Javaの自動メモリ管理は、主にオブジェクトメモリのリサイクルとオブジェクトメモリの割り当てを目的としています。同時に、Java自動メモリ管理のコア機能は、ヒープメモリ内のオブジェクトの割り当てと回復です。

Javaヒープは、ガベージコレクターによって管理される主要な領域であるため、GCヒープ(ガベージコレクションヒープ)とも呼ばれます。ガベージコレクションの観点から、コレクターは基本的に世代別ガベージコレクションアルゴリズムを使用するようになったため、Javaヒープは次のように細分化することもできます。新世代と旧世代:より詳細:Edenスペース、From Survivor、ToSurvivorスペースなど。さらに分割する目的は、メモリをより適切に再利用するか、メモリをより速く割り当てることです。

ヒープスペースの基本構造:

ここに画像の説明を挿入
上図に示したEdenエリア、From Survivor0( "From")エリア、To Survivor1( "To")エリアはすべて新世代に属し、OldMemoryエリアは旧世代に属します。

ほとんどの場合、オブジェクトは最初にエデンエリアに割り当てられます。新世代のガベージコレクションの後、オブジェクトがまだ生きている場合、オブジェクトはs0またはs1に入り、オブジェクトの経過時間は1増加します(エデンエリア- >オブジェクト後のサバイバーエリア初期年齢は1歳になります。年齢が一定レベル(デフォルトは15歳)になると、老年に昇格します。

このGCの後、Eden領域と「From」領域がクリアされました。このとき、「From」と「To」は役割を交換します。つまり、新しい「To」は最後のGCの前の「From」であり、新しい「From」は最後のGCの前の「To」です。いずれの場合も、Toという名前のSurvivorエリアは空であることが保証されています。マイナーGCは、「宛先」領域がいっぱいになるまでこのプロセスを繰り返します。「宛先」領域がいっぱいになると、すべてのオブジェクトが古い世代に移動されます。特定のガベージコレクションアルゴリズムを以下に紹介します。

 

1.2。ヒープメモリの一般的な割り当て戦略

ここに画像の説明を挿入
画像ソース:JavaGuide1
 
。オブジェクトは最初にeden領域に割り当てられます

現在、主流のガベージコレクターは世代別収集アルゴリズムを採用するため、ヒープメモリを若い世代と古い世代に分けて、各時代の特性に応じて適切なガベージコレクションアルゴリズムを選択する必要があります。

ほとんどの場合、オブジェクトは若い世代のエデンエリアに割り当てられます。エデンエリアに割り当てるのに十分なスペースがない場合、仮想マシンはマイナーGCを開始します。マイナーGcとフルGCの具体的な紹介については以下で説明します。

2.大きな物が直接老後を迎える

ラージオブジェクトは、大量の連続したメモリスペースを必要とするオブジェクトです(文字列、配列など)。

理由:大きなオブジェクトにメモリを割り当てる際のコピーによる効率低下の影響を回避するため。

3.長寿命のオブジェクトは老後に入ります

仮想マシンは、メモリを管理するために世代別収集の概念を採用しているため、メモリ収集中に若い世代に配置する必要があるオブジェクトと古い世代に配置する必要があるオブジェクトを識別できる必要があります。これを行うために、仮想マシンは各オブジェクトにオブジェクトの年齢(Age)カウンターを提供します。

オブジェクトがエデンで生まれ、最初のマイナーGCを生き残り、サバイバーが収容できる場合、オブジェクトはサバイバースペースに移動され、オブジェクトの年齢は1に設定されます。オブジェクトがマイナーGCを生き残るたびにサバイバー、年齢は1歳増加し、一定の年齢(デフォルトは15歳)に達すると、老齢に昇格します。

1.3.GCの概要

HotSpot VMの実装では、実際には2種類のGCしかありません。

1.部分収集(部分GC):

  • 若い世代のコレクション(マイナーGC /若いGC):ガベージコレクションは若い世代でのみ実行されます。
  • 旧世代のコレクション(メジャーGC /旧GC):旧世代ではガベージコレクションのみが実行されます。メジャーGCは、コンテキストによってはヒープコレクション全体を参照するためにも使用されることに注意してください。
  • 混合コレクション(混合GC):若い世代全体と古い世代の一部のガベージコレクション。

2.ヒープ全体のコレクション(フルGC):

Javaヒープとメソッド領域全体を収集します。
 
 

2.オブジェクトの死を判断する方法

ほとんどすべてのオブジェクトインスタンスがヒープに配置されます。ヒープでガベージコレクションを行う前の最初のステップは、どのオブジェクトが停止しているか(つまり、使用できなくなったオブジェクト)を特定することです。

2.1。参照カウント

オブジェクトに参照カウンターを追加します。参照がある場合は常にカウンターが1ずつ増加し、参照が無効な場合はカウンターが1ずつ減少します。カウンターが0のオブジェクトはいつでも使用できません。

この方法は実装が簡単で効率的ですが、現在の主流の仮想マシンはメモリを管理するためにこのアルゴリズムを選択していません。主な理由は、オブジェクト間の循環参照の問題を解決することが難しいためです。

オブジェクト間の循環参照の問題:

オブジェクトobjAとobjBが相互に参照することを除いて、2つのオブジェクト間に参照はありません。ただし、それらは相互に参照しているため、それらの参照カウンターは0ではありません。したがって、参照カウントアルゴリズムは、GCコレクターにそれらを再利用するように通知できません。
 

2.2。到達可能性分析アルゴリズム

このアルゴリズムの基本的な考え方は、「GCルート」と呼ばれる一連のオブジェクトを開始点として使用し、これらのノードから開始して下方向に検索することです。ノードが通過するパスは参照チェーンと呼ばれます。オブジェクトがGCルートに到達すると、参照チェーンはありません。接続されている場合は、オブジェクトが使用できないことを証明します。

ここに画像の説明を挿入
GCルートとして使用できるオブジェクトは次のとおりです。

  • 仮想マシンスタックで参照されるオブジェクト(スタックフレーム内のローカル変数テーブル)
  • ネイティブメソッドスタックで参照されるオブジェクト(ネイティブメソッド)
  • メソッド領域のクラス静的プロパティによって参照されるオブジェクト
  • メソッド領域の定数によって参照されるオブジェクト
     

2.3。定数が廃止された定数であるかどうかを判断する方法

実行時定数プールは、主に廃止された定数を再利用します。では、定数が放棄された定数であるとどのように判断するのでしょうか。

文字列「abc」が文字列定数プールに存在する場合、現在文字列定数を参照しているStringオブジェクトがない場合は、定数「abc」が廃止された定数であることを意味します。この時点でメモリの再利用が発生し、必要な場合は、「abc」 "は、システムによって定数プールからクリアされます。
 

2.4。クラスの判断方法は役に立たない

メソッド領域は主に役に立たないクラスを再利用するので、クラスを判断する方法は役に立たないのでしょうか?

定数が「非推奨の定数」であるかどうかを判断するのは比較的簡単ですが、クラスが「役に立たないクラス」であるかどうかを判断するための条件は比較的厳しいものです。クラスが「役に立たないクラス」と見なされるには、次の3つの条件を満たす必要があります。

  • このクラスのすべてのインスタンスがリサイクルされています。つまり、Javaヒープにこのクラスのインスタンスはありません。
  • このクラスをロードしたClassLoaderはリサイクルされています。
  • このクラスに対応するjava.lang.Classオブジェクトはどこにも参照されておらず、このクラスのメソッドにはどこでもリフレクションを介してアクセスできません。

仮想マシンは、上記の3つの条件を満たす役に立たないクラスをリサイクルできます。ここで言うのは「はい」だけであり、オブジェクトのように使用されない場合、必ずしも再生されるとは限りません。

 
 

3.見積もり

参照カウント法でオブジェクト参照数を判断する場合でも、到達可能性分析法でオブジェクトの参照チェーンに到達可能かどうかを判断する場合でも、オブジェクトの存続を判断することは「参照」に関係します。

JDK1.2より前は、Javaでの参照の定義は非常に伝統的でした。参照型データに格納された値が別のメモリブロックの開始アドレスを表す場合、このメモリブロックは参照を表すと言われます。

JDK1.2以降、Javaは参照の概念を拡張し、参照を4つのタイプに分割しました。強い参照、ソフト参照、弱い参照、およびファントム参照(参照の強度は徐々に弱くなっています)

3.1。強い参照(StrongReference)

以前に使用した参照のほとんどは、実際には強力な参照であり、最も一般的に使用される参照です。オブジェクトに強い参照がある場合、それは重要な家庭用品に似ており、ガベージコレクターはそれをリサイクルしません。メモリスペースが不足している場合、Java仮想マシンはOutOfMemoryErrorエラーをスローしてプログラムを異常終了させ、強力な参照を持つオブジェクトを自由に再利用してメモリ不足の問題を解決しません。

3.2。ソフトリファレンス(SoftReference)

オブジェクトにソフト参照しかない場合、それは不可欠な日用品に似ています。メモリスペースが十分である場合、ガベージコレクタはそれを再利用しません。メモリスペースが不十分である場合、ガベージコレクタはこれらのオブジェクトのメモリを再利用します。ガベージコレクターがそれを再利用しない限り、オブジェクトはプログラムで使用できます。ソフト参照を使用して、メモリに依存するキャッシュを実装できます。

ソフト参照は、参照キュー(ReferenceQueue)と組み合わせて使用​​できます。ソフト参照によって参照されるオブジェクトがガベージコレクションされている場合、Java仮想マシンはそれに関連付けられた参照キューにソフト参照を追加します。

3.3。弱参照(WeakReference)

オブジェクトの参照が弱い場合は、不要な商品に似ています。

弱参照とソフト参照の違いは次のとおりです。

参照が弱いオブジェクトは、ライフサイクルが短くなります。ガベージコレクタスレッドがその管轄下のメモリ領域をスキャンするプロセスでは、弱い参照のみを持つオブジェクトが見つかると、現在のメモリスペースが十分であるかどうかに関係なく、そのメモリが再利用されます。ただし、ガベージコレクターは優先度の低いスレッドであるため、参照が弱いオブジェクトはすぐに見つからない場合があります。

弱参照は、参照キュー(ReferenceQueue)と組み合わせて使用​​できます。弱参照によって参照されるオブジェクトがガベージコレクションである場合、Java仮想マシンはそれに関連付けられた参照キューに弱参照を追加します。

3.4。ファントムリファレンス(PhantomReference)

名前が示すように、「ファントム参照」は仮想参照に他なりません。他のタイプの参照とは異なり、仮想参照はオブジェクトのライフサイクルを決定しません。オブジェクトがファントム参照のみを保持している場合、それは参照がない場合と同じであり、いつでもガベージコレクションされる可能性があります。

ファントム参照は、主にガベージコレクションされているオブジェクトのアクティビティを追跡するために使用されます。

仮想参照とソフト参照および弱参照の違いの1つは、次のとおりです

ファントム参照は、参照キュー(ReferenceQueue)と組み合わせて使用​​する必要があります。ガベージコレクターがオブジェクトを再利用しようとしているときに、ファントム参照があることがわかった場合、オブジェクトのメモリを再利用する前に、関連する参照キューにファントム参照を追加します。プログラムは、仮想参照が参照キューに追加されているかどうかを判断することにより、参照オブジェクトがガベージコレクションされるかどうかを判断できます。プログラムは、ファントム参照が参照キューに追加されていることを検出すると、参照されているオブジェクトのメモリが再利用される前に、必要なアクションを実行できます。

弱参照とファントム参照がプログラム設計で使用されることはめったになく、ソフト参照が使用されることが多いという事実に特に注意してください。これは、ソフト参照がJVMによるガベージメモリの回復を高速化し、安全性を維持できるためです。システム、およびメモリオーバーフロー(OutOfMemory)およびその他の問題を防止します。
 
 

4.ガベージコレクションアルゴリズム

4.1。コピーアルゴリズム

コピーアルゴリズムは、メモリを同じサイズの2つのブロックに分割します。GCの開始時には、オブジェクトはEden領域とFrom領域にのみ存在し、To領域は空です。

次にGCを実行すると、エデンエリアに残っているすべてのオブジェクトが宛先エリアにコピーされ、差出人エリアでは、生き残っているオブジェクトが年齢に基づいてどこに行くかを決定します。特定の年齢に達したオブジェクトは古い世代に移動され、しきい値に達していないオブジェクトは[宛先]領域にコピーされます。

このGCの後、Eden領域とFrom領域がクリアされます。このとき、FromエリアとToエリアが入れ替わり、Toエリアが空になります。

次に、GCは、「宛先」領域がいっぱいになるまでこのプロセスを繰り返し、すべてのオブジェクトが古い世代に移動されます。

利点:断片化がない

短所:メモリスペースの浪費、スペースの半分以上が空である、極端な場合、オブジェクトの100%が生き残る、複雑な操作と移動

ここに画像の説明を挿入
 

4.2。マークスイープアルゴリズム

アルゴリズムは、マーキング段階とクリア段階に分けられます。

まず、リサイクルが必要なすべてのオブジェクトにマークを付け、マークが完了した後、マークされたすべてのオブジェクトを均一に収集します。

利点:追加のスペースは必要ありません

短所:2回のスキャンには時間がかかり、メモリの断片化が発生します。

ここに画像の説明を挿入
 

4.3.Mark-compress-sweepアルゴリズム

マーキングプロセスはマークスイープアルゴリズムと同じですが、後続の手順では、リサイクル可能なオブジェクトを直接再生するのではなく、残っているすべてのオブジェクトを一方の端に移動してから、端の境界の外側のメモリを直接クリーンアップします。メモリの断片化を防ぎます。

ここに画像の説明を挿入

改善:圧縮する前に数回マークとクリアを行うことができます
 

4.4。世代別収集アルゴリズム

新世代では、収集されるたびに多数のオブジェクトが停止するため、レプリケーションアルゴリズムを選択でき、わずかなオブジェクトレプリケーションコストで各ガベージコレクションを完了することができます。老後のオブジェクトの存続の可能性は比較的高く、オブジェクトを割り当てるための余分なスペースがないため、ガベージコレクションには「mark-sweep」または「mark-compress-sweep」アルゴリズムを選択する必要があります。
 

4.5。まとめ

  • メモリ効率:(時間計算量)

コピーアルゴリズム>マーク除去アルゴリズム>マーク圧縮アルゴリズム

  • 記憶のすっきり:

コピーアルゴリズム=マーク削除アルゴリズム>マーク圧縮アルゴリズム

  • メモリ使用率:

マーカー圧縮アルゴリズム=マーカー除去アルゴリズム>コピーアルゴリズム
 
 

おすすめ

転載: blog.csdn.net/weixin_43901865/article/details/112801006