著者は最近、Java のガベージ コレクション メカニズムに関する多くの問題に遭遇したため、Java のガベージ コレクションとは何かを共有するために特別にブログを書きました。いわゆるガベージ コレクションとは、JVM がオブジェクトが存在する必要がないと考えて削除したとしても、問題が発生することを意味します。
オブジェクトをリサイクルする必要があるかどうかを判断するにはどうすればよいですか?
典型的なガベージ コレクション アルゴリズムはどのようにオブジェクトをリサイクルするのでしょうか?
典型的なガベージ コレクターとは何ですか?
問題を 1 つずつ見てみましょう
ここで、まず次の質問を理解します。オブジェクトが「ゴミ」であると判断された場合はどうなるでしょうか?ガベージ コレクターの仕事は、ガベージ オブジェクトによって占有されているスペースを新しいオブジェクトで使用できるように再利用することであるため、ガベージ コレクターはオブジェクトが「ガベージ」であることをどのように判断するのでしょうか。 ――つまり、リサイクルできるかどうかをどうやって判断するかということです。一部のオブジェクトは JVM メモリを使い果たしているため、メモリ空間をクリーンアップする必要があり、次のラウンドでリサイクルする必要があるオブジェクトはクリーンアップされます。
Javaでは、オブジェクトは参照を通じて相互に関連付けられます。つまり、オブジェクトを操作したい場合は、参照を通じて操作する必要があります。したがって、明らかに簡単な方法は、参照カウントを使用してオブジェクトがリサイクルできるかどうかを判断することです。一般性を失うことなく、オブジェクトに関連付けられた参照がない場合、そのオブジェクトが他の場所で使用される可能性は基本的に低いことを意味し、そのオブジェクトはリサイクル可能なオブジェクトになります。この方法は参照カウントと呼ばれます。
この方法は単純かつ粗雑ですが、非常に効率的です。効率が高くなると、必然的にいくつかの問題が発生します。一部のオブジェクトに循環参照がある場合、そのオブジェクトを null に割り当てたとしても、このアルゴリズムは再利用できません。以下のコードを見てください
public class GcTest {public Object object = null; public static void main(String[] args) { GcTest gcTest1 = new GcTest(); GcTest gcTest2 = new GcTest(); gcTest1.object = gcTest1; gcTest2.object = gcTest2; gcTest1 = null; gcTest2 = null; } }
gcTest1 と gcTest2 は null ですが、それらが指すオブジェクトにはアクセスできなくなりますが、相互に参照しているため、参照カウントが 0 ではないため、ガベージ コレクションが行われます。決してリサイクルしません。
上記の問題が明らかになりました。jvm がこの問題をどのように解決するかを見てみましょう。この問題を解決するために、Javaでは到達可能性解析手法が採用されています。このメソッドの基本的な考え方は、一連の「GC ルート」オブジェクトを開始点として検索することです。ただし、「GC ルート」とオブジェクトの間に到達可能なパスがない場合、そのオブジェクトは到達不能であると言われます。到達不可能と判断されたオブジェクトが必ずしもリサイクル可能なオブジェクトになるわけではないことに注意してください。到達不可能と判断されたオブジェクトは、リサイクル可能なオブジェクトになるために少なくとも 2 つのマーキング プロセスを経る必要があり、これら 2 つのマーキング プロセス中に脱出してリサイクル可能なオブジェクトになる可能性がまだない場合、そのオブジェクトは基本的にリサイクル可能なオブジェクトになります。 。 「JVM の徹底理解」では、著者が GC Roots の概念を簡単に紹介しています。詳しく知りたい場合は、著者が紹介した本を読んでください。
オブジェクトがリサイクルできるかどうかを判断するために、次の 3 種類のオブジェクトが jvm の GC ルートとして使用されます (通常、必要なのは仮想マシン スタックと静的参照だけです)
1. 仮想マシン スタック (JVM スタック)参照されるオブジェクト (正確には、仮想マシン スタック内のスタック フレーム (フレーム))。各メソッドが実行されると、jvm が対応するスタック フレームを作成することがわかります (スタック フレームには、オペランド スタック、ローカル変数テーブル、および実行時定数プールへの参照が含まれます)。スタック フレームには、メソッド内で使用されるすべての情報が含まれます。オブジェクトの参照 (およびもちろん他の基本型データ)。この方法で、スタック フレームが仮想マシン スタックからポップされます。または、一時的に作成されたオブジェクトの参照は存在しなくなります。これらの一時オブジェクトを指す gc ルートは存在せず、これらのオブジェクトは次の GC 中にリサイクルされます。 2. メソッド領域のクラスの静的属性によって参照されるオブジェクト。静的プロパティはこのタイプ (クラス) のプロパティであり、単独ではどのインスタンスにも属さないため、このプロパティは当然 gc ルートとして機能します。このクラスが存在する限り、この参照が指すオブジェクトも存在します。クラスもリサイクルされますが、これについては後ほど説明します
3. Native Stackで参照されるオブジェクト
以下はソフト参照(softReference)と弱参照(weakReference)のオブジェクトガベージコレクションの紹介です。彼らはやりました
りー
上記のオブジェクト間のリサイクル状況は、Bはメモリ不足の場合にStringオブジェクトをリサイクル可能オブジェクトとして決定し、Cはどのような状況であってもStringオブジェクトをリサイクル可能オブジェクトとして決定します。つまり、ソフト参照はメモリ オーバーフロー (OOM) が発生した場合にリサイクルされますが、弱い参照は、何があっても次のリサイクル ラウンドでリサイクルされます。
通常、jvm はこれらのオブジェクトをリサイクルします
1. 参照を明示的に null に割り当てるか、すでにオブジェクトを指している参照を新しいオブジェクトにポイントします。
2. ローカル参照が指すオブジェクト。
3. 上記の弱参照。
どのガベージがリサイクルできるかを決定した後、ガベージ コレクターはガベージ コレクションを開始する必要がありますが、ガベージ コレクションを効率的に実行する方法という問題が伴います。 Java 仮想マシンの仕様ではガベージ コレクターの実装方法が明確に規定されていないため、さまざまなメーカーの仮想マシンがさまざまな方法でガベージ コレクターを実装できます。そのため、ここではコアについてのみ説明します。いくつかの一般的なガベージ コレクション アルゴリズムのアイデア。
これが最も基本的なガベージコレクションアルゴリズムである理由は、実装が最も簡単であり、考え方が最も単純であるためです。マークスイープ アルゴリズムは、マーク フェーズとクリア フェーズの 2 つのフェーズに分かれています。マーキング フェーズのタスクは、リサイクルする必要があるすべてのオブジェクトにマークを付けることです。クリア フェーズは、マークされたオブジェクトが占めていたスペースを回復することです。この図はインターネットから入手したもので、マーククリア アルゴリズムの処理前後のメモリ分布を示しています。
下のすべての写真はシミュレートされたメモリブロックで、赤は未使用のメモリブロック、灰色はリサイクルされるオブジェクトのメモリブロック、黄色はライブオブジェクトです
リサイクル前
リサイクル後このような操作には欠点があることが簡単にわかります。マークされたオブジェクトがクリアされた後、大量のメモリを占有しているオブジェクトがある場合は、この時点でゴミを再度実行する必要があります。この大きなオブジェクトのためのスペースを確保します。 2. コピーアルゴリズム マークスイープアルゴリズムの欠点を解決するために、コピーアルゴリズムが提案されました。利用可能なメモリを容量に応じて 2 つの同じサイズのブロックに分割し、一度にそのうちの 1 つだけを使用します。このメモリ ブロックが使い果たされたら、残ったオブジェクトを別のブロックにコピーし、使用されているメモリ領域をすぐにクリーンアップすることで、メモリの断片化の問題が発生する可能性が低くなります。 リサイクル前 リサイクル後 コピーアルゴリズムは、事前に一般メモリを空にし、ガベージコレクション中に生き残ったオブジェクトをメモリの残りの半分に移動します。このメモリの移動は大量のメモリを消費します。メモリは断片化していませんが、価格が高すぎます。 3. Mark-Compact (Mark-Compact) アルゴリズム Copying アルゴリズムの欠点を解決し、メモリ空間を最大限に活用するために、Mark-Compact アルゴリズムが提案されました。このアルゴリズムのマーキング フェーズはマーク スイープと同じですが、マーキングの完了後、リサイクル可能なオブジェクトを直接クリーンアップするのではなく、生き残ったオブジェクトを一方の端に移動し、その後、端の境界の外側のメモリをクリーンアップします。具体的なプロセスを以下の図に示します。 リサイクル前 リサイクル後 4. Generational Collection (世代別コレクション) アルゴリズム 世代別コレクション アルゴリズムは、現在、ほとんどの JVM ガベージ コレクター アルゴリズムで使用されています。 。その中心的なアイデアは、オブジェクトのライフサイクルに従ってメモリをいくつかの異なる領域に分割することです。通常の状況では、ヒープ領域は Tenured Generation と Young Generation に分割されます。Old Generation の特徴は、各ガベージ コレクションでリサイクルする必要があるオブジェクトの数が少なく、すべてのオブジェクトをリサイクルする必要がないことです。若い世代の特徴は、ガベージ コレクションごとにリサイクルする必要があるオブジェクトの数が多いため、世代ごとの特性に応じて最適なコレクション アルゴリズムを採用できることです。 System.gc() メソッドを呼び出してリサイクル状況を確認できます。
現在、ほとんどのガベージ コレクターは新世代のコピー アルゴリズムを採用しています。これは、ほとんどのオブジェクトが新世代の各ガベージ コレクションでリサイクルされる必要があるためです。つまり、コピー操作の数は少なくなりますが、実際には 1 ではありません。 1. 新世代の空間は、一般的に、リサイクルの際に、より大きな Eden 空間と 2 つの小さな Survivor 空間に分割されます。 、Eden と Survivor に残っているオブジェクトを別の Survivor スペースにコピーし、Eden と使用したばかりの Survivor スペースをクリーンアップします。
古い世代の特徴は、毎回少数のオブジェクトのみがリサイクルされることであるため、一般的に Mark-Compact アルゴリズムが使用されます。
ヒープ領域の外側には別の世代があることに注意してください。これは永続世代 (Permanet Generation) であり、クラス、定数、メソッドの説明などを保存するために使用されます。永続世代のリサイクルでは、主に、放棄された定数と役に立たないクラスの 2 つの部分がリサイクルされます。
以下は、作者が理解しているようですので、ここに移動して共有します
2.ParNew
3.Parallel Scavenge
4.Parallel Old
5.CMS
6.G1
IV. 概要と補足
一般に、ラージ オブジェクトは古い世代に直接割り当てられます。いわゆるラージ オブジェクトは、大量の連続ストレージ領域を必要とするオブジェクトを指します。最も一般的なラージ オブジェクトは、次のような大規模な配列です。 byte[] data = new byte[4*1024*1024]
これは通常、古い世代に直接ストレージスペースを割り当てます。
もちろん、割り当てルールは 100% 固定されているわけではなく、現在使用されているガベージ コレクターの組み合わせと JVM の関連パラメーターによって異なります。
以上がJava ガベージ コレクション メカニズムとは何ですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。