JVM_16 のパート 1_ガベージ コレクション関連の概念_シリコン バレー

1 System.gc() の理解

Systen.gc() メソッドの理解

  • デフォルトでは、System.gc() または Runtime.getRuntime().gc() の呼び出しは明示的にフル GC をトリガーし、古い世代と新しい世代を同時にリサイクルし、破棄された世代によって占有されているメモリを解放しようとします。物体。
  • ただし、 System.gc() 呼び出しには免除の有効期間があり、ガベージ コレクターの呼び出しが保証されません。
  • JVM 実装者は、System.gc() 呼び出しを通じて JVM の GC 動作を決定できます。通常の状況では、ガベージ コレクションは手動でトリガーせずに自動的に実行する必要があります。そうしないと面倒ですパフォーマンス ベンチマークを書いているような特殊なケースでは、実行前に System.gc() を呼び出すことができます。

demo01テストシステム01()

public class SystemGCTest {
    
    
    public static void main(String[] args) {
    
    
        new SystemGCTest();
        // 提醒jvm的来及回收器执行gc,但是不确定是否马上执行
        System.gc();

        // 强制调用失去引用对象的finalize()
//        System.runFinalization();
    }

    @Override
    protected void finalize() throws Throwable {
    
    
        super.finalize();
        System.out.println("SystemGCTest 重写了finalize()");
    }
}

demo02 テスト リカバリ

public class LocalVarGC {
    
    
    /**
     * 不会回收
     */
    public void localvarGC1() {
    
    
        byte[] buffer = new byte[10 * 1024 * 1024];
        System.gc();
    }


    /**
     * 会回收
     */
    public void localvarGC2() {
    
    
        byte[] buffer = new byte[10 * 1024 * 1024];
        buffer = null;
        System.gc();
    }

    /**
     * 不会回收
     * 局部变量表中的slot未被覆盖
     *
     */
    public void localvarGC3() {
    
    
        {
    
    
            byte[] buffer = new byte[10 * 1024 * 1024];
        }
        System.gc();
    }

    /**
     * 可以回收
     * value复用了buffer的槽,可以被回收
     */
    public void localvarGC4() {
    
    
        {
    
    
            byte[] buffer = new byte[10 * 1024 * 1024];
        }
        int value = 10;
        System.gc();
    }

    /**
     * 可以回收
     */
    public void localvarGC5() {
    
    
        localvarGC1();
        System.gc();
    }

    public static void main(String[] args) {
    
    
        LocalVarGC localVarGC = new LocalVarGC();
        localVarGC.localvarGC5();
    }
}

2 メモリオーバーフローとメモリリーク

メモリ不足 (OOM)

  • メモリ リークと比較すると、メモリ オーバーフローは非常に単純に理解できますが、同様に、メモリ オーバーフローもプログラム クラッシュを引き起こす原因の 1 つです。

  • GC が発達しているため、一般的には、アプリケーションが占有するメモリが急速に増大し、メモリ消費の速度にガベージ コレクションが追いつかない限り、OOM は発生しにくくなります。

  • ほとんどの場合、GC はさまざまな年齢層のガベージ コレクションを実行します. 本当に不十分な場合は、サイズを大きくして排他的なフル GC 操作を実行します. このとき、大量のメモリが回復され、継続的に使用されます.アプリケーションで使用します。

  • javadoc の OutOfMemoryError の説明は、空きメモリがなく、ガベージ コレクタがそれ以上のメモリを提供できないというものです。

  • まず、空きメモリがない状況について説明しましょう。これは、Java 仮想マシンのヒープ メモリが十分でないことを意味します。2 つの理由があります。

(1) Java仮想マシンのヒープメモリ設定が不足している。

例: メモリー・リークの問題がある可能性があり、ヒープのサイズが不当である可能性が非常に高い.例えば、比較的客観的な量のデータを処理する必要があるが、JVM ヒープ・サイズが明示的に指定されていない、または指定した値が小さすぎます。パラメータ -Xmx、Xmx で調整できます。

(2) コード内に大量のラージオブジェクトを作成し、長時間ガベージコレクタで回収できない (参考文献あり)

