1. ガベージ コレクション アルゴリズムの中心的な考え方
Java 言語は、使用中のオブジェクトを追跡し、使用されなくなった (参照された) オブジェクトを検出してリサイクルするためのガベージ コレクション メカニズムを確立しました。このメカニズムにより、動的メモリ割り当てで発生する可能性のある 2 つの危険、つまり過剰なメモリ ガベージによるメモリ枯渇と、不適切なメモリ解放による不正なメモリ参照を効果的に防ぐことができます。
ガベージ コレクション アルゴリズムの中心的な考え方は、仮想マシンの利用可能なメモリ領域、つまりヒープ領域内のオブジェクトを識別することです。オブジェクトが参照されている場合、それは逆にリビング オブジェクトと呼ばれます。オブジェクトが参照されなくなった場合、それはガベージ オブジェクトであり、再割り当てのために占有しているスペースを再利用できます。ガベージ コレクション アルゴリズムの選択とガベージ コレクション システム パラメーターの適切な調整はシステムのパフォーマンスに直接影響するため、開発者はより深い理解を必要とします。
2. メイン GC (ガベージ コレクター) のトリガー条件
JVM は非常に頻繁にセカンダリ GC を実行しますが、この GC にかかる時間は非常に短いため、システムへの影響はほとんどありません。さらに注目に値するのは、メイン GC のトリガー条件です。これはシステムに重大な影響を与えるためです。一般に、メイン GC をトリガーする条件は 2 つあります:
(1) アプリケーションがアイドル状態のとき、つまりアプリケーション スレッドが実行されていないとき、GC が呼び出されます。 GC は最も優先度の低いスレッドで実行されるため、アプリケーションがビジー状態の場合、以下の条件を除いて GC スレッドは呼び出されません。
(2) Javaヒープメモリが不足した場合、GCが呼び出されます。アプリケーション スレッドが実行中で、実行プロセス中に新しいオブジェクトが作成されるとき、その時点でメモリ領域が不十分な場合、JVM は強制的に GC スレッドを呼び出して、新しい割り当てのためにメモリを再利用します。 1 回の GC 後にメモリ割り当て要件を満たすことができない場合、JVM はさらに 2 回の GC 試行を実行し、それでも要件を満たすことができない場合、JVM は「メモリ不足」エラーを報告し、Java アプリケーションは次のことを行います。停止。
メイン GC を実行するかどうかはシステム環境に基づいて JVM によって決定され、システム環境は常に変化するため、メイン GC の動作は不確実であり、いつ必ず発生するかを予測することはできませんが、アプリケーションの場合、メイン GC が繰り返し実行されるため、長期的な運用には必ず必要になります。
3. GC のオーバーヘッドを軽減するための対策
上記の GC メカニズムによれば、プログラムの動作はシステム環境の変化に直接影響を及ぼし、それによって GC のトリガーに影響を与えます。 GC の特性に応じた設計とコーディングを行わないと、メモリの保持などの悪影響が次々と発生します。これらの影響を回避するには、ガベージを減らし、GC プロセスのオーバーヘッドを可能な限り減らすことが基本原則です。具体的な対策としては、以下のような点が挙げられます。
(1) System.gc() を明示的に呼び出さない
この関数は、JVM がメイン GC を実行することを推奨していますが、これは単なる提案であり、保証するものではありません。メイン GC の頻度、つまり断続的な一時停止の回数が増加します。ここで特別な説明が必要なのは、コードに示されている System.gc() の呼び出しが必ずしも GC を実行できるわけではないということです。これは、finalize() メソッド、つまり System.gc() を積極的に呼び出すことで確認できます。毎回、finalize() メソッドが呼び出されるわけではありません。 Finalize() メソッドの特徴は、オブジェクトがリサイクルされる前に最初に Finalize() メソッドが呼び出されることです。
(2) 一時オブジェクトの使用を最小限に抑える
一時変数の使用量を減らすことは、一時オブジェクトの生成を減らすことと同じであり、その結果、上記の 2 番目のトリガー条件が発生するまでの時間が長くなります。 、メインGCの可能性を減らします。
(3) 使用されていないオブジェクトは明示的に Null に設定するのが最善です
一般的に、Null オブジェクトはガベージとして扱われるため、未使用のオブジェクトを明示的に Null に設定すると、GC コレクターがガベージを判断しやすくなります。 GCの効率。
(4) 文字列を蓄積するために String の代わりに StringBuffer を使用してみてください (JAVA の String と StringBuffer については、このブログの別の記事を参照してください)
String は固定長の文字列オブジェクトであるため、String オブジェクトを蓄積するとき、それらはString オブジェクトですが、Str5=Str1+Str2+Str3+Str4 などの新しい String オブジェクトを再作成します。各「+」演算を新しい String オブジェクトで作成する必要があるため、このステートメントの実行中に複数のガベージ オブジェクトが生成されます。 , しかし、これらの遷移オブジェクトはシステムにとって実質的な意味はなく、ゴミが増えるだけです。この状況を回避するには、代わりに StringBuffer を使用して文字列を蓄積します。StringBuffer は可変長であるため、元のベースに基づいて拡張され、中間オブジェクトは生成されません。
(5) Int や Long などの基本型を使用できる場合は、Integer オブジェクトや Long オブジェクトは必要ありません。
基本型変数は、対応するオブジェクトよりも占有するメモリ リソースがはるかに少ないため、必要がない場合は使用するのが最善です。基本的な変数を使用します。整数を使用する必要があるのはどのような場合ですか?
(6) 静的オブジェクト変数はできるだけ使用しないでください
静的変数はグローバル変数であり、GC によってリサイクルされません。常にメモリを占有します。
(7) オブジェクトの作成または削除の時間を分散する
短期間に大量の新しいオブジェクト、特に大きなオブジェクトを集中して作成すると、大量のメモリが必要になる場合があります。この状況では、JVM はメイン GC を実行してメモリを再利用するか、メモリ フラグメントを統合してメイン GC の頻度を高めることしかできません。同じ原則がオブジェクトの集中削除にも当てはまります。これにより、大量のガベージ オブジェクトが突然出現し、空き領域が必然的に減少するため、次回新しいオブジェクトが作成されるときにメイン GC が強制される可能性が大幅に高まります。
4. ガベージ コレクション アルゴリズム
(1) 参照カウント コレクター
参照カウントはガベージ コレクションの初期の戦略です。このアプローチでは、ヒープ内のすべてのオブジェクトに参照カウントがあります。オブジェクトが作成され、そのオブジェクトへの参照が変数に割り当てられると、オブジェクトの参照カウントは 1 に設定されます。たとえば、新しいオブジェクト A a=new A(); を作成すると、a が別の変数 b に割り当てられます (つまり、b=a)。オブジェクト a の参照カウントは +1 になります。他の変数にこのオブジェクトへの参照が割り当てられると、カウントは 1 つ増加します。オブジェクトへの参照が有効期間を超えるか、新しい値に設定されると、オブジェクトの参照カウントは 1 つ減ります。たとえば、b=c の場合、a の参照カウントは -1 になります。参照カウントが 0 のオブジェクトはすべてガベージ コレクションの対象になります。オブジェクトがガベージ コレクションされると、そのオブジェクトが参照するオブジェクトの数は 1 つ減ります。このアプローチでは、1 つのオブジェクトのガベージ コレクションが、その後の他のオブジェクトのガベージ コレクション アクションにつながる可能性があります。たとえば、A a=new A();b=a; b がガベージ コレクションされると、a の参照カウントが 0 になり、a もガベージ コレクションされます。
この方法の利点: 参照カウント コレクターは迅速に実行でき、プログラムの実行と連動します。このリマインダーは、プログラムを長時間中断できないリアルタイム環境で役立ちます。
メソッドの欠点: 参照カウントではサイクル (つまり、2 つ以上のオブジェクトが相互参照) を検出できません。ループの例としては、親オブジェクトが子オブジェクトへの参照を持ち、さらに子オブジェクトが親オブジェクトを参照する場合があります。このように、実行中のプログラムのルート オブジェクトからオブジェクト ユーザーに到達できなくなったとしても、オブジェクト ユーザーのカウントが 0 になることはあり得ません。もう 1 つの欠点は、参照カウントが増加または減少するたびに追加のオーバーヘッドが発生することです。
(2) Trace Collector
ガベージ検出は通常、ルート オブジェクトのコレクションを構築し、これらのルート オブジェクトから開始して到達可能性をチェックすることによって実装されます。実行中のプログラムがアクセスできるルート オブジェクトとオブジェクトとの間に参照パスが存在する場合、オブジェクトは到達可能です。ルート オブジェクトはプログラムから常にアクセス可能です。これらのルート オブジェクトから始まり、タッチできるオブジェクトはすべて「アクティブ」オブジェクトとみなされます。触れることができないオブジェクトは、今後のプログラムの実行に影響を与えないため、ガベージとみなされます。
トレース コレクターは、ルート ノードから開始してオブジェクト参照グラフを追跡します。追跡中に遭遇したオブジェクトには、特定の方法でマークが付けられます。一般に、オブジェクト自体にマーカーを設定するか、別のビットマップを使用してマーカーを設定します。追跡が終了すると、マークされていないオブジェクトは到達できなくなり、収集できるようになります。
基本的な追跡アルゴリズムは「マークアンドクリア」と呼ばれます。この名前は、ジャンク電話の 2 つの段階を示しています。マーキング フェーズでは、ガベージ コレクターは参照ツリーを走査し、検出された各オブジェクトをマークします。クリーンアップ フェーズでは、マークされていないオブジェクトが解放され、オブジェクトの解放後に取得されたメモリが実行プログラムに返されます。 Java 仮想マシンでは、クリーンアップ ステップにオブジェクトのファイナライズを含める必要があります。
Java ガベージ コレクションと関連記事のさらに詳細な説明については、PHP 中国語 Web サイトに注目してください。