blog

10.ガベージ・コレクション[概要、関連アルゴリズム].

メモリ内のゴミを適時に掃除しないと、ゴミオブジェクトが占有していたメモリ空間はアプリケーションの終了まで保持され、保持された空間は他のオブジェクトが使用することができません。この空間は他のオブジェクト...

Sep 19, 2020 · 17 min. read
シェア

JVMの概要

ゴミとは

  • ゴミとは、実行中のプログラム中にそのオブジェクトへのポインターを持たないオブジェクトのことで、このオブジェクトはリサイクルが必要なゴミです。
  • メモリ上のゴミを時間内に片付けないと、これらのゴミオブジェクトが占有していたメモリ領域がアプリケーションの終了まで残り、予約領域は他のオブジェクトが使用できなくなります。これはメモリ・オーバーフローにつながる可能性さえあります。

ゴミ収集は、Java言語の仲間ではありません。1960年、最初のLisp言語が誕生し、動的メモリ割り当てとゴミ収集技術を使い始めた頃、ゴミ収集に関する3つの古典的な疑問がありました:

  • 取り戻すべき記憶とは?
  • リコールはいつですか?
  • どのようにリサイクルされているのですか?

ゴミ収集メカニズムは、開発効率を大幅に向上させるJavaの特徴的な機能です。今日、ゴミ収集はモダンな言語のほぼ標準的な機能です。

GCが必要な理由

  • 高レベル言語の基本的な理解として、ゴミを集めなければ、遅かれ早かれメモリ不足に陥るということがあります。
  • 無駄なオブジェクトを解放するだけでなく、ゴミ収集はメモリからレコードの断片化を取り除きます。デフラグは、JVMが解放されたメモリを新しいオブジェクトに割り当てることができるように、占有されているヒープ・メモリをヒープの一端に移動させます。
  • アプリケーションが対応するビジネスがより大規模で複雑になり、ユーザー数が増えるにつれて、 GC 2011 なしでアプリケーションを継続する方法はありません。その結果、STWのGCが実際の需要に追いつけなくなることがよくあり、GCを最適化する試みが絶え間なく行われているのはそのためです

C/C++の黎明期、ゴミ収集は基本的に......手作業でした。手作業でした。開発者は、メモリ要求には new キーワードを、メモリ解放には delete キーワードを使うことができました。

この方法では、メモリ解放のタイミングを柔軟に制御できますが、頻繁にメモリを要求・解放するため、開発者に管理負担がかかります。プログラマーのコーディング上の問題により、メモリ間隔を再要求し忘れた場合、メモリリークが発生し、ごみオブジェクトは決して削除することができず、システムのランタイムが大きくなるにつれて、ごみオブジェクトによって消費されるメモリ量は、メモリオーバーフローが発生し、アプリケーションがクラッシュするまで増加し続ける可能性があります。

現在、Javaだけでなく、C#、Python、Rubyなどの言語でも自動ゴミ収集の考え方が使われるようになり、今後の開発トレンドにもなっています。このような自動メモリ割り当てとゴミ収集は、現代の開発言語にとって必要な標準になったと言えるでしょう。

Javaガベージコレクションの仕組み

  • 自動メモリー管理により、開発者がメモリーの割り当てや再利用に手動で参加する必要がなくなり、メモリー・リークやメモリー・オーバーフローのリスクを低減します。
  • 自動メモリ管理メカニズムにより、プログラマは重いメモリ管理から解放され、ビジネス開発に専念することができます。
  • Java開発者にとって、自動化されたメモリー管理はブラックボックスのようなもので、「自動化」に過度に依存すると災難に見舞われ、最悪の場合、Java開発者がプログラムのメモリー・オーバーフローを発見し、問題を解決する能力を弱めてしまう可能性があります。