古いバージョンの Oracle JDK では、永続世代のサイズが制限されており、JVMU は永続世代のガベージ コレクション (定数プールのリサイクル、不要になった型の書き込みなど) に対して非常に非アクティブであるため、引き続き新しいタイプの追加 場合によっては、永続的な世代の OutOfMemoryError も非常に一般的であり、特に実行時に生成される動的なタイプが多数ある場合、同様のインターン文字列キャッシュがあまりにも多くのスペースを占有し、OOM の問題を引き起こす可能性もあります。対応する例外情報がマークされ、永久世代に関連付けられます: " java.lang.OutOfMemoryError: PermGen space "。

メタデータ領域の導入により、メソッドがメモリに移動することがそれほど恥ずかしくなくなったため、対応する OOM が変更されました.OOM が発生した場合、例外情報は「 java.lang.OutOfMemoryError: Metaspace 」になります不十分な直接メモリも OOM を引き起こします

  • 通常、OutOfMemoryError をスローする間にガベージ コレクターがトリガーされ、スペースをクリーンアップするために最善を尽くすという暗黙の意味があります。
    • たとえば、参照メカニズムの分析では、JVM がソフト参照によってポイントされたオブジェクトをリサイクルしようとすることが関係しています。
    • java.nio.Bits.reserveMemory() メソッドでは、領域をクリーンアップするために System.gc() が呼び出されることが明確にわかります。
  • もちろん、すべての場合にガベージ コレクターがトリガーされるわけではありません。
    • たとえば、ヒープの最大値を超える超大規模データと同様に、超大規模オブジェクトを割り当てた場合、JVM はガベージ コレクションではこの問題を解決できないと判断できるため、直接 OutOfMemoryError をスローします。

メモリーリーク

「ストレージリーク」とも呼ばれます。厳密に言えば、オブジェクトがプログラムによって使用されなくなったが、GC がオブジェクトを再利用できない場合のみ、メモリ リークと呼ばれます。

しかし、実際には、タイミングの悪さ (または怠慢) によって、オブジェクトの寿命が非常に長くなったり、OOM が発生したりすることがあり、これは広義の「メモリ リーク」とも呼ばれます

可能な限り、メモリ リークが発生してもプログラムがすぐにポンプアップすることはありませんが、メモリ リークが発生すると、プログラムで使用可能なメモリが徐々に侵食され、直接メモリが使い果たされ、最終的に OutOfMemory 例外が発生して、クラッシュするプログラム。

ここでのストレージ容量は、物理メモリの値ではなく、ディスクスワップ領域に設定されたサイズに依存する仮想メモリのサイズであることに注意してください。

画像-20220904230645166

例:

1.シングルトンモード

シングルトンのライフサイクルはアプリケーションと同じくらい長いため、外部オブジェクトへの参照がシングルトンプログラムで保持されていると、外部オブジェクトを再利用できなくなり、メモリリークが多発します。

2. close を提供する一部のリソースが閉じられていないため、メモリ リークが発生する

データベース接続 (dataSource.getConnection())、ネットワーク接続 (ソケット)、io 接続は手動で閉じる必要があります。そうしないと、再利用できません。

3 ストップ・ザ・ワールド

ストップ・ザ・ワールドの説明

  • Stop the world (略して STW) は、GC 時間の発生中のアプリケーションの一時停止を指します。一時停止が発生すると、アプリケーション スレッド全体が応答なしで中断されます。これはスタック感に少し似ています。この一時停止は STW と呼ばれます。

    • 到達可能性分析アルゴリズムでルート ノード (GC ルート) を列挙すると、すべての Java 実行スレッドが停止します。
      • 分析作業は一貫したスナップショットで実行する必要があります
      • 一貫性とは、実行システム全体が分析全体の特定の時点で停止しているように見えることを意味します
      • 分析プロセス中にオブジェクト参照関係が変化している場合、分析結果の正確性は保証されません。
  • STW によって中断されたアプリケーション スレッドは、GC の完了後に再開されます. 頻繁な中断は、ユーザーがネットワーク速度の低下によってムービーがスタックしたように感じられるため、STW の発生を減らす必要があります.

  • STW イベントは、どの GC が使用されているかとは関係がなく、すべての GC にこの時間があります。

  • G1でもストップ・ザ・ワールドの状況を完全に回避することはできません.ガベージコレクターはますます良くなり、リサイクル効率はますます高くなり、一時停止時間は可能な限り短縮されているとしか言えません.

  • STW は、バックグラウンドで JVM によって自動的に開始および完了されますユーザーが表示されていない場合、ユーザーの通常の作業スレッドはすべて停止します。

  • 開発中に System.gc() を使用しないと、Stop-the-world が発生します。

