JVMは、オブジェクトがガベージであるとどのように判断しますか(到達可能性分析アルゴリズム)

 
まえがき:ガベージと判断されたオブジェクトまたはメモリ領域は、ガベージコレクタによって回収されます。では、どのようなオブジェクトやメモリ領域がゴミと判断されるのでしょうか。次に、ジャンク判定の基礎としてよく使用される到達可能性分析アルゴリズム参照カウント方法について説明ますこれらのアルゴリズムは両方とも、ガベージ判定アルゴリズムとしてよく使用されます。以下では、これら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ルートとは何ですか?なぜ彼はオブジェクトが到達可能かどうかを判断するために彼を使用する必要がありますか?
  1. GCルートとは何ですか?
    GCルートは、アクセスルートノードとして使用できるオブジェクトのセットです。一般的なGCルートには
    、仮想マシンスタック(ローカル変数テーブル)で参照される次の①オブジェクトがあります
    ②メソッド領域の静的プロパティによって参照されるオブジェクト(静的変数はJDK8の後のヒープにあります)。
    ③メソッド領域の定数で参照されるオブジェクト(ここでは、実行時定数プールの定数を指します)。
    ④ネイティブメソッドでJNI(ネイティブメソッド)が参照する変数。
    ⑤基本的なデータ型に対応するクラスオブジェクトなど、JVM内の参照。
  2. オブジェクトが到達可能かどうかを判断するためにそれらを使用するのはなぜですか?
    GCルートとして使用できる最初の4つのオブジェクトを分析すると、それらすべてに特性があることがわかります。これらのオブジェクトの参照はすべてヒープメモリの外部に保存されます。ヒープ内にGCルートとしてオブジェクトが存在するだけではないのはなぜですか。ヒープにはインスタンス変数などのオブジェクトしかないため、最後の呼び出し元は最初の4つのオブジェクトである必要があります(ここでは、前に記述したコードを慎重に検討できます)。GCルートとして使用できる4つの一般的なオブジェクトは、実際には最も一般的な参照呼び出しの最も外側の構造です。したがって、ルートノードとして、参照関係を検索するのが妥当です。
4.ジャンクを判断するために、仮想マシンはどのようなアルゴリズムを使用しますか?

最も一般的に使用されている仮想マシンHotSpotは、常に到達可能性分析アルゴリズムを使用してきましたが、なぜHotSpotは参照カウントを使用しないのですか?これら2つのアルゴリズムの長所と短所を分析します。

  1. 参照カウント法
    長所は判断の効率高いこと、短所は一定のメモリを占有すること、そして最も重要な短所は相互参照の問題を解決できないことです。オブジェクトが相互に参照する場合、参照カウンターは少なくとも1です。次のコードに示すように
    objA.instance = objB;
    objB.instance = objA;
    objA = null;
    objB = null;
    
    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になります。また、仮想マシンでリサイクルすることはできません。参照カウントの最大の欠点。以下に示すように:

ここに画像の説明を挿入します

  1. 到達可能性分析アルゴリズム:
    利点は、必ずしも各オブジェクトに参照カウントを格納するための小さなメモリに割り当てられるとは限りません。他のシーン参照を解決するために、光が互いに緩む可能性があります不利な点も参照カウントに関連しており、判断効率は参照カウント方法ほど良くありません
5.参照について話すには、Javaの強い参照、ソフト参照、弱い参照、およびファントム参照(強い参照から弱い参照への左から右への参照)について話す必要があります。

強い、柔らかい、弱い、仮想的な外観の背景:スタックに格納されている参照が別のメモリまたはオブジェクトの開始アドレスを表す場合、その参照を特定のメモリまたはオブジェクトへの参照と呼びます。何もありません。定義は間違っていますが、JDKバージョンの継続的な更新により、この定義はやや狭くなりました。たとえば、これらの比較的味のないオブジェクトに対してこの方法だけを定義するだけでは不十分です。したがって、強い参照、ソフト参照、および弱い参照参照が導入されます。参照、仮想参照などは、さまざまなシナリオでオブジェクト参照を説明するために使用されます。
 

  1. 強力な参照:強力な参照は、最も伝統的な参照定義を指します。これは、Object obj = new Object()などのJavaコードを記述するときに最も一般的に使用される参照割り当てを指します。これは、強力、ソフト、脆弱、および仮想の定義でもあります。前に参照方法を定義します。
    強い参照をいつリサイクルするのですか?
    強い参照はGCルートとして存在できます。参照関係が存在する限り、強い参照はリサイクルされないため、使用しないときはnullに設定してください。

  2. ソフト参照:ソフト参照は、まだ有用であるが必要ではないオブジェクトを記述するために使用されます。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
    
  3. 弱参照:弱参照は、必須ではないオブジェクトを記述するために使用されます。弱参照は、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
    
  4. ファントム参照:ファントム参照とも呼ばれます。ファントム参照は、ファントム参照を定義するために使用されます。getメソッドは常にnullです。同時に、ファントム参照を定義するときにReferenceQueueを渡す必要があります。オブジェクトがクリーンアップされるとき、オブジェクトの参照がキューに入れられます。ファントム参照に関連付けられたオブジェクトは、getメソッドを介してオブジェクトのインスタンスを取得できないため(ソフト参照とウィーク参照の両方が可能です)、ファントム参照に関連付けられたオブジェクトは実際にはすぐにリサイクルされます。ファントム参照宣言の方法は次のとおりです。

    String str = "abc";
    ReferenceQueue rq = new ReferenceQueue();
    PhantomReference<String> pf = new PhantomReference<String>(str,rq);
    System.out.println(pf.get());
    

おすすめ

転載: blog.csdn.net/m0_46897923/article/details/113886487