【Java】JVM (5)

ガベージコレクションメカニズム

オブジェクトの生存を判断する

ほとんどすべてのオブジェクト インスタンスはヒープに格納されます。ガベージ コレクターがオブジェクトをリサイクルする前に行うべきことは、これらのオブジェクトのうちどれがまだ「生存」しており、どれが「死んでいるか」を判断することです (死んでいるとは再利用が不可能であることを意味します) ) オブジェクトを使用する任意の方法)

アクセシビリティ分析

オブジェクトが生きているかどうかを判断します。このアルゴリズムの基本的な考え方は、「 」と呼ばれる一連のGC Rootsオブジェクトを開始点として使用することです。これらのノードを起点として向下搜索、検索によって移動するパスは引用链(参照チェーン) と呼ばれます。オブジェクトが GC ルートなしで GC ルートに接続される場合参照チェーンが存在する場合、オブジェクトが利用できないことが証明されます。

GC ルートとして機能するオブジェクトには次のものがあります (最初の 4 つに注目します)。

虚拟机栈(栈帧中的本地变量表)中引用的对象; 各スレッド呼び出しメソッドスタックで使用されるパラメータ、ローカル変数、一時変数など。

方法区中类静态属性引用的对象; Java クラスの参照型静的変数。

方法区中常量引用的对象; 例: 文字列定数プール内の参照。

本地方法栈中JNI(即一般说的Native方法)引用的对象

JVM 内部参照 (クラス オブジェクト、例外オブジェクト NullPointException、OutofMemoryError、システム クラス ローダー)。(強調しない)

 同期ロック(同期キー)で保持されているすべてのオブジェクト。(強調しない)

JVM内のJMXBean、JVMTIに登録されたコールバック、ローカルコードキャッシュなど(非強調)

 JVM 実装の「一時」オブジェクト、世代を超えて参照されるオブジェクト (世代モデルを使用して、世代の一部のみをリサイクルするオブジェクトをリサイクルする場合、これについては後で詳しく説明します。最初に概念を一般的に理解しましょう) (非強調)

上記リサイクルは全て対象であり、クラスのリサイクル条件は以下の通りです。

クラスをリサイクルするには条件が比較的厳しく、次の条件を同時に満たす必要があることに注意してください (制御できるパラメータがまだいくつかあるため、可能であるというだけであり、必ずではありません)。

1. このクラスのすべてのインスタンスはリサイクルされています。つまり、ヒープ内にこのクラスのインスタンスはありません。

2. このクラスをロードする ClassLoader はリサイクルされています。

3. このクラスに対応する java.lang.Class オブジェクトはどこからも参照されておらず、リフレクションを通じてこのクラスのメソッドにアクセスすることはできません。

ファイナライズメソッド

到達不可能なオブジェクトが到達可能性分析によって判断された場合でも、そのオブジェクトは「死ななければならない」わけではなく、依然として「保護観察」の段階にあります。オブジェクトが実際に死んでいると宣言するには、2 つのマーキング プロセスを経る必要があります。1 つは見つからない初めてマークされる GCRoots 参照チェーンを使用します。次に、スクリーニングを実行し (オブジェクトがファイナライズをオーバーライドする場合)、ファイナライズで保存できます。

引用

強力な参照

一般 Object obj = new Object() は強参照です。いずれの場合も、強い参照の関連付け (ルートに到達可能) がある限り、ガベージ コレクターは参照されたオブジェクトを再利用することはありません。

ソフトリファレンス SoftReference

ソフト参照に関連付けられた一部の便利だが必要ではないオブジェクトは、システムがメモリ (OuyOfMemory) をオーバーフローする前にリサイクルされます (このリサイクル後も十分なスペースがない場合は、メモリ オーバーフローがスローされます)。

たとえば、ユーザーが提供した画像を処理するためにプログラムが使用されます。すべての写真がメモリに読み込まれると、写真はすぐに開くことができますが、メモリ スペースが膨大になり、あまり使用されない一部の写真はメモリ スペースを浪費するため、メモリから手動で削除する必要があります。画像を開くたびにディスクファイルからメモリに読み込んで表示すると、メモリ使用量は少ないですが、よく使う画像によっては開くたびにディスクにアクセスする必要があり、コストが膨大になります。現時点では、ソフト参照を使用してキャッシュを構築できます。

弱い参照 WeakReference

一部は便利ですが (ソフト参照よりも低い)、必須ではありません。弱い参照に関連付けられたオブジェクトは、次のガベージ コレクションまでしか存続できません。GC が発生すると、十分なメモリがあるかどうかに関係なく、リサイクルされます。

注: ソフト参照 SoftReference と弱い参照 WeakReference は、メモリ リソースが不足している場合や、あまり重要ではないデータ キャッシュを作成する場合に使用できます。システム メモリが不足している場合、キャッシュ内のコンテンツが解放されることがあります。

実践的な応用(WeakHashMap、ThreadLocal)

仮想リファレンス PhantomReference

ゴーストリファレンス、最も弱い(いつでもリサイクルされます)

ガベージ コレクション中に通知を受信して​​、ガベージ コレクターが適切に動作しているかどうかを監視します。

JDK には、DirectByteBuffer という典型的なアプリケーションもあります。
DirectByteBuffer はゼロコピーの特性を持ち、Netty などのさまざまな NIO フレームワークで使用され、オフヒープ メモリを使用します。ヒープ メモリは JVM 自体によって管理され、オフヒープ メモリは手動で解放する必要があります。DirectByteBuffer にはファイナライザがありません。ネイティブ メモリのクリーニング作業は、sun.misc.Cleaner によって自動的に完了します。これは、以下に基づくクリーニング ツールです。 PhantomReference は、通常のファイナライザーよりも優れており、軽量です。