テスト STW デモ

public class StopTheWorldDemo {
    
    
  public static class WorkThread extends Thread{
    
    
    List<byte[]> list = new ArrayList<byte[]>();
    @Override
    public void run() {
    
    
      try {
    
    
        while (true) {
    
    
          for (int i = 0; i < 1000; i++) {
    
    
            byte[] buffer = new byte[1024];
            list.add(buffer);
          }
          if (list.size() > 1000) {
    
    
            list.clear();
            System.gc();
          }
        }

      } catch (Exception e) {
    
    
        e.printStackTrace();
      }
    }
  }

  public static class PrintThread extends Thread {
    
    
    public final long startTime = System.currentTimeMillis();
    public int i = 0;

    @Override
    public void run() {
    
    
      try {
    
    
        while (true) {
    
    
          // 每秒打印时间信息
          long t = System.currentTimeMillis() - startTime;
          System.out.println("i = " + (i++) + ",耗时:" + t );
          Thread.sleep(1000);
        }
      } catch (Exception e) {
    
    
        e.printStackTrace();
      }
    }
  }

  public static void main(String[] args) {
    
    
    WorkThread w = new WorkThread();
    PrintThread p = new PrintThread();
    w.start();
    p.start();
  }
}

印刷糸のみを使用する

i = 0,耗时:0
i = 1,耗时:1010
i = 2,耗时:2019
i = 3,耗时:3025
i = 4,耗时:4037
i = 5,耗时:5047
i = 6,耗时:6057
i = 7,耗时:7065
i = 8,耗时:8079
i = 9,耗时:9079
i = 10,耗时:10085

2 つのスレッドが同時に開始されると、時間が出力されます (効果は明ら​​かではありません。テストを変更する必要があります)。

i = 0,耗时:0
i = 1,耗时:1015
i = 2,耗时:2023
i = 3,耗时:3027
i = 4,耗时:4036
i = 5,耗时:5049
i = 6,耗时:6060
i = 7,耗时:7068
i = 8,耗时:8073
i = 9,耗时:9079
i = 10,耗时:10087

4 ガベージ コレクションの並列性と並行性

同時

  • オペレーティング システムでは、いくつかのプログラムが実行され、一定期間内に完了するまで実行され、これらのプログラムはすべて同じプロセッサ上で実行されていることを意味します。
  • コンカレンシーとは、本当の意味での「同時動作」ではなく、CPUが一定の時間をいくつかの時間帯(時間間隔)に分割し、その間を行ったり来たりすることです.CPUの処理速度は非常に速いため、間隔を適切に処理すると、ユーザーは複数のアプリケーションが同時に実行されているように感じることができます。

平行

  • システムに複数の CPU がある場合、1 つの CPU が 1 つのプロセスを実行すると、別の CPU が別のプロセスを実行でき、2 つのプロセスは互いに CPU リソースを占有せず、同時に実行できます。これを並列と呼びます (平行)。
  • 実は並列度を決める要素はCPUの数ではなく、CPUのコア数であり、例えばCPUの複数コアを並列化することもできます。
  • 科学計算やバックグラウンド処理などの弱い相互作用のシナリオに適しています。

並行性と並列性

並行性とは、同時に複数のことが同時に発生することを指します。

並行とは、同時に複数のことが同時に起こることを意味します。

複数の同時タスクは、リソースを互いに先取りします。複数の並列タスクが互いにリソースを先取りすることはありません。

並列処理は、複数の CPU または複数のコアを持つ CPU の場合にのみ発生します。そうしないと、同時に発生しているように見えることが、実際には同時に実行されます。

ガベージ コレクションの同時実行性と並列性