提案

  • JVMの自動メモリ割り当てとメモリ再利用の原則を理解することは非常に重要であり、JVMがどのようにメモリを管理しているかを正しく理解して初めて、OutOfMemoryErrorに遭遇したときに、エラー・ログに基づいて問題を素早く見つけ出し、解決することができるようになります。
  • これらの "自動化された "技術は、様々なメモリオーバーフローやリークを検出する必要があるとき、また、より高い同時実行性を達成するためにゴミ収集がボトルネックになるときに、監視し、調整する必要があります。
  • ゴミ収集業者は、若い世代だけでなく、古い世代、さらにはフルヒープとメソッド領域、Javaヒープがゴミ収集業者の努力の焦点であってもリサイクルすることができます
  • ヤングゾーンの頻繁な回収、オールドゾーンの回収頻度の低下、パーマゾーンの移動の少なさ

リターマーキング段階

ゴミ表示の概要

対象生存判定

  • ヒープには、ほとんどすべてのJavaオブジェクトのインスタンスが格納されており、GCがゴミ回収を行う際には、まずメモリ上に生き残っているオブジェクトと死んでいるオブジェクトを区別する必要があります。GCがゴミ回収を行う際には、死んだとマークされたオブジェクトだけがGCによって解放されるので、このプロセスはゴミのタグ付け段階と呼ぶことができます。
  • では、JVMの中でデッド・オブジェクトはどのようにマークされるのでしょうか?簡単に言うと、オブジェクトが死んだと宣言されるのは、そのオブジェクトが生き残っているどのオブジェクトからも参照されなくなったときです。
  • オブジェクトの生存を決定する一般的な方法には、参照カウント・アルゴリズムと到達可能性解析アルゴリズムの2つがあります。

参照カウント

  • 参照カウントアルゴリズムは比較的単純で、各オブジェクトに整数の参照カウンタ属性があります。これは、オブジェクト
  • オブジェクトAに対して、Aを参照しているオブジェクトがある限り、Aの参照カウンタは1だけ増加し、参照が無効になると、参照カウンタは1だけ減少します。オブジェクトAの参照カウンタの値が0である限り、オブジェクトAはそれ以上使用される可能性がなく、リサイクルできることを意味します。
  • 利点:簡単な導入、ゴミの識別が容易、効率的な判定、リサイクルの遅延なし
  • 欠点:
    • カウンターを格納するために別のフィールドを必要とするため、ストレージスペースのオーバーヘッドが増加。
    • カウンタは代入のたびに更新される必要があり、加算や減算の演算を伴います。
    • 参照カウンタには、循環参照を扱えないという深刻な問題があります。これは致命的な欠陥であり、Javaのゴミ・コレクターにそのようなアルゴリズムがないことにつながっています。

コードデモ

javaが参照カウントを使っていないことの証明

/**
 * -XX:+PrintGCDetails
 * 証明:Javaは参照カウント・アルゴリズムを使っていない。
 */
public class RefCountGC {
 //このメンバ・プロパティがすることは、メモリを少し消費することだけである。
 private byte[] bigSize = new byte[5 * ];//5MB
 Object reference = null;
 public static void main(String[] args) {
 RefCountGC obj1 = new RefCountGC();
 RefCountGC obj2 = new RefCountGC();
 obj1.reference = obj2;
 obj2.reference = obj1;
 obj1 = null;
 obj2 = null;
 //ゴミ収集動作を明示的に実行する
 //ここでGCが発生した場合、obj1とobj2は取り戻せるだろうか?
 System.gc();
 try {
 Thread.sleep();
 } catch (InterruptedException e) {
 e.printStackTrace();
 }
 }
}

0bj1-referenceと0bj2-referenceを注意深くnullに設定しないと、Javaヒープ内の2つのメモリは互いに参照されたままとなり、再要求できなくなります。

短い

  • 参照カウントアルゴリズムは、Pythonのような多くの言語で選択されているリソース回収です。Pythonは人工知能のおかげでさらに人気があり、参照カウントとゴミ回収の両方のメカニズムをサポートしています。
  • 正確にはどちらが最適かはシナリオによりますが、業界ではスループットを向上させるために、大規模な練習では基準計数メカニズムだけを保持しようとする試みがあります。
  • Javaが参照カウントを選ばなかったのは、参照カウントの根本的な難しさ、つまり循環的な参照関係を扱うのが難しいからです。
  • Python は循環参照をどのように解決するのですか?
    • 手動の再参照: 適切なタイミングで再参照することはよく知られています。
    • Pythonの標準ライブラリであるweakrefを使って循環参照を解決します。

