バックグラウンド
Javaには非常に重要な概念があります。つまり、すべてがオブジェクトです。いわゆるオブジェクトは、現実のものを抽象化し、継承、実現、組み合わせによってすべてを含めることができるため、Java(すべてのオブジェクト指向言語を含む)を学習する過程でオブジェクトの概念を理解することが重要です。
プログラムでオブジェクトを使用する必要がある場合、それは祖父です。リフレクションを使用する場合でも、オブジェクトを作成する必要があります。必要がない場合は、新世代から逃れることができても、それはゴミです。老後を追いかけます。
今日、私たちが学びたいのは、JVMがガベージ(オブジェクト)をどのように処理するかです。学習する前に、まず次の質問について考えます。
- オブジェクトのライフサイクルの段階は何ですか?
- オブジェクトがゴミになったことを判断するにはどうすればよいですか?
- ゴミをマークする方法は?
- ゴミ(物)をリサイクルする方法とその戦略は?
1.オブジェクトのライフサイクル
通常の状況では、Java開発者は、JVMがクリーンアップ(オブジェクトの管理)に役立つため、これらのオブジェクトのライフサイクルを管理する必要なしに、不用意に新しいオブジェクトを作成できます。ただし、高度なコーダーとして、私たちはそれを注意深く理解する必要があります。まず、次のオブジェクトのライフサイクルを理解しましょう。
1.1マクロの視点
マクロの観点から、オブジェクトのライフサイクルは次のようになります。オブジェクトの作成--->オブジェクトの使用--->オブジェクトのリサイクル。
- オブジェクトの作成
オブジェクトの作成では、新しい命令、逆シリアル化、リフレクションなどのメソッドを使用できます。この手順は、主にオブジェクトにメモリを割り当てて初期化することです。
- オブジェクトの使用
オブジェクトの使用法は、JVMスタック内のオブジェクトの参照を見つけ、ヒープ内のオブジェクトの特定の場所にアクセスすることです。よく使用されるメソッドは、ハンドルと直接ポインターです。
- オブジェクトのリサイクル
オブジェクトのコレクションはガベージコレクションです。以下で詳しく説明します。
これらの3つの領域を見るだけではまだ少し不明確なので、粗すぎます。オブジェクトのライフサイクルを顕微鏡の観点から分析してみましょう。
1.2ミクロの視点
ミクロの観点から、オブジェクトのライフサイクルは、作成フェーズ、アプリケーションフェーズ、非表示フェーズ、到達不能フェーズ、収集可能フェーズ、最終フェーズ、およびオブジェクトスペース再配布フェーズの7つのフェーズに大別できます。
- 作成フェーズ
オブジェクト作成段階は、主にオブジェクトにメモリスペースを割り当て、オブジェクトの構築を開始し、静的メンバーの初期化を完了することです。オブジェクトが作成され、特定の変数に割り当てられると、このオブジェクトの状態はアプリケーションフェーズに切り替わります。
- 申請段階
アプリケーションフェーズは、オブジェクトが少なくとも1つの強力な参照に関連付けられている場合です。(慌てる必要はありません。強力な見積もりの概念については、以下で説明します)
- 見えないステージ
オブジェクトが非表示の段階にある場合、プログラムはオブジェクトへの強い参照を保持していませんが、これらの参照はまだ存在している可能性があります。一般に、プログラムの実行がスコープを超えたことを意味します。
boolean flag= false;if(flag){ flag = 0; num++; }System.out.println(num);
上記のプログラムでは、ローカル変数numがSystem.out.println(num)のスコープを超えており、プログラムは変数numが非表示の段階にあると見なします。
- 到達不能な段階
到達不能ステージのオブジェクトは、オブジェクトが強力な参照によって保持されなくなったことを意味しますが、オブジェクトは、ロードされた静的変数やスレッド、またはJVMやその他のシステムのJNIなどの強力な参照によって保持される可能性があります。これらの特別な強力な参照「GCルート」と呼ばれます。これらのGCルートが存在すると、オブジェクトのメモリリークが発生し、リサイクルできなくなります。
- コレクタブルステージ
ガベージコレクターは、オブジェクトがすでに「到達不能フェーズ」にあることを検出し、ガベージコレクターがオブジェクトのメモリスペースを再度割り当てる準備ができると、オブジェクトは「収集フェーズ」に入ります。
- 最終段階
finalizeメソッドを実行した後もオブジェクトがまだ到達不能状態にある場合、オブジェクトは最終段階に入ります。この段階では、ガベージコレクターがオブジェクトスペースを再利用するのを待っています。
- オブジェクト空間の再割り当てフェーズ
オブジェクトスペースが再度割り当てられると、ガベージコレクタは、オブジェクトが占有していたメモリスペースを再利用または再配布し、オブジェクトが完全に消えます。これを「オブジェクトスペースの再割り当て」と呼びます。
上からロープがたくさん落ちていて、小さな友達は理解しているようですが、小さな友達は、オブジェクトがゴミになり、ゴミ(オブジェクト)がいつクリアされるかを知りたいだけです。
実際、これらの段階には、オブジェクトの作成、オブジェクトの使用、オブジェクトの無効化、オブジェクトのガベージとしてのマーク付け、およびオブジェクトのリサイクルのプロセス全体が伴います。要件を満たすために、ガベージ(オブジェクト)の問題に焦点を当てましたが、オブジェクトがガベージになるのに非常に役立つため、問題に焦点を当てる前に、まずオブジェクト参照の概念を理解します。
1.3オブジェクト参照
JDK1.2以降、Java設計者は、オブジェクト参照を4つのカテゴリ、つまり、強参照、ソフト参照、弱参照、およびファントム参照に分類します。
- 強い引用
強い参照は、オブジェクトが[有用で必要な]状態にあることを示し、最も一般的に使用される参照です。オブジェクトに強い参照がある場合、ガベージコレクターはそれを再利用しません。メモリスペースが不足している場合でも、Java仮想マシンはOutOfMemoryErrorをスローしてプログラムを異常終了させ、強力な参照を持つオブジェクトをリサイクルしてもメモリ不足の問題を解決しません。
Student student = new Student(); // 这就是强引用
- ソフトリファレンス
ソフトリファレンスは、オブジェクトが[有用ですが必須ではない]状態にあることを示します。十分なメモリスペースの場合、オブジェクトにソフト参照しかない場合、ガベージコレクターはそれを再利用しませんが、メモリスペースが不十分な場合、ガベージコレクターはオブジェクトを再利用します(リサイクルはOutOfMemoryErrorエラーの前に発生します)。ガベージコレクタがそれを収集しない限り、オブジェクトはプログラムで使用できます。
ソフト参照は、Webページのキャッシュ、画像のキャッシュなど、メモリに依存するキャッシュを実装するために使用されます。
Student student = new Student();SoftReference softReference = new SoftReference(student);
- 弱い参照
弱参照は、オブジェクトが[有用かもしれないが必須ではない]状態にあることを意味します。ソフト参照に似ていますが、ソフト参照よりも弱いです。弱い参照を持つオブジェクトのみがライフサイクルが短くなります。GCスレッドがその管轄下のメモリ領域をスキャンするとき、弱い参照のみを持つオブジェクトを見つけると、弱い参照に関連付けられたこれらのオブジェクトを再利用します。つまり、現在のメモリ不足に関係なく、GCは弱参照に関連付けられたオブジェクトを再利用します。ただし、GCは優先度の低いスレッドであるため、参照が弱いオブジェクトをすばやく検出できるとは限りません。
java.langThreadLocalの古典的な実用的なケース
- ファントムリファレンス
ファントム参照とは、オブジェクトが[役に立たない]状態にあることを意味します。つまり、ファントム参照は参照なしと同等であり、GCによっていつでもリサイクルできます。仮想参照を設定する目的は、仮想参照に関連付けられたオブジェクトがガベージコレクターによってリサイクルされたときにシステム通知を受信することです(GCによってリサイクルされているオブジェクトのアクティビティを追跡するために使用されます)。
ReferenceQueue referenceQueue = new ReferenceQueue();PhantomReference phantomReference = new PhantomReference(object,queue);
2.ごみ(対象)の判断
ガベージコレクションの最初のステップは、オブジェクトがガベージであるかどうかを判断することです。実際、オブジェクトがガベージであるかどうかを判断するのはプログラマーの責任ではありません。より正確には、Java開発者は気にする必要はありません。本当にコードのクリーンさや強迫性障害がある場合、gcメソッドを無料で呼び出すことができます。
実際、ガベージコレクションには、参照カウントアルゴリズムと到達可能性分析アルゴリズムの2つの主要なアルゴリズムがあります。
2.1参照カウントアルゴリズム
参照カウントアルゴリズムは非常に古いアルゴリズムであり、現在多くのJavaバージョンで廃止されています。学習者として、それを理解する必要があります。
定義:参照カウントアルゴリズムは、オブジェクトに参照カウンターを追加します。オブジェクトが参照されると、カウントが1増加します。参照が無効な場合、カウンターは1減少し、参照カウントが0の場合、オブジェクトは減少します。また、失敗するとガベージになり、JVMがリサイクルを開始します。
定義の観点から、参照カウントアルゴリズムはまだ非常に理解しやすいです、それは明らかな長所と短所を持つアルゴリズムです、私たちは見下し続けます。
- 利点
①参照カウントアルゴリズムの原理は単純で、リアルタイムのパフォーマンスは強力です。参照カウンターが0の場合、JVMはそれを直接リサイクルできます。
②参照カウンターは単一のオブジェクトに対してのみ機能します。つまり、JVMがスキャンする場合、オブジェクトのみをスキャンし、参照に沿ったすべてのオブジェクトをスキャンするわけではありません。
- 不利益
①オブジェクトが参照されるたびに、参照カウンターの更新に一定の時間がかかります。
②参照サイクルの問題が発生します。つまり、オブジェクトAがオブジェクトBを参照し、オブジェクトBがオブジェクトAを参照します。AとBは相互に参照するため、カウントする必要がなくなり、ガベージコレクションが行われません。 JVM。
参照カウントアルゴリズムによって引き起こされる問題を解決するために、優れたJava開発者は別のアルゴリズムを提案しました。それは到達可能性分析アルゴリズムです。
2.2到達可能性分析アルゴリズム
到達可能性分析アルゴリズムのアイデアは、開始点として一連の「GCルート」オブジェクトを検索することです。「GCルート」とオブジェクトの間に到達可能なパスがない場合、オブジェクトは到達不能であると言われます。オブジェクトは到達不能と見なされます。オブジェクトはリサイクルできます。
到達可能性分析アルゴリズムを次の図に示します。
参照カウントアルゴリズムとは異なり、参照カウントアルゴリズムは、オブジェクトが死んでいるかどうか、到達可能性分析アルゴリズムによって分析されたオブジェクトがまだ生きているかどうかを判断します。到達可能性分析アルゴリズムは、参照カウントアルゴリズムの循環参照問題を効果的に解決できます。到達可能性分析アルゴリズムは、オブジェクトがゴミであるかどうかを判断するための主流のアルゴリズムです。
3.ガベージマーク
以上、対象物がごみであるかどうかを判断する方法を説明し、参照カウント法と到達可能性分析法を使用します。私の友人は、これらの2つのアルゴリズムが、オブジェクトがガベージであるかどうかを判断し、同時にオブジェクトにマークを付けることであることを発見したに違いありません。
はい、より正確に言うと、オブジェクトに参照がない場合、そのオブジェクトはガベージです。参照カウントアルゴリズムのカウンターと到達可能性分析アルゴリズムの参照チェーンは、どちらもオブジェクトをマークする方法です。
また、参照カウントアルゴリズムの循環参照問題のため、既存の主流のマーキングアルゴリズムは到達可能性分析アルゴリズムであると前述しました。以下では、到達可能性分析アルゴリズムの下でガベージオブジェクトをマークするプロセスを詳細に分析します。
Javaでは、GCルートとして使用できるオブジェクトには通常次のものが含まれます。
- 仮想マシンスタックで参照されるオブジェクト
- メソッド領域の静的プロパティによって参照されるオブジェクト
- メソッド領域の定数によって参照されるオブジェクト
- ネイティブメソッドで(ネイティブ)によって参照されるオブジェクト
到達可能性分析アルゴリズムでは、到達不能なオブジェクトがあったとしても、そのオブジェクトは必ずしも避けられないわけではありません。オブジェクトが本当に死ぬためには、2回のマーキングプロセスを経る必要があります。
マーキングプロセス分析:
到達可能性分析アルゴリズムを使用してオブジェクトを分析するときに、GCルートチェーンから到達できないオブジェクトが見つかった場合、そのオブジェクトは最初にマークされてからフィルタリングされます。フィルタの条件は、オブジェクトはfinalize()メソッドを実行する必要があります(各オブジェクトにはデフォルトでこのメソッドがあります)が、オブジェクトがfinalize()メソッドをオーバーライドしない場合、またはオブジェクトのfinalizeメソッドが仮想マシンによって一度呼び出された場合、それは見なされます「実行する必要がない」とガベージコレクションとしてデバイスを直接リサイクルできます。
オブジェクトがfinalize()メソッドを実行するために必要であると判断された場合、仮想マシンはオブジェクトをキューに入れ、専用のFinalizerスレッドがオブジェクトのfinalize()メソッドを実行します。この時点で参照チェーン内のオブジェクトに再関連付けされているオブジェクトがある場合、それらは2回目にマークされたときに、このキューに移動されます。
4.ガベージコレクション
メモリは非常に貴重なリソースであり、不要なゴミオブジェクトの場合は、それを排除する必要があります。上で説明したガベージ(オブジェクト)の判断とガベージマーキングの手順はすべて、ガベージコレクションへの道を開いています。
記事JVMメモリでは、JVMのランタイムデータ領域を詳細に分析し、JVMに慣れていない学生は勉強に行くことができます。
4.1ヒープレイアウト構造の分析
実際、ガベージコレクションの場所は、ランタイムデータ領域のヒープ内にあります。オブジェクトの存続が循環することは誰もが知っています。オブジェクトが参照されていない場合は、オブジェクトをクリアできると見なすことができます。私たちがゴミだと思うもの。オブジェクトごとに存続時間が異なるため、GCスレッドによるガベージスキャンの時間と頻度を減らすために、存続期間の長いオブジェクトを別の領域に配置できます。したがって、ヒープのレイアウトが決定されます。一般に、ヒープは2つの部分に分けられます。若い世代と古い世代です。
画像-20210205113109427
新世代と旧世代の比率は1:2です。この比率は一意ではありません。パラメータ-XX:NewRatioを使用して、特定のシーンに応じて指定できます。きめ細かい分割に分割すると、新しい世代が世代はエデンエリアとサバイバーエリアに分割でき、サバイバーエリアはFromSurvivorとToSurvivorに分割できます。デフォルトの比率は8:1:1です。
このとき、なぜサバイバーを2つの同じサイズのスペースに分割するのかという疑問が再び生じます。良い質問です。答えから始めましょう。メモリの断片化の問題を解決するために、2つの部分を2つの部分に分割します。メモリの断片化が深刻な場合、つまり2つのオブジェクトが不連続メモリを占有し、既存の連続メモリでは不十分な場合新しいオブジェクトを保存します。、ガベージコレクション(GC)をトリガーします。
4.2ガベージコレクションアルゴリズム
上記では、ランタイムデータ領域のヒープのレイアウト構造を分析しました。オブジェクトのライフサイクルに応じて、ヒープは新世代と旧世代に分けられます。新世代は、エデンの3つの領域に細分されます。 、サバイバーからサバイバーへ、そしてプロポーションそれは8:1:1です。ヒープのレイアウト構造を理解することは、JVMでのガベージコレクションアルゴリズムのフローを理解するのに非常に役立ちます。なんでそんなこと言うの?ガベージオブジェクトは主にヒープ内にあり、ヒープは異なるパーティションに分割されているため、各パーティションの特性に応じて使用されるガベージコレクションアルゴリズムも異なります。
次の学習では、最初に一般的に使用されるガベージコレクションアルゴリズムを学習し、最後にヒープ内のさまざまなパーティションの特性に応じて適切なガベージコレクションアルゴリズムを選択します。
一般に、一般的に使用されるガベージコレクションアルゴリズムには、マークスイープアルゴリズム、マークコピーアルゴリズム、マークソートアルゴリズム、および世代別コレクションアルゴリズムが含まれます。
マークスイープアルゴリズム
マーク除去アルゴリズムは、マークとクリアの2つの段階に分けられます。つまり、最初にリサイクルするオブジェクトにマークを付け、マークが完了した後にオブジェクトを均一にクリーニングします。その利点は高効率ですが、欠点はメモリの断片化が発生しやすいことです。
マーキングフェーズは、参照チェーンをスキャンして存続するオブジェクトをマーキングするための開始点としてのルートノード(GCルート)に基づいています。クリーニングフェーズでは、オブジェクトのコレクション全体をスキャンし、コレクション内のマークされていないオブジェクトを削除します。
ガベージマークとガベージコレクションの段階を次の図に示します。
マークコピーアルゴリズム
マークコピーアルゴリズムは、メモリを2つのスペースに分割します。どの時点でも、動的に割り当てられたすべてのオブジェクトは1つのスペースにのみ割り当てることができます。このスペースはアクティブスペースと呼ばれ、もう1つのスペースは空きです。有効なメモリスペースが使い果たされると、JVMはプログラムを一時停止し、レプリケーションアルゴリズムのGCスレッドを開始します。次に、GCスレッドは、アクティブ範囲内のすべての存続オブジェクトを空き範囲にコピーし、メモリアドレスに従って厳密な順序で配置します。同時に、GCスレッドは、存続オブジェクトのメモリ参照アドレスを次のように更新します。新しいメモリアドレス。
この時点で、空きセクションはアクティブセクションと交換され、すべてのガベージオブジェクトは、現在の空きセクションである元のアクティブセクションに残されています。実際、アクティブな間隔が空間的な間隔に変換されると、ガベージオブジェクトが一度に収集されます。
タグソートアルゴリズム
マークの並べ替えアルゴリズムは、マークのクリアアルゴリズムと非常によく似ており、マーキングと並べ替えの2つの段階に分かれています。
マーキング:その最初の段階は、マーキング/スイープアルゴリズムとまったく同じです。これは、GCルートをトラバースしてから、残っているオブジェクトにマークを付けることです。
再編成:残っているすべてのオブジェクトを移動し、メモリアドレスの順に並べてから、終了メモリアドレスの後にすべてのメモリを再利用します。したがって、第2段階は仕上げ段階と呼ばれます。
世代別収集アルゴリズム
現在、ほとんどのJVMガベージコレクターは世代別収集アルゴリズムを使用しています。オブジェクトの存続のライフサイクルに応じて、メモリを若い世代、古い世代、永続的な世代の3つの領域に分割するという考え方です。分割領域は、ヒープのメモリ構造を参照できます。新世代と旧世代は、ヒープ内の分割と1対1で対応し、永続世代はJava8のメタスペースに置き換えられました。
- 新生代のリサイクルアルゴリズム
若い世代は主にライフサイクルの短いオブジェクトを保存します。新しく生成されたオブジェクトはすべて、最初に若い世代のエデンエリアに配置する必要があります。再生時に、エデンエリアに残っているオブジェクトはTo Survivorエリアにコピーされ、次にエデンエリアがクリアされます。To Survivorエリアも満杯になると、EdenエリアとToSurvivorエリアのサバイバーオブジェクトがFromSurvivorエリアにコピーされ、EdenとFrom Survivorエリアがクリアされます。このとき、ToSurvivorエリアは空です。 、次にToSurvivorエリアとFromSurvivorエリアが空になります。Survivorエリア交換、つまりFromSurvivorエリアを空のままにします。
FromSurvivorエリアがEdenエリアとToSurvivorエリアに生き残ったオブジェクトを保存するのに十分でない場合、生き残ったオブジェクトは古い時代に直接保存されます。古い世代がいっぱいになると、フルGCがトリガーされます。つまり、若い世代と古い世代の両方がリサイクルされます。
通常の状況では、新世代で発生するGCはマイナーGCとも呼ばれ、マイナーGCの頻度は比較的高くなります(トリガーする前にエデンエリアがいっぱいになるのを必ずしも待つ必要はありません)。
- 老後のリサイクルアルゴリズム
若い世代のN個のガベージコレクションを生き残ったオブジェクトは、古い世代に配置されます。したがって、旧世代に格納されているオブジェクトは寿命の長いオブジェクトと見なすことができます。また、旧世代のメモリは新世代よりもはるかに大きい(約1:2)。旧世代のメモリがいっぱいになると、メジャーGCまたはフルGCがトリガーされます。フルGCの発生頻度は比較的高いです。旧世代の対象物の生存期間が長く、生存率が高い。
- 恒久的な生成収集アルゴリズム
永続プロキシは、Javaクラス、メソッドなどの静的ファイルを格納するために使用されます。永続生成はガベージコレクションに大きな影響を与えませんが、一部のアプリケーションはHibernateなどの一部のクラスを動的に生成または呼び出す場合があります。この場合、これらの新しく追加されたクラスを格納するために、比較的大きな永続生成スペースを設定する必要があります。操作。JDK1.8より前は、永続世代はメソッド領域と呼ばれ、JDK1.8以降は、永続世代はメタスペースと呼ばれていました。
ヒープ構造のさまざまなパーティションでのアルゴリズムの選択
実際、JVMは、ヒープのさまざまな領域(新世代と旧世代)に対してさまざまなアルゴリズムを使用します。
新世代はレプリケーションアルゴリズムにより適しています。新世代には、Eden、From Survivor、To Survivorの3つの領域があります。これは、Eden領域のオブジェクトがTo Survivorにコピーされ、FromSurvivorとToSurvivorが交換されるためです。頻繁に発生する場合は、レプリケーションアルゴリズムが採用されます。
老後のオブジェクトのライフサイクルは比較的長いため、レプリケーションアルゴリズムには適していません。老後では、マークソート/クリアアルゴリズムが一般的に使用されます。
元のリンク:https://xie.infoq.cn/article/0494fd7554ecf2788344f3b1d
この記事がお役に立てば幸いです。私の公式アカウントに注意を払い、キーワード[インタビュー]に返信して、Javaコアナレッジポイントとインタビューギフトパッケージをまとめてください。共有する技術的な乾物記事と関連資料がもっとあります。一緒に学び、進歩しましょう!