同時実行性と並列性は、ガベージ コレクターについて説明する文脈では、次のように説明できます。

  • 並列 (Parallel) とは、複数のガベージ コレクション スレッドが並行して動作することを指しますが、この時点ではユーザー スレッドはまだ待機状態にあります。
    • ParNew、Parallel Scavenge、Parallel Old など。
  • シリアル
    • 並列処理の概念と比較して、シングルスレッド実行
    • メモリが十分でない場合、プログラムは中断され、JVM ガベージ コレクタがガベージ コレクションのために開始されます。リサイクル後、プログラムのスレッドを再度開始します

同時実行性と並列性は、ガベージ コレクターについて説明する文脈では、次のように解釈できます。

  • コンカレント: ユーザー スレッドとガベージ コレクション スレッドの同時実行 (ただし、必ずしも並列ではなく、交互に実行される可能性があります) を指し、ガベージ コレクション スレッドは実行中にユーザー プログラムの実行を停止しません。
    • ユーザー プログラムは引き続き実行されますが、ガベージ コレクターは別の CPU で実行されます。
    • のような: CMS、G1

5 セーフポイントとセーフエリア

セーフポイント

プログラムを実行すると、すべての場所で GC を停止および開始できるわけではなく、特定の位置でのみ GC を停止および開始することができます。

セーフ ポイントの選択は非常に重要です。小さすぎると、GC の待機時間が長くなり、頻度が高すぎると、ランタイム パフォーマンスの問題が発生する可能性がありますほとんどの実行の実行時間は非常に短く、通常はメソッド呼び出し、ループ ジャンプ、例外ジャンプなどの「プログラムを長時間実行できる特性を備えているかどうか」に基づいています

すべてのスレッドが最も近い安全なポイントまで実行され、GC が発生したときに停止することを確認する方法は?

  • プリエンプティブ割り込み: (現在、仮想マシンは使用されていません)

    すべてのスレッドを中断することをお勧めします。セーフ ポイントにないスレッドがまだある場合は、スレッドを復元し、スレッドをセーフ ポイントまで実行させます。

  • アクティブ割り込み:

    割り込みフラグを設定します。各スレッドがセーフ ポイントまで実行されると、このフラグがアクティブにポーリングされます。割り込みフラグが true の場合、スレッド自体に割り込みがかかり、中断されます。

安全な地域

Safepoint メカニズムは、プログラムが実行されると、GC に短時間で入ることができる Safepoint に遭遇することを保証します。しかし、プログラムが「実行されない」場合はどうでしょうか? たとえば、スレッドがスリープ状態またはブロック状態にある場合、この時点で、スレッドは JVM の割り込み要求に応答できず、安全な領域に「歩いて」割り込み、一時停止します。目覚める。この場合、それを解決するには安全な領域 (Safe Region) が必要です。

安全な領域とは、コード セグメント内でオブジェクトの参照関係が変更されないことを意味し、この領域内であればどこでも GC を開始しても安全です。Safe Region を拡張された SafePoint と見なすこともできます。

実際に実行すると:

  1. When the thread runs to the code of the Safe Region, it has first mark that it has reached the Safe Region. この期間中に GC が発生すると、JVM はそのスレッドを Safe Region 状態のスレッドとしてマークします。
  2. スレッドがセーフ リージョンを離れようとすると、JVM が GC を完了したかどうかをチェックします。完了している場合は実行を継続します。完了していない場合、スレッドは安全にセーフ リージョンを離れることのできるシグナルを受信するまで待機する必要があります。領域;

6 再び参照について話す: 強い参照

このようなオブジェクトのクラスを記述したいと考えています: メモリ空間が十分にある場合、それらはメモリ内に保持できます; ガベージ コレクションの後もメモリ空間がまだ逼迫している場合、これらのオブジェクトは破棄できます。

[部分的かつ非常によくあるインタビューの質問] 強参照、弱参照、弱参照、幻参照の違いは何ですか? 具体的な使用シーンは?

JDK1.2版以降、Javaは参照の概念を拡張し、参照を強参照、弱参照、弱参照、幻参照の4種類に分けましたが、これら4つの引用の強さは次第に弱くなっていきました

強い参照を除いて、他の 3 つの参照は java.lang.ref パッケージにあります。次の図は、これら 3 つの参照型に対応する型を示しており、開発者はそれらをアプリケーションで直接使用できます。

画像-20220912003201532

