JVM -- ガベージ コレクション; ガベージ コレクション アルゴリズム (3)

読む前の参考

https://blog.csdn.net/MinggeQingchun/article/details/126947384

https://blog.csdn.net/MinggeQingchun/article/details/127066302

1.マークされたオブジェクトがゴミかどうか

JVM のメモリ構造には、プログラム カウンタ、仮想マシン スタック、ローカル メソッド スタック、ヒープ領域、およびメソッド領域の 5 つの領域があります。

このうち、プログラムカウンター、仮想マシンスタック、ローカルメソッドスタックの3つの領域は、スレッドで生まれては破壊されるため、これらの領域のメモリ割り当てと回復は決定論的であり、リサイクルについてあまり考える必要はありません。メソッドが終了するか、スレッドが終了すると、メモリは自然に回復します。

Java ヒープ領域はメソッド領域とは異なり、メモリのこの部分の割り当てと回復は動的であるため、ガベージ コレクターが注意を払う必要があります。

(1) 参照カウント方法

参照カウントは、ガベージ コレクターの初期の戦略でした。このアプローチでは、ヒープ内の各オブジェクト インスタンスに参照カウントがあります。オブジェクトが作成されると、オブジェクト インスタンスは、カウントが 1 に設定された変数に割り当てられます。他の変数にこのオブジェクトへの参照が割り当てられると、カウントは 1 増加します (a = b の場合、b によって参照されるオブジェクト インスタンスのカウンターは +1 になります)。新しい値が取得されると、オブジェクト インスタンスの参照カウンタが 1 減らされます。参照カウントが 0 のオブジェクト インスタンスはすべてガベージ コレクションの対象になります。オブジェクト インスタンスがガベージ コレクションされると、それが参照するすべてのオブジェクト インスタンスの参照カウンターが 1 減ります。

  • 利点: 参照カウント コレクターは非常に迅速に実行でき、プログラムの実行中に絡み合うことができます。プログラムを長時間中断する必要がないリアルタイム環境では、より有益です。

  • 短所:循環参照を検出できません。 A オブジェクトが B オブジェクトへの参照を持っている場合、B オブジェクトは A オブジェクトを参照します。このように、参照カウントが 0 になることはありません

JVM はこのアプローチを採用しませんが、到達可能性分析

(2) アクセシビリティ分析

Java 仮想マシンのガベージ コレクターは、到達可能性分析を使用して、存続しているすべてのオブジェクトを調査します。

到達可能性アルゴリズムは、現在主流の仮想マシンで使用されているアルゴリズムで、プログラムはすべての参照関係をグラフと見なし、ノード GC Roots から開始して、対応する参照ノードを検索し、このノードを見つけた後、参照を検索し続けます。参照ノード、すべての参照ノードが見つかった場合、残りのノードは参照されていないノード、つまり役に立たないノードと見なされ、不要なノードは再利用可能なオブジェクトと判断されます。

Java 言語では、GC ルートとして使用できるオブジェクトには次のものがあります。

(1) 仮想マシンスタック内で参照されるオブジェクト(スタックフレーム内のローカル変数テーブル)

(2) メソッド領域のクラス静的プロパティから参照されるオブジェクト

(3) メソッド領域の定数が参照するオブジェクト

(4) ローカルメソッドスタックでJNI(ネイティブメソッド)が参照するオブジェクト

上記の図から、reference1、reference2、および reference3 はすべて GC Root です。

参照 1 --> オブジェクト インスタンス 1

reference2 --> オブジェクト インスタンス 2

参照 3 --> オブジェクト インスタンス 4 --> オブジェクト インスタンス 6

オブジェクト インスタンス 1、2、4、および 6 はすべて、オブジェクトの到達可能性、つまり、生き残ったオブジェクト、つまり GC によってリサイクルできないオブジェクトを持っていると結論付けることができます。ただし、インスタンス 3 と 5 は直接接続されていますが、それらに接続されている GC ルートはありません。つまり、GC ルートによって到達できず、GC によってリサイクルされるオブジェクトです。

2.ガベージコレクションの原則