アクセシビリティ分析のアルゴリズム

  • 到達可能性分析は、Java、C#のオプションであり、トレーサビリティのゴミ収集とも呼ばれます。
  • ルート・オブジェクトから始まり、ルート・オブジェクト・コレクションによって接続されたターゲット・オブジェクトが到達可能かどうかを上から下へ検索します。
  • 到達可能性分析アルゴリズムを使用した後、メモリに残っているオブジェクトは、ルートオブジェクトの集合によって直接または間接的に接続されており、検索によって移動した経路は参照チェーンと呼ばれます。
  • ターゲットオブジェクトが参照チェーンで接続されていない場合、それは到達不可能です。
  • 到達可能性分析アルゴリズムでは、ルート・オブジェクト・セットによって直接または間接的に接続できるオブジェクトだけが生き残るオブジェクトです

GCのルーツ

  • 個々のスレッドから呼び出されるメソッドで使用されるパラメータやローカル変数など、仮想マシン・スタックで参照されるオブジェクト。
  • ローカル・メソッド・スタックでJNIが参照するオブジェクト
  • メソッド領域のクラス静的属性によって参照されるオブジェクト
    • 例:Javaクラスの参照型静的変数
  • メソッド領域の定数によって参照されるオブジェクト
    • 文字列定数プールでの参照
  • 同期ロックによって保持されているすべてのオブジェクト。
  • Java仮想マシン内の参照
    • 基本データ型に対応するクラス・オブジェクト、nullpointerException、OOMerrorなどのいくつかの常駐例外オブジェクト、システム・クラス・ローダー
  • Java仮想マシンJMXBean、JVMTI登録コールバック、ローカルコードキャッシュなどの内部状況を反映します。
  • GCRootsの固定セットに加えて、GC Rootsのフルセットを構成するために "一時的に "追加することができる他のオブジェクトがあり、ユーザーによって選択されたごみコレクターと現在再生されているメモリの領域に応じて。たとえば、世代およびローカルの再生利用
    • Java ヒープのある領域だけがゴミとして回収される場合、そのメモリ領域は仮想マシン自身の実装の詳細であり、隔離されて閉じていないことはもちろん、この領域のオブジェクトが他の領域のオブジェクトから参照される可能性があることを考慮しなければなりません。この領域のオブジェクトは、他の領域のオブジェクトによって参照される可能性があり、関連する領域のオブジェクトは、到達可能性分析の精度を確保するために、GC Rootsのセットに追加する必要があります。
  • ヒント: ルートは変数やポインタを格納するためにスタックを使用するため、ヒープ・メモリ内のオブジェクトを保持するポインタがヒープ・メモリ自体に格納されていない場合、それはルートです。

特筆

  • 到達可能性解析アルゴリズムを使ってメモリが回復可能かどうかを判断する場合、一貫性が保証されたスナップショットで解析を実行する必要があります。そうでない場合、結果の正確性は保証されません。
  • これは、GCが "StopTheWorld "しなければならない主な理由の1つです。"StopTheWorld "は、停止しないと言われているCMSコレクタでも、ルートノードを列挙するときに停止しなければなりません。

オブジェクトの最終化メカニズム

  • Java言語では、オブジェクトの破棄を処理するためのカスタム・ロジックを開発者が提供できるように、オブジェクトの終了ファイナライズ・メカニズムを提供しています。
  • あるオブジェクトへの参照がないとわかったとき、つまり、そのオブジェクトをゴミとして回収するとき、ゴミコレクターは必ず最初にそのオブジェクトのfinalize()メソッドを呼び出します。
  • finalize() メソッドは、サブクラスでオーバーライドすることができます。通常は、ファイルやソケット、データベース接続のクローズなど、リソースの解放とクリーンアップがこのメソッドで行われます。
  • オブジェクトに対してfinalize()メソッドを呼ばないでください。