Reference サブクラスのファイナライザー参照のみがパッケージに表示され、他の 3 つの参照型はパブリックであり、アプリケーションで直接使用できます。

  • 強い参照: 「参照」の最も伝統的な定義は、プログラム コードに一般的に存在する参照割り当て、つまり「Object obj = new Object()」のような参照関係を指します。いずれにせよ、強い参照関係が存在する限り、ガベージ コレクターは参照されたオブジェクトをリサイクルしません。
  • ソフト参照: システムが出荷可能なメモリをオーバーフローする前に、これらのオブジェクトは 2 回目のリサイクルのリサイクルの範囲に含まれます。このリサイクル後に十分なメモリがない場合、メモリ オーバーフロー例外がスローされます。
  • 弱参照: 弱参照に関連付けられたオブジェクトは、次のガベージ コレクションまでしか存続できません。ガベージ コレクターが機能すると、十分なメモリ領域があるかどうかに関係なく、弱参照に関連付けられたオブジェクトが回収されます。
  • ファントム参照: オブジェクトに仮想参照があるかどうかは、その有効期間にまったく影響せず、仮想参照を介してオブジェクトのインスタンスを取得することは不可能です。オブジェクトのファントム参照関連付けを設定する唯一の目的は、オブジェクトが収集されたときにシステム通知を受け取ることです。

強参照 (Strong Reference) はリサイクルされません

Java プログラムでは、最も一般的な参照タイプは強参照 (通常のシステムの 99% 以上が強参照) であり、これは最も一般的なオブジェクト参照であり、デフォルトの参照タイプでもあります。

Java 言語で new 演算子を使用して新しいオブジェクトを作成し、それを変数に割り当てる場合、その変数はオブジェクトへの強い参照と呼ばれます。

強く参照されたオブジェクトは到達可能であり、ガベージ コレクターは参照されたオブジェクトを回収しません。

通常のオブジェクトの場合、他に参照関係がない場合、参照の範囲を超えるか、対応する (強い) 参照割り当てが null である限り、ガベージとして収集できます. もちろん、具体的な回復時間は依存します.ガベージ コレクション戦略。

対照的に、ソフト参照、弱参照、およびファントム参照のオブジェクトは、ソフト、弱、および仮想タッチ可能であり、特定の条件下でリサイクルできます。したがって、強い参照は Java メモリ リークの主な原因の 1 つです。

  • 強参照は、ターゲット オブジェクトに直接アクセスできます。
  • The object point to the strong reference will not be reclaimed by the system at any time. 仮想マシンは、強い参照が指すオブジェクトを再利用するよりも、OOM 例外をスローします。
  • 強い参照はメモリ リークを引き起こす可能性があります

7 再び引用について話す: ソフトサイテーション

メモリが不足すると、ソフト参照 (Soft Reference) がリサイクルされます

ソフト参照は、有用ではあるが必須ではないオブジェクトを記述するために使用されます。ソフト参照のみに関連付けられているオブジェクトは、システムでメモリ オーバーフロー例外が発生する前に、2 回目の回復の回復範囲に含まれます。この回復に十分なメモリがない場合、メモリ オーバーフローが異常スローされます

ソフト参照は、メモリに依存するキャッシュを実装するためによく使用されます。例:キャッシュは再びソフト参照を使用します。空きメモリがまだある場合は、キャッシュを一時的に確保し、メモリが不足しているときにクリーンアップして、キャッシュを使用する同僚がメモリ不足にならないようにすることができます。

ガベージ コレクターは、特定の時点でソフト到達可能オブジェクトを再利用することを決定すると、ソフト参照をクリーンアップし、オプションで参照をキュー (参照キュー) に格納します。

弱参照と同様ですが、Java 仮想マシンはソフト参照をより長い期間存続させようとし、最後の手段としてそれらをクリーンアップします。

JDK1.2 以降では、ソフト参照を実装するために java.lang.ref.SoftReference クラスが提供されています。

Object obj = new Object() // 声明强引用
SoftReference<Object> sf = new SoftReference<Object>(obj);
obj = null; // 取消强引用

ソフト参照コード