ガベージコレクション GC (Garbage Collection) の基本原理: メモリ内で使用されなくなったオブジェクトをリサイクルする. GC でリサイクルに使用される方法はコレクターと呼ばれる. GC はリソースと時間を消費する必要があるため, Java はオブジェクトの寿命 サイクル特性が分析された後、GC によるアプリケーションへの一時停止を可能な限り短縮するために、新しい世代と古い世代に従ってオブジェクトが収集されます。

  • ヒープメモリは两块1つに分割され年轻代、もう1つは老年代。老年代:年轻代比例为2:1

  • 若い世代はさらにEdenとに分けられますsurvivorスペース サイズの比率は、デフォルトで 8:2 です。

  • サバイバルエリアはs0(From Spaces1(To Spaceに分かれていますこの 2 つのスペースはまったく同じサイズで、1 対 1 の比率の双子です。

ヒープ メモリ ガベージ コレクション プロセス 

1.新生成オブジェクトは最初にEdenゾーン(エデンゾーンに配置され、エデンゾーン满了がトリガーされますMinor GC

2. 最初のステップで GC を生き残ったオブジェクトは、survivorゾーン内のS0 エリアFrom Spaceに移動されます. S0 エリアがいっぱいになると、それがトリガーされますMinor GC. S0 エリアを生き残ったオブジェクトは、に移動されます. S1エリアのTo Space、S0エリアはフリーです。

S1 がいっぱいになったら、次に GC を実行し、生き残ったオブジェクトが再び S0 領域に移動し、S1 領域が解放されるというGC が繰り返されます.GC が実行されるたびに、オブジェクトの年齢は涨一岁特定のしきい値に達します. (15)、そして入ります老年代

3.ガベージコレクタによっては、1回発生した後Minor GC(前提条件)、古い世代が表示される場合がありますMajor GC

フル GC トリガー条件

  • System.gc を手動で呼び出すと、フル GC が継続的に実行されます。

  • 古い世代のスペースが不足しています/いっぱいです

  • メソッド領域のスペースが足りない/いっぱいです

stop-the-world(STW)

stop-the-worldどの GC アルゴリズムでも発生します。stop-the-world は、GC を実行する必要があるため、JVM が停止アプリケーションの実行を停止することを意味します。

stop-the-world が発生すると、 GC タスクが完了するまで、GC に必要なスレッドを除くすべてのスレッドが状態线程になります。等待GC の最適化は、多くの場合、stop-the-world の発生を減らすために行われます

JVM GC はリサイクル堆内存方法区内オブジェクトのみです。また、栈内存データは範囲外になると JVM によって自動的に解放されるため、JVM GC の管理範囲には含まれません。

参照できます

7種類のjvmガベージコレクター、今回は全部わかる - 見てみよう

1、マイナーGC

新世代のオブジェクトのコレクション

マイナー GC とは、新世代 GC、つまり、新世代 (Eden 領域と Survivor 領域を含む) で発生するガベージ コレクション操作を指し、新世代が新しいオブジェクトにメモリ空間を割り当てることができない場合に、マイナー GC がトリガーされます。新しい世代のほとんどのオブジェクトのライフ サイクルは非常に短いため、マイナー GC の頻度は非常に高く、ストップ ザ ワールドをトリガーしますが、そのリサイクル速度は非常に高速です

2、メジャーGC

古い世代のオブジェクトのコレクション

メジャー GC は、老朽化をリサイクルするために使用される Tenured 領域をクリーンアップします. メジャー GC が発生すると、通常、少なくとも 1 つのマイナー GC が発生します。

3、フルGC

System.gc() を積極的に呼び出して、プログラムで GC を強制する

フル GC は、新世代、旧世代、およびメタスペース全体のグローバル GC です (メタスペース、java8 以降は、パーマネント ジェネレーション perm gen を置き換えます)。Full GC は Major GC と等しくなく、Minor GC+Major GC とも等しくない. Full GC の発生は、ガベージ コレクションの種類を説明するために使用されるガベージ コレクタの組み合わせに依存します。

3 つまたは 5 つの参照

1.強い参照

2.ソフト参照

3、弱引用

4.ファントム参照

5.ターミネーターのリファレンス

(1) 強参照

すべての GC Roots オブジェクトが [強参照] によってオブジェクトを参照していない場合にのみ、オブジェクトをガベージ コレクションできます。

(2)ソフトリファレンス(SoftReference)

ソフト参照のみがオブジェクトを参照する場合、ガベージ コレクションの後、メモリがまだ不足しているときにガベージ コレクションが再度トリガーされます. ソフト参照オブジェクトをリサイクルすると、参照キューと連携してソフト参照自体を解放できます。

次のコードでは、VM はヒープ メモリを 20M に設定し、コメント部分はサイクルを 5 回実行した後にヒープ メモリ オーバーフローのエラーを報告します。

/**
 * 软引用
 * -Xmx20m -XX:+PrintGCDetails -verbose:gc
 */
public class GC2SoftReference {
    private static final int _4MB = 4 * 1024 * 1024;

    public static void main(String[] args) throws IOException {
        //java.lang.OutOfMemoryError: Java heap space
        /*List<byte[]> list = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            list.add(new byte[_4MB]);
        }
        System.in.read();*/

        //软引用
        soft();
    }

    public static void soft() {
        // list --> SoftReference --> byte[]

        List<SoftReference<byte[]>> list = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            SoftReference<byte[]> ref = new SoftReference<>(new byte[_4MB]);
            System.out.println(ref.get());
            list.add(ref);
            System.out.println(list.size());

        }
        System.out.println("循环结束:" + list.size());
        for (SoftReference<byte[]> ref : list) {
            System.out.println(ref.get());
        }
    }
}

ソフト参照出力を使用します。値をチェックするときに最初の 4 つのオブジェクトがリサイクルされ、5 番目のオブジェクトのみが存在します。 

パラメータを VM に追加して出力を表示する

-Xmx20m -XX:+PrintGCDetails -verbose:gc

GC は 3 番目のオブジェクトが出力されるときにトリガーされます 

ソフト参照キュー

/**
 * 软引用, 配合引用队列
 * -Xmx20m -XX:+PrintGCDetails -verbose:gc
 */
public class GC3SoftReferenceQueue {
    private static final int _4MB = 4 * 1024 * 1024;

    public static void main(String[] args) {
        List<SoftReference<byte[]>> list = new ArrayList<>();

        // 引用队列
        ReferenceQueue<byte[]> queue = new ReferenceQueue<>();

        for (int i = 0; i < 5; i++) {
            // 关联了引用队列, 当软引用所关联的 byte[]被回收时,软引用自己会加入到 queue 中去
            SoftReference<byte[]> ref = new SoftReference<>(new byte[_4MB], queue);
            System.out.println(ref.get());
            list.add(ref);
            System.out.println(list.size());
        }

        // 从队列中获取无用的 软引用对象,并移除
        Reference<? extends byte[]> poll = queue.poll();
        while( poll != null) {
            list.remove(poll);
            poll = queue.poll();
        }

        System.out.println("===========================");
        for (SoftReference<byte[]> reference : list) {
            System.out.println(reference.get());
        }

    }
}

(3) 弱参照 (WeakReference)

弱参照のみがオブジェクトを参照している場合、ガベージ コレクション中に、メモリが十分であるかどうかに関係なく、弱参照オブジェクトは再利用され、弱参照自体は参照キューと連動して解放されます。

/**
 * 弱引用
 * -Xmx20m -XX:+PrintGCDetails -verbose:gc
 */
public class GC4WeakReference {
    private static final int _4MB = 4 * 1024 * 1024;

    public static void main(String[] args) {
        //  list --> WeakReference --> byte[]
        List<WeakReference<byte[]>> list = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            WeakReference<byte[]> ref = new WeakReference<>(new byte[_4MB]);
            list.add(ref);
            for (WeakReference<byte[]> w : list) {
                System.out.print(w.get()+" ");
            }
            System.out.println();

        }
        System.out.println("循环结束:" + list.size());
    }
}

(4)ファントムリファレンス(PhantomReference)

参照キューと共に使用する必要があり、主に ByteBuffer で使用されます。参照されたオブジェクトが再利用されると、ファントム参照がキューに入れられ、リファレンス ハンドラー スレッドがファントム参照関連のメソッドを呼び出してダイレクト メモリを解放します。

(5) FinalReference(ファイナルリファレンス)

すべてのオブジェクトは Object から継承され、Object には finalize() メソッドがあり、オブジェクトは finalize() メソッドをオーバーライドできます。このメソッドは、オブジェクトがガベージ コレクションされるときに呼び出されます。しかし、オブジェクトには強い参照がなくなり、finalize() メソッドは実際にはファイナライザー参照を通じて実装されます。B オブジェクトが A4 の強い参照を切断した後、ファイナライザー参照が参照キューに追加され、それは非常に低い優先度の finalizeHandler によってスキャンされます. 参照キュー内のファイナライザー参照がスキャンされると、参照された A4 はオブジェクトの finalize() メソッド。finalize() メソッドはすぐに実行されないため、最初にエンキューされ、スキャンを担当する finalizeHandler は優先度が低く、finalize() が遅延する可能性があるため、リソースのリサイクルに使用することはお勧めしません。

手動コーディングの必要はありませんが、参照キューで内部的に使用されます. ガベージ コレクション中に、ファイナライザー参照がキューに入れられ (参照されるオブジェクトは一時的に再利用されていません)、ファイナライザー スレッドはファイナライザーを介して参照されるオブジェクトを見つけます.参照し、その finalize メソッドを呼び出すと、参照されたオブジェクトは 2 番目の GC 中にのみ回復できます

4. ガベージ コレクション アルゴリズム

(1) Mark-Clear アルゴリズム (Mark Sweep)

マーキング段階:

マーキングのプロセスは、実際には、以前に紹介した到達可能性分析アルゴリズムのプロセスであり、すべての GC ルート オブジェクトを走査し、GCRoots オブジェクトから到達可能なオブジェクトを通常はオブジェクトのヘッダーにマーキングし、到達可能なオブジェクトとして記録します。

クリーンアップ フェーズ:

クリアのプロセスは、ヒープ メモリをトラバースすることであり、(オブジェクト ヘッダー情報を読み取ることによって) オブジェクトが到達可能なオブジェクトとしてマークされていないことが判明した場合、そのオブジェクトはリサイクルされます。

欠点:

(1) メモリの断片化が発生します

(2) 継続的なメモリ空間を割り当てる必要がある大きなオブジェクトがある場合、ガベージ コレクション メカニズムが 2 回トリガーされることがあります。

まとめ:古い世代に向いており、生存オブジェクトが多い方が効率的

(2) マーク照合・圧縮アルゴリズム(マークコンパクト)

マークされたガベージをクリーンアップした後、散在するメモリ空間を圧縮し、アクティブなメモリと不連続なメモリをほぼ連続したメモリ空間に連続的にコピーして、使用されているメモリにできるだけ多くの穴があることを確認します

利点:多くのメモリの断片化を避ける

短所:全体的な効率が低下する

(3) コピーアルゴリズム (Copy)

生き残ったすべてのオブジェクトをマークし、これらの生き残ったオブジェクトを新しいメモリ (図の右側のメモリ領域) にコピーしてから、出荷されたすべてのメモリ (図の左側のメモリ領域) を再利用します。

アドバンテージ:

(1) 高効率、デブリなし

(2) 空間全体を 1 回だけスキャンする

欠点:

(1) 空きメモリ空間が必要

(2) 動くオブジェクトをコピーする必要がある

(3) メモリ使用率が低く、オブジェクトの生存率が高い年末の利用には向かない

新しい世代、つまり「生と死」に当てはまる

5.世代回復アルゴリズム

世代別コレクション アルゴリズムは、現在仮想マシンで使用されているリサイクル アルゴリズムです。メモリを世代に分割することで、古い世代に対してマーク照合が機能しないという問題を解決します。一般に、ヒープ領域は旧世代(Tenured Generation)と新世代(Young Generation)に分けられ、ヒープ領域の外に永続世代(Permanet Generation)という別の世代があります。

JDK8より前は、メソッド領域の実装は永久世代と呼ばれ、ヒープの一部をメソッド領域として使用していました

JDK8 以降、永続世代の実装が削除され、メタスペースの実装が置き換えられました. メタスペースは、ヒープの一部ではなく、オペレーティング システムの一部 (一部のメモリ) をメソッド領域として使用しました.

時代ごとに異なるアルゴリズムが使われているため、最も適したアルゴリズムが使われ、新しい世代の生存率は低く、複製アルゴリズムを使うことができます。ただし、古い時代のオブジェクトの生存率は高く、それを保証する余分なスペースがないため、マーク クリアまたはマーク フィニッシング アルゴリズムのみを使用できます。

オブジェクトは最初に Eden 領域に割り当てられます

新しい世代のスペースが不足すると、マイナー gc がトリガーされ、Eden と from の生き残ったオブジェクトがコピーによってコピーされ、生き残ったオブジェクトの年齢が 1 増加してから to が交換されます。

マイナー GC は、ワールドの停止をトリガーし、他のユーザー スレッドを一時停止し、ユーザー スレッドが実行を再開する前にガベージ コレクションが終了するのを待ちます。

オブジェクトの寿命が閾値を超えると老齢に昇格し、最大寿命は15(4bit)

古い世代のスペースが不足している場合は、最初にマイナー GC をトリガーしようとし、それでもスペースが不足している場合はフル GC をトリガーし、STW 時間が長くなります。

VM 関連のパラメーターを設定する 

パラメータ 意味
-Xms ヒープの初期サイズ (ヒープ メモリの初期サイズ、単位 m、g)
-Xmx または -XX:MaxHeapSize=size ヒープの最大サイズ。通常は物理メモリの 80% を超えません
-Xmn または (-XX:NewSize=サイズ + -XX:MaxNewSize=サイズ) 新生代の大きさ
-XX:InitialSurvivorRatio=ratio と -XX:+UseAdaptiveSizePolicy 生存面積率(動的)
-XX:SurvivorRatio=アカウント サバイバルゾーンの比率
-XX:MaxTenuringThreshold=しきい値 老齢閾値の促進
-XX:+PrintTenuringDistribution プロモーションの詳細
-XX:+PrintGCDetails -verbose:gc GC の詳細
-XX:+ScavengeBeforeFullGC FullGC 前 MinorGC
-XX:パーマサイズ 非ヒープ メモリの初期サイズ、一般的なアプリケーション設定は 200m に初期化され、最大 1024m で十分です
-XX:MaxPermSize 非ヒープ メモリの最大許容サイズ
-XX:SurvivorRatio=8 Young 世代の Eden 領域と Survivor 領域の容量比率。デフォルトは 8、つまり 8:1 です。
-XX:+DisableExplicitGC System.gc() を閉じる
-XX:+CollectGen0First FullGC 中に最初に YGC を行うかどうか。デフォルトは false です
-XX:TLABWasteTargetPercent eden 領域の TLAB のパーセンテージ。デフォルトは 1% です。
-Xnoclassgc ガベージ コレクションを無効にする

TLABメモリ 

TLABの正式名称はThread Local Allocation Buffer 、つまり线程本地分配缓存その名前から、オブジェクトの割り当てを高速化するために生まれた、スレッド専用のメモリ割り当て領域です。

各スレッドは、スレッド専用の作業領域である TLAB を生成します.Java 仮想マシンは、この TLAB 領域を使用して、マルチスレッドの競合を回避し、オブジェクト割り当ての効率を向上させます。

TLAB スペースは一般的にそれほど大きくありません.TLAB で大きなオブジェクトを割り当てることができない場合は、ヒープに直接割り当てられます

パラメータ 意味
-Xx:+UseTLAB TLAB を使用する
-XX:+TLABSize TLAB サイズの設定
-XX:TLABRefillWasteFraction TLAB 空間に入る単一オブジェクトのサイズを設定および維持します。これは比例値です。デフォルトは 64 です。つまり、オブジェクトが空間全体の 1/64 より大きい場合、オブジェクトはヒープ上に作成されます。
-XX:+PrintTLAB TLAB 情報を表示する
Xx:ResizeTLAB 自己調整 TLABRefillWasteFraction しきい値
/**
 * 分代回收
 * -Xms20M -Xmx20M -Xmn10M -XX:+UseSerialGC -XX:+PrintGCDetails -verbose:gc -XX:-ScavengeBeforeFullGC
 */
public class GC5Generational {
    private static final int _512KB = 512 * 1024;
    private static final int _1MB = 1024 * 1024;
    private static final int _6MB = 6 * 1024 * 1024;
    private static final int _7MB = 7 * 1024 * 1024;
    private static final int _8MB = 8 * 1024 * 1024;

    // -Xms20M -Xmx20M -Xmn10M -XX:+UseSerialGC -XX:+PrintGCDetails -verbose:gc -XX:-ScavengeBeforeFullGC
    public static void main(String[] args) throws InterruptedException {

    }
}

エデンの園 

Eden オブジェクトが From スペースと to スペースに転送され、From スペースと to スペースが交換されます

大きなオブジェクトは古い世代に直接昇格されます

OOM

おすすめ

転載: blog.csdn.net/MinggeQingchun/article/details/127089533