オブジェクトは" "

  • finalize()メソッドのおかげで、VM内のオブジェクトは一般的に以下の3つの状態になります
  • オブジェクトがすべてのルート・ノードからアクセスできない場合、そのオブジェクトはもう使用されていません。一般的に言えば、このオブジェクトはリサイクルする必要があります。しかし実際には、「死んでいる」のではなく、一時的に「保護観察中」なのです。アンタッチャブルなオブジェクトは、特定の条件下で「復活」する可能性があり、その場合はリサイクルするのは妥当ではありません。 このため、仮想マシン内のオブジェクトには3つの状態が考えられます。以下の3つです:
    • 到達可能: オブジェクトにルート・ノードから到達可能。
    • Revivable: オブジェクトへの参照はすべて解放されますが、finalize() でオブジェクトを復活させることは可能です。
    • Untouchable: オブジェクトの finalize は 1 回のみ呼び出されます。オブジェクトに到達できなくなったときにのみ、オブジェクトをリサイクルできます。

リサイクル可能性を判断するための具体的なプロセス

オブジェクトObjAがリサイクル可能かどうかを判断するには、少なくとも2つのマーキング処理が必要です。

  • 1.オブジェクトからGCRootsへの参照チェーンがない場合、最初のマーキングが実行されます。
  • 2.このオブジェクトに対してfinalize()メソッドを実行する必要があるかどうかのフィルタリングを行います。
    • オブジェクトAがfinalizeメソッドを書き換えていない場合、またはfinalizeメソッドが仮想マシンから呼び出されている場合、仮想マシンは実行不要と判断し、オブジェクトAはアンタッチャブルと判断します
    • オブジェクトAがfinalizeメソッドを上書きして実行した場合
    • finalizeメソッドは、オブジェクトが死を免れるための最後のチャンスであり、後でGCは2回目のためにFキューキューでオブジェクトをマークし、Aはfinalizeメソッドの参照チェーンのいずれかのオブジェクトとの接続を確立した場合、2回目のマーキングでAは差し迫った回復コレクションから削除されます。その後、オブジェクトは参照が存在しない状態で再び現れ、finalizeメソッドは再び呼び出されず、オブジェクトは直接到達不可能になります。
/**
 * Objectクラスのfinalize()メソッド、つまりオブジェクトの最終化メカニズムをテストする。
 *
 */
public class CanReliveObj {
 public static CanReliveObj obj;//GCルートに属するクラス変数
 //このメソッドは一度しか呼び出せない
 @Override
 protected void finalize() throws Throwable {
 super.finalize();
 System.out.println("現在のクラスでオーバーライドされているfinalize()メソッドを呼び出す");
 obj = this;//現在取り戻されているオブジェクトは、finalize()メソッドで参照チェーンのオブジェクトobjにリンクされている。
 }
 public static void main(String[] args) {
 try {
 obj = new CanReliveObj();
 // オブジェクトが初めて自己保存に成功する
 obj = null;
 System.gc();//ガベージ・コレクターを呼び出す
 System.out.println("第1GC");
 // ファイナライザー・スレッドの優先順位は低いので、2秒間一時停止して待機させる
 Thread.sleep(2000);
 if (obj == null) {
 System.out.println("obj is dead");
 } else {
 System.out.println("obj is still alive");
 }
 System.out.println("第2GC");
 // 次のコードは上のコードと同じだが、今回は自分自身の保存に失敗している。
 obj = null;
 System.gc();
 // ファイナライザー・スレッドの優先順位は低いので、2秒間一時停止して待機させる
 Thread.sleep(2000);
 if (obj == null) {
 System.out.println("obj is dead");
 } else {
 System.out.println("obj is still alive");
 }
 } catch (InterruptedException e) {
 e.printStackTrace();
 }
 }
}

コンソール出力

第1GC
現在のクラスでオーバーライドされているfinalize()メソッドを呼び出す
obj is still alive
第2GC
obj is dead