public class SoftReferenceTest {
    
    
  public static void main(String[] args) {
    
    
    // 创建对象,建立软引用
//    SoftReference<User> userSoftReference = new SoftReference<>(new User((1), "test"));
    // 上面的一行代码,等价于如下的三行代码
    User user = new User(1, "test");
    SoftReference<User> userSoftReference = new SoftReference<>(user);
    user = null;

    // 从软引用中重新获得强引用对象
    System.out.println(userSoftReference.get());

    System.gc();
    System.out.println("After GC:");
    // 垃圾回收之后获得软引用中的对象
    System.out.println(userSoftReference.get());

    try {
    
    
      byte[] b = new byte[1024 * 6848 - 564 * 1024]; // 根据实际老年代的大小填写
    } catch (Throwable e) {
    
    
      e.printStackTrace();
    } finally {
    
    
      System.out.println(userSoftReference.get());
    }
  }
}
class User{
    
    
  private Integer id;
  private String name;

  public User(Integer id, String name) {
    
    
    this.id = id;
    this.name = name;
  }
}

操作結果:

User@16d3586
[Full GC (System.gc()) [Tenured: 0K->569K(6848K), 0.0016977 secs] 1101K->569K(9920K), [Metaspace: 144K->144K(4480K)], 0.0017364 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
After GC:
User@16d3586
[GC (Allocation Failure) [DefNew: 110K->0K(3072K), 0.0002125 secs][Tenured: 569K->569K(6848K), 0.0006989 secs] 679K->569K(9920K), [Metaspace: 144K->144K(4480K)], 0.0009355 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (Allocation Failure) [Tenured: 569K->550K(6848K), 0.0006424 secs] 569K->550K(9920K), [Metaspace: 144K->144K(4480K)], 0.0006530 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
null
Heap
 def new generation   total 3072K, used 193K [0x05000000, 0x05350000, 0x05350000)
  eden space 2752K,   7% used [0x05000000, 0x050304e0, 0x052b0000)
  from space 320K,   0% used [0x05300000, 0x05300000, 0x05350000)
  to   space 320K,   0% used [0x052b0000, 0x052b0000, 0x05300000)
 tenured generation   total 6848K, used 6834K [0x05350000, 0x05a00000, 0x05a00000)
   the space 6848K,  99% used [0x05350000, 0x059fcbd8, 0x059fcc00, 0x05a00000)
 Metaspace       used 156K, capacity 2280K, committed 2368K, reserved 4480K

占有サイズを次のように変更します: 1024 * 6848 - 570 * 1024

User@16d3586
[Full GC (System.gc()) [Tenured: 0K->548K(6848K), 0.0014516 secs] 1030K->548K(9920K), [Metaspace: 144K->144K(4480K)], 0.0014876 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
After GC:
User@16d3586
User@16d3586
Heap
 def new generation   total 3072K, used 154K [0x05200000, 0x05550000, 0x05550000)
  eden space 2752K,   5% used [0x05200000, 0x05226820, 0x054b0000)
  from space 320K,   0% used [0x054b0000, 0x054b0000, 0x05500000)
  to   space 320K,   0% used [0x05500000, 0x05500000, 0x05550000)
 tenured generation   total 6848K, used 6833K [0x05550000, 0x05c00000, 0x05c00000)
   the space 6848K,  99% used [0x05550000, 0x05bfc708, 0x05bfc800, 0x05c00000)
 Metaspace       used 156K, capacity 2280K, committed 2368K, reserved 4480K

見つかった場合は、ソフト参照されたユーザーのアドレスを出力できます。これは、ソフト参照されたオブジェクトがリサイクルされていないことを示します。

8 再び参照について話す: 弱参照

弱参照 (弱参照) - 発見とリサイクル

弱い参照は、それらの重要でないオブジェクトを記述するためにも使用され、弱い参照にのみ関連付けられているオブジェクトは、次のガベージ コレクションが発生するまでしか存続できませんシステム GC では、弱い参照が検出される限り、システム ヒープ領域が十分かどうかに関係なく、弱いアプリケーションにのみ関連付けられているオブジェクトがリサイクルされます。

ただし、通常、ガベージ コレクタのスレッドの優先度は非常に低いため、脆弱なアプリケーションを保持しているオブジェクトはすぐに見つからない場合があります。この場合、弱く参照されたオブジェクトが長期間存在する可能性があります

弱参照はソフト参照と同じです. 弱参照を構築するとき, 参照キューを指定することもできます. 弱参照オブジェクトがリサイクルされると, 指定された参照キューに追加されます. このキューを介して, オブジェクトの回復は追跡されます。

ソフト参照と弱いインポートは、不要なキャッシュ データの格納に非常に適していますこれにより、システム メモリが不足したときに、メモリ オーバーフローを引き起こすことなく、キャッシュされたデータが再利用されます。また、メモリ リソースが十分にある場合は、これらのキャッシュされたデータが長時間存在する可能性があるため、システムが高速化されます。

java.lang.ref.WeakReference クラスは、弱参照を実装するために JDK1.2 以降で提供されています。

Object obj = new Object(); // 生命强引用
WeakReference<Object> wr = new WeaReference<ObjecL>(obj);
obj = null; // 销毁强引用

弱参照オブジェクトとソフト参照オブジェクトの最大の違いは、 GC がリサイクルしているときに、アルゴリズムを通じてソフト参照オブジェクトがリサイクルされているかどうかを確認する必要があることですが、弱参照オブジェクトの場合、GC は常にリサイクルします。弱く参照されたオブジェクトは、GC によってより簡単かつ迅速に再利用できます。

インタビューの質問: 開発で WeakHashMap を使用したことがありますか?

弱い参照コードの例:

public class WeakReferenceTest {
    
    
  public static void main(String[] args) {
    
    
    // 构造了弱引用
    WeakReference<Object> weakReference = new WeakReference<>(new Object());
    // 从弱引用中重新获取对象
    System.out.println(weakReference.get());

    System.gc();
    // 不管当前内存空间足够与否,都会回收它的内存
    System.out.println("After GC:");
    System.out.println(weakReference.get());
  }
}

結果:

java.lang.Object@16d3586
After GC:
null

9 引用の再考: 幻の引用

ファントム リファレンス - オブジェクト リサイクルの追跡

ファントム リファレンス - オブジェクト リサイクルの追跡

「ゴースト参照」または「ファントム参照」とも呼ばれ、すべての参照タイプの中で最も弱いものです。

オブジェクトに仮想参照があるかどうかは、オブジェクトのライフサイクルをまったく決定しません。オブジェクトがファントム参照のみを保持している場合、参照がないのとほぼ同じであり、いつでもガベージ コレクターによって回収される可能性があります。

単独では使用できず、ファントム参照によって参照先オブジェクトを取得することもできません。ファントム参照の get() メソッドでオブジェクトを取得しようとすると、常に null になります。

オブジェクトにファントム アソシエーションを設定する唯一の目的は、ガベージ コレクション プロセスを追跡することです。たとえば、このオブジェクトがコレクターによってリサイクルされると、システム通知を受け取ることができます。

  • ファントム参照は、参照キューで使用する必要があります。ファントム参照は、作成時にパラメーターとして参照キューを提供する必要があります。ガベージ コレクターがオブジェクトを再利用しようとしているときに、まだ仮想参照があることがわかった場合は、オブジェクトのリサイクル後に仮想参照を参照キューに追加して、オブジェクトのリサイクルをアプリケーションに通知します。
  • ファントム参照はオブジェクトの回復時間を追跡できるため、一部のリソース解放操作も実行してファントム参照に記録できます。
  • JDK1.2版以降は、ファントムリファレンスを実現するために PhatomReference クラスが提供されています。
Object obj = new Object();
ReferenceQueue phantomQueue = new ReferenceQueue();
PhantomReference<Object> pf = new PhantomReference<Objec>(obj, phantomQueue);
obj = null;

10 参考文献の再検討: ファイナライザの参考文献

最終参照

  • これは、オブジェクトの finalize() メソッドを実装するために使用され、ファイナライザー参照とも呼ばれます。
  • 手動でコーディングする必要はありません。そのメモリは参照キューで使用されます。
  • GC 時に、ファイナライザーの参照がキューに入れられます。ファイナライザー スレッドは、ファイナライザー参照を介して参照されたオブジェクトを見つけ、その finalize() メソッドを呼び出します。参照されたオブジェクトは、2 番目の GC 中にのみリサイクルできます。

おすすめ

転載: blog.csdn.net/weixin_43811294/article/details/126824004