オブジェクト割り当て戦略

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

ラージ オブジェクトとは、大量の連続メモリ領域を必要とする Java オブジェクトを指します。最も一般的なラージ オブジェクトは、非常に長い文字列または多数の要素を含む配列です。

ラージ オブジェクトは、仮想マシンのメモリ割り当てにとって悪いニュースです。ラージ オブジェクトに遭遇することよりも悪いニュースは、「生きて死ぬ」「寿命の短いラージ オブジェクト」のグループに遭遇することです。プログラミング時にはこれを避けてください。

Java 仮想マシンで大きなオブジェクトを避ける理由は、スペースを割り当てるときに、メモリ内にまだ多くのスペースがあるときに、配置するのに十分な連続スペースを確保するために、ガベージ コレクションが事前にトリガーされやすいためです。彼ら。

また、オブジェクトをコピーする場合、オブジェクトが大きいと、メモリ コピーのオーバーヘッドが高くなります。

HotSpot 仮想マシンは、-XX:PretenureSizeThreshold パラメーターを提供し、この設定値より大きいオブジェクトが古い世代で直接割り当てられるように指定します。この目的は、Eden 領域と 2 つの Survivor 領域の間でのコピーの往復を回避することです。多数のメモリコピー操作で発生します。

これを行う目的: 1. 大量のメモリ コピーを回避する、2. ガベージ コレクションを事前に回避する、明らかにメモリ割り当て用のスペースがあります。

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

ほとんどの場合、オブジェクトは若い世代の Eden エリアに割り当てられます。Eden 領域に割り当てる十分なスペースがない場合、仮想マシンはマイナー GC を開始します。

長期生存者が高齢者の領域に入る

HotSpot 仮想マシンのほとんどのコレクタは、世代別コレクションを使用してヒープ メモリを管理するため、メモリがリサイクルされるときに、どの生き残ったオブジェクトを若い世代に配置し、どの生き残ったオブジェクトを古い世代に配置するかを決定できる必要があります。これを行うために、仮想マシンはオブジェクトごとにオブジェクト経過時間 (Age) カウンターを定義し、オブジェクト ヘッダーに保存します。

ここに画像の説明を挿入

オブジェクトがエデンで誕生した後の最初のマイナー GC の後もまだ生きていて、サバイバーに収容できる場合、そのオブジェクトはサバイバー空間に移動され、オブジェクトの年齢は 1 に設定されます。オブジェクトが生き残るたびに、 Survivor 領域のマイナー GC では、経過時間が 1 増加し、経過時間が一定のレベル (同時ガベージ コレクターのデフォルト値は 15) まで増加し、CMS が 6 になると、古いバージョンに昇格します。世代。

オブジェクトの年齢の動的決定
誕生の背景

JVM は、-XX:MaxTenuringThreshold パラメータを通じてプロモーション期間を制御します。GC が渡されるたびに、期間は 1 ずつ増加し、最大期間を Old 領域に入力できます。最大値は 15 です (JVM がオブジェクトの年齢を表すために 4 ビットを使用します)。プロモーション基準として固定の MaxTenuringThreshold 値を設定します。次の問題が発生します。

1. MaxTenuringThreshold の設定が大きすぎる場合、昇格する必要があるオブジェクトは、Survivor 領域がオーバーフローするまで Survivor 領域に留まります。オーバーフローが発生すると、Eden + Survivor のオブジェクトは、次の基準に基づいて Old 領域に昇格されなくなります。これがオブジェクトの老化のメカニズムであり、失敗します。

2. MaxTenuringThreshold の設定が小さすぎると、早期に昇格することになり、オブジェクトが Young 領域で完全にリサイクルできなくなり、多数の短期オブジェクトが Old 領域に昇格され、Old 領域のスペースが急速に増加します。頻繁なメジャー GC が発生し、世代別リサイクルの意味が失われ、GC のパフォーマンスに深刻な影響を及ぼします。

解決

さまざまなプログラムのメモリ状態に適切に適応するために、仮想マシンは、古い世代に昇格するためにオブジェクトの年齢が MaxTenuringThreshold に達する必要があるとは限りません。同じ年齢のすべてのオブジェクトのサイズの合計がSurvivor 空間内の年齢が Survivor 空間の半分より大きい場合、この年齢以上のオブジェクトは、MaxTenuringThreshold で必要な年齢を待たずに直接古い年齢に入ることができます。

スペース割り当ての保証

マイナー GC が発生する前に、仮想マシンはまず、古い世代で使用可能な最大連続領域が新しい世代のすべてのオブジェクトの合計領域より大きいかどうかを確認し、この条件が真であれば、マイナー GC で安全性を確保できます。そうでない場合、仮想マシンは HandlePromotionFailure 設定値が保証失敗を許可するかどうかをチェックします。許可されている場合、古い世代で利用可能な最大連続領域が古い世代に昇格されたオブジェクトの平均サイズよりも大きいかどうかのチェックが継続され、それが大きい場合は、マイナー GC の実行が試行されます。保証が失敗した場合、マイナー GC は危険です。フル GC が実行されます。それより小さい場合、または HandlePromotionFailure 設定でリスクが許容されない場合は、この時点でフル GC も実行する必要があります。

おすすめ

転載: blog.csdn.net/qq_43358469/article/details/131391777