ダンプファイルの取得

  • 方法1: コマンドラインでjmapを使う
    • jps
    • jmap -dump:format=b,live,file=test1.bin {プロキシID}
  • 方法2:JVisualVMを使用してエクスポートします。

GC Roots

public class GCRootsTest {
 public static void main(String[] args) {
 List<Object> numList = new ArrayList<>();
 Date birth = new Date();
 for (int i = 0; i < 100; i++) {
 numList.add(String.valueOf(i));
 try {
 Thread.sleep(10);
 } catch (InterruptedException e) {
 e.printStackTrace();
 }
 }
 System.out.println("データが追加されたので操作してほしい:");
 new Scanner(System.in).next();
 numList = null;
 birth = null;
 System.out.println("numList誕生が空に設定されているので、操作してほしい:");
 new Scanner(System.in).next();
 System.out.println(" ");
 }
}

撤去段階

メモリ内の生きているオブジェクトと死んでいるオブジェクトの区別に成功した後、 GC 2011 の次のタスクは、新しいオブジェクトのためにメモリを割り当てるのに十分な空きメモリ スペースがあるように、役に立たないオブジェクトによって占有されているメモリ スペースを解放するためにゴミ収集を実行することです。JVMで最も一般的な3つのゴミ収集アルゴリズムは、マーク・アンド・クリーン、コピー、マーク・アンド・コンパクトです。

マーク・ワン・クリア・アルゴリズム

mark-one-cleanアルゴリズムは、1960年にJ . McCarthyらによって1960年に提案され、Lisp言語に適用されました。

実行プロセス

  • ヒープ内の有効メモリ空間がなくなると、プログラム全体が停止し、2つのタスクが実行されます。
  • タグ付け:コレクターは、参照ルートノードからトラバースし、すべての参照オブジェクトにタグを付けます。通常、オブジェクトの Header に到達可能として記録されます。
  • クリア:コレクターはヒープメモリを端から端まで直線的に走査し、ヘッダーに到達可能としてマークされていないオブジェクトを見つけると、それをリサイクルします。

欠点

  • あまり効率的ではありません。
  • GCを実行する場合、アプリケーション全体を停止する必要があり、ユーザーエクスペリエンスが低下します。
  • この方法でクリーンアップされた空きメモリーは連続性がなく、メモリーの断片化を引き起こします。空きリスト
  • 注:ここでのいわゆるクリーンアップは実際には空ではなく、クリーンアップが必要なオブジェクトのアドレスが空きアドレスリストに格納されます。次に新しいオブジェクトをロードする必要があるとき、ゴミの場所に十分なスペースがあるかどうかを判断し、もしあれば、それを

レプリケーション・アルゴリズム

M.L.Minskyは、マークアンドイレーズアルゴリズムの欠点であるゴミ収集効率を解決するために、1963年に有名な論文「Li sp言語による二重記憶装置を用いたゴミ収集アルゴリズム(CALISP Garbage Collector Algorithm Using Dual Storage)」を発表しました。この論文でM.L.Minskyが説明したアルゴリズムは複製アルゴリズムとして知られており、M.L.Minsky自身もLisp言語の実装版で導入に成功しています。

核となる考え方

生きているメモリ空間を2つのブロックに分け、片方ずつしか使用せず、ゴミ回収時に、使用中のメモリにある生きているオブジェクトをコピーします。使用中のメモリ内の生活オブジェクトは未使用のメモリブロックにコピーされ、その後、使用中のメモリブロック内のオブジェクトはすべてクリアされ、2つのメモリの役割が交換され、最後にゴミ収集が完了します

JVMの利点

  • マーキングや清算プロセスが不要で、導入が簡単で効率的な運用が可能
  • コピー後のスペースの連続性は保証されており、「断片化」の問題はありません。

欠点

  • このアルゴリズムの欠点も明らかです。
  • 多数のリージョンに分割されるG1のようなGCでは、移動ではなくコピーは、リージョン間のオブジェクト参照を維持する必要があることを意味し、これはメモリ使用量や時間の点でも小さなオーバーヘッドではありません。
  • レプリケーション・アルゴリズムが機能するためには、レプリケーション・アルゴリズムがレプリケートする必要のある生存オブジェクトの数が大きすぎたり、非常に少なかったりしないためです。

