まえがき:ガベージと判断されたオブジェクトまたはメモリ領域は、ガベージコレクタによって回収されます。では、どのようなオブジェクトやメモリ領域がゴミと判断されるのでしょうか。次に、ジャンク判定の基礎としてよく使用される到達可能性分析アルゴリズムと参照カウント方法について説明します。これらのアルゴリズムは両方とも、ガベージ判定アルゴリズムとしてよく使用されます。以下では、これら2つのアルゴリズムについて説明します。
1.参照カウント方法
オブジェクトに参照カウンターを追加します。オブジェクトがオブジェクトを参照している場合は、参照カウンターが1つ増え、参照が無効な場合は、参照カウンターが1つ減ります。参照カウンターの値がゼロの場合は、オブジェクトがガベージになることを意味します。
2.到達可能性分析アルゴリズム
「GCルート」と呼ばれる一連のノードセットから、参照関係に従って下向きに検索します。検索プロセスが通過するパスは、参照チェーン(参照チェーン)と呼ばれます。オブジェクトとGCルートの間に参照チェーンがない場合。 、またはグラフ理論を使用して、GCルートが到達不能なオブジェクトに到達すると、オブジェクトが到達不能であることを証明しますが、現時点では、オブジェクトがガベージであると判断することはできず、オブジェクトは到達不能であるとしか言えません。オブジェクトをガベージとして判別するには、次の手順を実行します。
上の図に示すように①:最初のステップは、到達可能性分析アルゴリズムを使用して、GCルートとオブジェクトの間に参照チェーンがあるかどうかを判別することです。参照チェーンがない場合、オブジェクトは最初のTimesマークになります。
図②に示すように、2番目のステップは、マークされたオブジェクトがfinalize()メソッドをオーバーライドしたかどうかを判別することです。メソッドがオーバーライドされて実行されたか、メソッドがオーバーライドされていない場合、オブジェクトはデッドであると判断されます。それ以外の場合、オブジェクトはデッドであると判断されます。はFに入れられます-キューキューでは、JVMは後でF-キューキュー内の各オブジェクトのfinalize()メソッドを実行するために、低いスケジューリング優先度でFinalizeスレッドを開始します。
図③に示すように、3番目のステップは、各オブジェクトのfinalize()メソッドが実行されるときです。これは、各オブジェクトが自己救済を完了し、ガベージと判断されない最後のチャンスです。現在のオブジェクトが参照を正常に確立した場合finalize()メソッドチェーンにGCルートを使用します。生きていると判断され、それ以外の場合は2回目のマークが付けられます。2回マークされたオブジェクトは死んだと判断されたオブジェクトであり、次のガベージコレクション時にガベージコレクターによってリサイクルされます。
3.確かに、ここでの質問のほとんどはこの質問ですか?GCルートとは何ですか?なぜ彼はオブジェクトが到達可能かどうかを判断するために彼を使用する必要がありますか?
- GCルートとは何ですか?
GCルートは、アクセスルートノードとして使用できるオブジェクトのセットです。一般的なGCルートには
、仮想マシンスタック(ローカル変数テーブル)で参照される次の①オブジェクトがあります。
②メソッド領域の静的プロパティによって参照されるオブジェクト(静的変数はJDK8の後のヒープにあります)。
③メソッド領域の定数で参照されるオブジェクト(ここでは、実行時定数プールの定数を指します)。
④ネイティブメソッドでJNI(ネイティブメソッド)が参照する変数。
⑤基本的なデータ型に対応するクラスオブジェクトなど、JVM内の参照。 - オブジェクトが到達可能かどうかを判断するためにそれらを使用するのはなぜですか?
GCルートとして使用できる最初の4つのオブジェクトを分析すると、それらすべてに特性があることがわかります。これらのオブジェクトの参照はすべてヒープメモリの外部に保存されます。ヒープ内にGCルートとしてオブジェクトが存在するだけではないのはなぜですか。ヒープにはインスタンス変数などのオブジェクトしかないため、最後の呼び出し元は最初の4つのオブジェクトである必要があります(ここでは、前に記述したコードを慎重に検討できます)。GCルートとして使用できる4つの一般的なオブジェクトは、実際には最も一般的な参照呼び出しの最も外側の構造です。したがって、ルートノードとして、参照関係を検索するのが妥当です。
4.ジャンクを判断するために、仮想マシンはどのようなアルゴリズムを使用しますか?
最も一般的に使用されている仮想マシンHotSpotは、常に到達可能性分析アルゴリズムを使用してきましたが、なぜHotSpotは参照カウントを使用しないのですか?これら2つのアルゴリズムの長所と短所を分析します。
- 参照カウント法:
長所は判断の効率が高いこと、短所は一定のメモリを占有すること、そして最も重要な短所は相互参照の問題を解決できないことです。オブジェクトが相互に参照する場合、参照カウンターは少なくとも1です。次のコードに示すように
objA = null; objB = null;を使用すると、将来、objAとobjBへの参照が1つあります。objA.instanceの失敗には最初にobjAの失敗が必要であり、objAの失敗にはobjB.instanceの失敗が必要であるため(objB.instanceはobjAを指しているため)、これら2つの参照は、参照カウント方法を使用して削除することはできません。 objB.instanceの失敗にはobjBが必要です。無効化、objBの無効化は最初にobjA.instanceを無効化する必要があるため(objA.instanceはobjBを指しているため)、無限ループに陥り、objAとobjBの参照カウンターは常に1になります。また、仮想マシンでリサイクルすることはできません。参照カウントの最大の欠点。以下に示すように:objA.instance = objB; objB.instance = objA; objA = null; objB = null;
- 到達可能性分析アルゴリズム:
利点は、必ずしも各オブジェクトに参照カウントを格納するための小さなメモリに割り当てられるとは限りません。他のシーンへの参照を解決するために、光が互いに緩む可能性があります。不利な点も参照カウントに関連しており、判断効率は参照カウント方法ほど良くありません。
5.参照について話すには、Javaの強い参照、ソフト参照、弱い参照、およびファントム参照(強い参照から弱い参照への左から右への参照)について話す必要があります。
強い、柔らかい、弱い、仮想的な外観の背景:スタックに格納されている参照が別のメモリまたはオブジェクトの開始アドレスを表す場合、その参照を特定のメモリまたはオブジェクトへの参照と呼びます。何もありません。定義は間違っていますが、JDKバージョンの継続的な更新により、この定義はやや狭くなりました。たとえば、これらの比較的味のないオブジェクトに対してこの方法だけを定義するだけでは不十分です。したがって、強い参照、ソフト参照、および弱い参照参照が導入されます。参照、仮想参照などは、さまざまなシナリオでオブジェクト参照を説明するために使用されます。
-
強力な参照:強力な参照は、最も伝統的な参照定義を指します。これは、Object obj = new Object()などのJavaコードを記述するときに最も一般的に使用される参照割り当てを指します。これは、強力、ソフト、脆弱、および仮想の定義でもあります。前に参照方法を定義します。
強い参照をいつリサイクルするのですか?
強い参照はGCルートとして存在できます。参照関係が存在する限り、強い参照はリサイクルされないため、使用しないときはnullに設定してください。 -
ソフト参照:ソフト参照は、まだ有用であるが必要ではないオブジェクトを記述するために使用されます。SoftReferenceを使用してオブジェクトを変更します。getメソッドを使用して、弱参照によって参照されるオブジェクトを取得します。定義は次のとおりです。
Object obj = new Object(); String str = "abc"; SoftReference<String> sf = new SoftReference<String>(str); String strRefe = sf.get(); System.out.println(strRefe); 输出结果: abc Process finished with exit code 0
ソフト参照をいつリサイクルするのですか?
メモリが不足している場合、jvmはソフト参照をクリーンアップしようとしますが、ソフト参照オブジェクトを再利用できない場合があります。JVMは、OOMしようとしているときに、ヒープ内のソフト参照オブジェクトをクリーンアップしようとします。クリアすることはできませんが、それでもOOMが報告されます。「メモリがオーバーフローする前に、jvmはソフト参照によって参照されるオブジェクトをクリアします」というこのステートメントを見た人は、これが間違っていることを知っておいてください。この間違ったステートメントを確認しましょう(多くの人が言うので)、コードは次のとおりです。import java.lang.ref.SoftReference; import java.util.ArrayList; import java.util.List; public class TestHeapOom { static class OOMObject{ String name; public OOMObject(String name){ this.name = name; } } // 虚拟机配置:-Xmx10M -Xms10M public static void main(String[] args){ int n =0; List<OOMObject> list = new ArrayList<OOMObject>(); SoftReference<List<OOMObject>> sr = new SoftReference<List<OOMObject>>(list); System.out.println(sr.get().size()); while (true){ list.add(new OOMObject("a"+n)); System.out.println(sr.get().size()); } } }
上記のプログラムは、リストをソフト参照に関連付けてから、オブジェクトをリストに継続的に追加するループを作成します。この場合、10Mヒープメモリはすぐに使い果たされます。通常の状況では、メモリがオーバーフローすると、レポートが表示されます。 OOM:Javaヒープスペース、ここに出力を以下に示します。
106280 106281 106282 106283 106284 106285 Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded at java.lang.AbstractStringBuilder.<init>(AbstractStringBuilder.java:68) at java.lang.StringBuilder.<init>(StringBuilder.java:89) at sgcc.supplier.pojo.model.queues.TestHeapOom.main(TestHeapOom.java:23) Process finished with exit code 1
明らかに、OOM:Javaヒープスペースはここではスローされませんが、OOM:GCオーバーヘッド制限を超えました(必ずしも存在する必要はありません)。なぜこれがスローされるのですか?まず、このエラーの意味を説明します。このエラーは、CPUが98%の時間メモリを再利用しているが、再利用されたメモリはまだ非常に小さく、システムをサポートするには不十分であり、システムがクラッシュしようとしていることを意味します。この種の問題が報告されます。ソフト参照を導入するときに、メモリがオーバーフローする前にソフト参照がリサイクルされようとすることが説明されています。上記の例ではメモリがオーバーフローしそうなので、jvmはソフトを回復しようとしています。参照リストですが、リストはまだです到達可能な状態を再利用することはできませんが、システムはソフト参照を試行し続け、次のエラーが発生します。OOM:GCオーバーヘッド制限を超えました。したがって、「jvmはメモリがオーバーフローする前にソフト参照によって参照されるオブジェクトをクリアする」と言うのは誤りです。ソフト参照オブジェクトを返すための最終的な基礎は到達可能性分析アルゴリズムです。Jvmはメモリがタイト。オブジェクト。別の言い方をすれば、「jvmは、メモリがオーバーフローする前にソフト参照によってのみ参照されるオブジェクトをクリアする」というのは正しいことです。確認コードは次のとおりです。
import java.lang.ref.SoftReference; import java.util.ArrayList; import java.util.List; public class TestHeapOom { static class OOMObject{ String name; public OOMObject(String name){ this.name = name; } } // 虚拟机配置:-Xmx10M -Xms10M -XX:+UseSerialGC public static void main(String[] args){ int n =0; //List<OOMObject> list = new ArrayList<OOMObject>(); SoftReference<List<OOMObject>> sr = new SoftReference<List<OOMObject>>(new ArrayList<OOMObject>()); while (true){ sr.get().add(new OOMObject("a"+n)); System.out.println(sr.get().size()); } } }
上記のプログラムから、新しく作成されたArrayListオブジェクトはソフト参照のみに関連付けられており、ソフト参照以外の参照の関連付けはないことが簡単にわかります。次のログは操作の最後に生成され、レポートはnullポインタなので、ガベージコレクションにあります。この時点で、ソフト参照にのみ関連付けられているオブジェクトがクリアされます。さらに、ソフト参照にのみ関連付けられているオブジェクトが空になる理由を別の観点から説明できます。オブジェクトがガベージであるかどうかを判断するための基礎は到達可能性分析アルゴリズムであり、到達可能性分析アルゴリズムではGC間に参照がある必要があります。ルートとオブジェクト。チェーンは、オブジェクトが到達可能であることを意味するだけであり、ソフト参照をGCルートとして使用することはできません。
132782 132783 132784 132785 132786 132787 132788 132789 132790 132791 132792 132793 Exception in thread "main" java.lang.NullPointerException at sgcc.supplier.pojo.model.queues.TestHeapOom.main(TestHeapOom.java:22) Process finished with exit code 1
-
弱参照:弱参照は、必須ではないオブジェクトを記述するために使用されます。弱参照は、Javaで弱参照を記述するために使用され、弱参照によって参照されるオブジェクトは、getメソッドを使用して取得できます。弱参照の使用は次のとおりです。
String str = "abc"; WeakReference<String> wf = new WeakReference<String>(str); String strRew = wf.get(); System.out.println(strRew);
弱参照をいつリサイクルするのですか?
システムは、弱く参照されたオブジェクトを次のガベージコレクションでリサイクルしようとします。オブジェクトが到達不能の場合は再利用されます。オブジェクトが到達可能の場合は再利用されません。検証コードは次のとおりです。import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; public class TestHeapOomWeak { static class OOMObject{ String name; public OOMObject(String name){ this.name = name; } } // 虚拟机配置:-Xmx10M -Xms10M public static void main(String[] args){ int n =0; List<OOMObject> list = new ArrayList<OOMObject>(); WeakReference<List<OOMObject>> wr = new WeakReference<List<OOMObject>>(list); System.out.println(wr.get().size()); while (true){ list.add(new OOMObject("a"+n)); System.out.println(wr.get().size()); } } }
出力結果は次のとおりです。システムがガベージコレクションを実行していてクラッシュしそうですが、参照の有効期限が切れていないため、ガベージコレクション中に弱い参照が収集されるとは限りません(ここでの説明は多くの人がいるためです) )は、次のガベージコレクションに収集することができるだろうと言うの弱参照オブジェクトの再利用ではないための究極の根拠は、到達可能性解析アルゴリズムである。JVMが唯一の次のGCでの弱参照を再利用しようとします。
106273 106274 106275 106276 106277 106278 106279 106280 106281 Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded at java.lang.Integer.toString(Integer.java:401) at java.lang.String.valueOf(String.java:3099) at java.io.PrintStream.print(PrintStream.java:597) at java.io.PrintStream.println(PrintStream.java:736) at sgcc.supplier.pojo.model.queues.TestHeapOomWeak.main(TestHeapOomWeak.java:25) Process finished with exit code 1
ただし、オブジェクトが弱参照によってのみ参照されている場合、そのオブジェクトは次のガベージコレクションで再利用されます。確認コードは次のとおりです。
import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; public class TestHeapOomWeak { static class OOMObject{ String name; public OOMObject(String name){ this.name = name; } } public static void main(String[] args){ int n =0; //List<OOMObject> list = new ArrayList<OOMObject>(); WeakReference<List<OOMObject>> wr = new WeakReference<List<OOMObject>>(new ArrayList<OOMObject>()); System.out.println(wr.get().size()); while (true){ wr.get().add(new OOMObject("a"+n)); System.out.println(wr.get().size()); } } }
以下に示すように、出力はOOMではなくnullポインターになります。これは、GCの後、弱参照に関連付けられたオブジェクトのみが再利用され、後のオブジェクトが弱参照を通過してnullになり、nullポインターが報告されるためです。
28532 28533 28534 28535 Exception in thread "main" java.lang.NullPointerException at sgcc.supplier.pojo.model.queues.TestHeapOomWeak.main(TestHeapOomWeak.java:24) Process finished with exit code 1
-
ファントム参照:ファントム参照とも呼ばれます。ファントム参照は、ファントム参照を定義するために使用されます。getメソッドは常にnullです。同時に、ファントム参照を定義するときにReferenceQueueを渡す必要があります。オブジェクトがクリーンアップされるとき、オブジェクトの参照がキューに入れられます。ファントム参照に関連付けられたオブジェクトは、getメソッドを介してオブジェクトのインスタンスを取得できないため(ソフト参照とウィーク参照の両方が可能です)、ファントム参照に関連付けられたオブジェクトは実際にはすぐにリサイクルされます。ファントム参照宣言の方法は次のとおりです。
String str = "abc"; ReferenceQueue rq = new ReferenceQueue(); PhantomReference<String> pf = new PhantomReference<String>(str,rq); System.out.println(pf.get());