マーキング圧縮アルゴリズム

複製アルゴリズムの効率は、生存オブジェクトが少なく、ゴミオブジェクトが多いという前提に基づいています。これは新しい世代ではよくあることですが、古い世代ではほとんどのオブジェクトが生存オブジェクトであることが一般的です。このままレプリケーション・アルゴリズムを使用すると、生存オブジェクトが多いため、レプリケーションのコストが高くなります。

マーク・アンド・ワン・クリーン・アルゴリズムは確かに昔は使えたのですが、実行効率が悪いだけでなく、メモリー再生後にメモリーの断片化を引き起こしていたので、JVMの設計者はそれを改善する必要がありました。マーク・アンド・コンパクト・アルゴリズムが誕生したのです。

核となる考え方

  • 最初の段階は、マーク・ワン・クリーン・アルゴリズムと同じで、ルート・ノードから始まるすべての参照オブジェクトをマークします。
  • 第2段階は、生き残ったすべてのオブジェクトをメモリの一端に圧縮し、順番に排出します。
  • その後、境界の外側のスペースをすべて取り除きます。
  • 最終的な結果は、マーカークリアリングアルゴリズムの実行が終了した後に、別のメモリデフラグを行うのと同じです。
  • 移動しないアルゴリズムであるマーク除去アルゴリズムとは本質的に異なり、マーク圧縮は移動します。
  • この2つの本質的な違いは、mark-one-clearアルゴリズムは移動しないリサイクルアルゴリズムであり、mark-one-compress .Shrinkは移動します。回収された生存オブジェクトを移動させるかどうかは、メリットとデメリットの両方があるリスキーな決定です。
  • ご覧のように、タグ付けされた生存オブジェクトはメモリー・アドレス順に整理され、一方、マークされていないメモリーはクリーンアップされます。このようにして、新しいオブジェクトのためにメモリーを割り当てるとき、JVMはメモリーの開始アドレスを保持するだけでよくなります。

ポインタの衝突メモリ空間が規則正しく整然と分布している場合、つまり、使用済みメモリと未使用メモリがそれぞれの側にあり、次の割り当ての開始点を記録するタグ付きポインタがあり、新しいオブジェクトのためにメモリを割り当てるとき、新しいオブジェクトを最初の空きメモリ位置に割り当てるためにポインタのオフセットを修正するだけでよい場合、このタイプの割り当てはポインタの衝突と呼ばれます。

JVMの利点

  • マーク・アンド・クリーン・アルゴリズムでメモリ領域が散在する欠点をなくし、JVMは新しいオブジェクトのためにメモリを割り当てるときに開始アドレスを保持するだけでよいようにします。
  • コピー・アルゴリズムにおけるメモリ半減の高コストを排除

欠点

  • 効率の点では.効率という点では、マーク・ア・コレート・アルゴリズムはレプリケーション・アルゴリズムよりも低い。
  • オブジェクトの移動中、そのオブジェクトが他のオブジェクトから参照されている場合、参照のアドレスも調整する必要があります。移動中はユーザー・アプリケーションを一時停止する必要があります。つまり、STW

概要

  • レプリケーション・アルゴリズムは、効率という点では正当なボスですが、メモリを浪費しすぎます。
  • また、上記の3つの指標のバランスを取るために、マーククリーンアルゴリズムは比較的スムーズですが、効率はそれほど良くありません。コピーアルゴリズムよりもマーキングフェーズが1つ多く、マーククリアアルゴリズムよりもメモリクリーニングフェーズが1つ多くなります。

分割収集アルゴリズム

最良のアルゴリズムはなく、より適切なアルゴリズムがあるだけです。

世代別収集アルゴリズムは、異なる物体は異なるライフサイクルを持つという事実に基づいています。したがって、ライフサイクルの異なるオブジェクトは、リサイクル効率を向上させるために、異なる方法で収集することができます。一般的に、Javaヒープは新しい世代と古い世代に分けられ、ゴミ収集の効率を向上させるために、各世代の特性に応じて異なる収集アルゴリズムを使用することができます。

HotSpotでは、世代分割の概念に基づき、GCが使用するメモリ再生アルゴリズムは、若い世代と古い世代のそれぞれの特性を組み合わせる必要があります。

  • 若い世代
    • 若い世代の特徴:古い世代に比べて領域が比較的小さく、オブジェクトのライフサイクルが短く、生存率が低く、リサイクルが頻繁。
    • この場合、複製アルゴリズムがリサイクルを選別する最も速い方法です。複製アルゴリズムの効率は現在生きているオブジェクトのサイズにしか関係しないので、若い世代の回収に非常に適しています。レプリケーション・アルゴリズムのメモリ使用率が低いという問題は、ホットスポットに2つの生存者を設計することで緩和されます。
  • 古い世代
    • 古い世代の特徴:領域が大きい、オブジェクトのライフサイクルが長い、生存率が高い、若い世代よりもリサイクルの頻度が低い
    • この場合、生存率の高いオブジェクトが多数存在し、複製アルゴリズムは明らかに不適切になります。これは通常、マークアンドクリーン、またはマークアンドクリーンとマークアンドソートのミックスによって実装されます。

HotSpotのCMSリサイクラを例にとると、CMSはMark-Sweep実装に基づいており、オブジェクトのリサイクルに非常に効率的です。断片化の問題に対して、CMSはMark-Compactアルゴリズムに基づくSerial old recyclerを補償手段として採用しています。メモリの回収がうまくいかない場合、Serial 0ldを使用してFull GCを実行し、古いメモリの整理を達成します。

インクリメンタル・コレクション・アルゴリズム、パーティショニング・アルゴリズム

インクリメンタル収集アルゴリズム

基本的な考え方一度にすべてのゴミを取り除くとシステムのダウンタイムが長くなる場合は、ゴミ収集スレッドとアプリケーションスレッドを交互に実行します。毎回、ゴミ収集スレッドはメモリ空間の小さな領域だけを収集し、アプリケーションスレッドに切り替えます。これをゴミ収集が完了するまで繰り返します。

一般的に、インクリメンタル収集アルゴリズムは、従来のマーク・ア・クリーンとコピーのアルゴリズムに基づいています。インクリメンタル収集アルゴリズムは、スレッド間の競合を適切に処理することで、ゴミ収集スレッドが段階的にマーク、パージ、コピーを完了できるようにします。

デメリット:この方法を使用すると、ごみ収集プロセス中にアプリケーションコードも断続的に実行されるため、システムのダウンタイムが短くなります。しかし、スレッドの切り替えやコンテキストの切り替えが発生するため、ゴミ収集の全体的なコストが上昇し、システムのスループットが低下する可能性があります。

分割アルゴリズム

  • GCで発生するダウンタイムを抑制するために、大きなメモリ領域を小さなブロックに分割し、ヒープ空間全体ではなく、ターゲットのダウンタイムに基づいて、一度にいくつかの小さな区間を合理的に回収することで、1回のGCで発生する時間を削減します。
  • 生成アルゴリズムはオブジェクトをライフサイクルの長さに応じて2つの部分に分割し、分割アルゴリズムはヒープ全体を連続的に異なるサブ区間に分割します。
  • 各インターバルは独立して使用され、独立してリサイクルされます。このアルゴリズムの利点は、一度にリサイクルされるインターバルの数を制御できることです。

これらはあくまで基本的なアルゴリズム上の考え方であり、実際のGCの実装プロセスはもっと複雑で、現在も開発中の最先端のGCは複合アルゴリズムであり、並列性と並行性の両方を備えていることに注意してください。



























Read next

JS関数のタイミング

理由:forループが実行されると、ループ全体が完了し、この時点でiの値が6であるため、6 "アラーム "を設定するため、印刷枚数は6です。

Sep 19, 2020 · 1 min read