Java 仮想マシンの詳細な説明 ---- GC アルゴリズムとタイプ

Java 仮想マシンの詳細な説明 ---- GC アルゴリズムとタイプ

この記事の主な内容:

  • GCの概念

  • GCアルゴリズム

参照カウント方式(循環参照の問題を解決できず、Javaでは採用されていない)

ルート探索アルゴリズム

最新の仮想マシンにおけるガベージ コレクション アルゴリズム:

マークスイープ

レプリケーションアルゴリズム(新世代)

マークコンパクト(旧世代)

世代別コレクション

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

1. GCの概念:

  • GC:ガベージコレクション

  • Lispは1960年にGCを使用した

  • Javaでは、 GCの対象はJavaヒープとメソッド領域(つまり***領域)です

上記の3つの文を一つずつ説明してみましょう。

(1)GC:ガベージコレクションここで言うガベージとは、システムの動作中に生成される役に立たないオブジェクトのことを指します。これらのオブジェクトは一定量のメモリ空間を占有します。長期間解放されない場合、OOM を引き起こす可能性があります

C/C++ では、メモリ空間の適用、管理、解放はプログラマーの責任であるため、GC の概念はありません。 Java では、バックグラウンドでガベージ コレクション専用の特別なスレッドが監視およびスキャンし、一部の無駄なメモリを自動的に解放します。これが、プログラマーによって導入された人為的なメモリ リークを防ぐことを目的としたガベージ コレクションの基本的な考え方です。

(2)実はGCの歴史はJavaよりも長い。1960年にMITで誕生したLispは、動的メモリ割り当てとガベージコレクション技術を本格的に活用した最初の言語である。 Lisp がまだ初期段階にあったとき、人々は GC が達成する必要のある 3 つのことについて考えていました。

どのメモリを再利用する必要がありますか?

いつリサイクルするのですか?

リサイクル方法は?

(3)メモリ領域内のプログラムカウンタ、仮想マシンスタック、ローカルメソッドスタックはスレッドによって生成・破棄される。スタック内のスタックフレームはメソッドの出入りに応じて順番にポップ・プッシュ操作を行う。各スタックフレームに割り当てられるメモリ量は基本的にクラス構造が決まれば分かるメソッドが終了するかスレッドが終了するとメモリは自然にリサイクルされるため、これらの領域ではリサイクルの問題をあまり考慮する必要はありません

Java ヒープとメソッド領域は異なります。インターフェース内の複数の実装クラスに必要なメモリは異なる場合がありますし、メソッド内の複数の分岐に必要なメモリも異なる場合があります。プログラムの実行時にどのオブジェクトが作成されるかしかわかりません。この部分のメモリの割り当てと回復は動的であり、GC もこの部分のメモリに重点を置いています。以下の記事で「メモリ」の割り当てと回復が関係している場合、それはメモリの一部のみを指します。

#p#

2. 参照カウント アルゴリズム: (古いガベージ コレクション アルゴリズム。循環参照を処理できず、Java では採用されていません)

1. 参照カウントアルゴリズムの概念:

オブジェクトに参照カウンターを追加します。どこかから参照されるたびに、カウンターの値が 1 増加します。参照が無効になると、カウンターの値が 1 減少します。カウンターが 0 になっているオブジェクトは、使用できなくなります。

2. ユーザー例:

参照カウントアルゴリズムは実装が簡単で、判断効率が高く、ほとんどの場合に適したアルゴリズムです。多くの場所で応用されています。例えば:

Microsoft の COM テクノロジ: コンピュータ オブジェクト モデル

ActionScript 3 を使用した Flash Player

パイソン

しかし、主流の Java 仮想マシンでは、メモリ管理に参照カウント アルゴリズムは使用されていません。主な理由は、オブジェクト間の循環参照の問題を解決するのが難しいためです。

3. 参照カウントアルゴリズムの問​​題:

  • 参照と参照解除には加算と減算が伴い、パフォーマンスに影響します。

  • 致命的な欠陥:循環参照されたオブジェクトはリサイクルできない

上記の 3 つの図のうち、右端の図では、循環参照のカウンターは 0 ではありませんが、ルート オブジェクトに到達できず、解放もできません。

循環参照コードの例:

  1. 公共 クラスオブジェクト{
  2.  
  3. オブジェクトフィールド = null ;
  4.      
  5. 公共 静的  void main(String[] args) {
  6. スレッド thread = new Thread( new Runnable() {
  7. 公共  void実行() {
  8. オブジェクト objectA =新しいObject();
  9. Object objectB = new Object(); //位置1  
  10. オブジェクトA.フィールド = オブジェクトB;
  11. objectB.field = objectA; //位置2  
  12. //何かをする 
  13. オブジェクトA = null ;
  14. objectB = null ; //位置3  
  15. }
  16. });
  17. スレッドを開始します。
  18. while ();
  19. }
  20.      
  21. }

上記のコードは少し不自然に見えますが、実際のプログラミングでは、1 対 1 の関係にある 2 つのデータベース オブジェクトがそれぞれ他方への参照を保持するなど、よく発生します。 ***ループは JVM が終了しないようにするためのものであり、実用的な意味はありません。

コードの説明:

コードには 1、2、3 の数字がマークされています。位置 1 のステートメントが実行されると、2 つのオブジェクトの参照カウントは両方とも 1 になります。位置 2 のステートメントが実行されると、両方のオブジェクトの参照カウントは 2 になります。位置 3 のステートメントが実行された後、つまり両方が null 値に設定された後、2 つの参照カウントは依然として 1 のままです。参照カウント アルゴリズムのリサイクル ルールによれば、参照カウントは 0 に戻るまでリサイクルされません。

現在使用している GC では、スレッドが終了すると、objectA と objectB の両方がリサイクルされるオブジェクトとして扱われます。 GC が上記の参照カウント アルゴリズムを採用している場合、使用後にオブジェクトを明示的に null 値に設定しても、これら 2 つのオブジェクトはリサイクルされません

#p#

3. ルート検索アルゴリズム:

1. ルート探索アルゴリズムの概念:

参照カウント アルゴリズムの欠陥のため、JVM では一般に、ルート検索アルゴリズムと呼ばれる新しいアルゴリズムが採用されています。これを処理する方法は、複数のルート オブジェクトを設定することです。特定のオブジェクトがいずれかのルート オブジェクトに到達できない場合、そのオブジェクトはリサイクル可能と見なされます

上図に示すように、ObjectD と ObjectE は互いに関連しています。ただし、これら 2 つのオブジェクトには GC ルートが到達できないため、D と E は最終的に GC オブジェクトとして扱われます。上図で参照カウント方式を使用すると、5 つのオブジェクト A から E はいずれもリサイクルされません。

2. アクセシビリティ分析:

先ほど、複数のルート オブジェクトを設定したことを説明しました。特定のオブジェクトがいずれかのルート オブジェクトに到達できない場合、そのオブジェクトはリサイクル可能とみなされます。後でマークスイープアルゴリズム/マークコンパクトアルゴリズムを紹介するときには、ルートノードから始めて、到達可能なすべてのオブジェクトを一度マークすることを常に強調します。では、到達可能とはどういう意味でしょうか?説明は次のとおりです。

到達可能性分析:

ルート (GC ルート) オブジェクトから開始して、下方向への検索が開始されます。検索がたどるパスは参照チェーン」と呼ばれます。オブジェクトを GC ルートに接続する参照チェーンがない場合 (グラフ理論では、オブジェクトが GC ルートから到達できないことを意味します)、オブジェクトが使用できないことが証明されます。

3. GC ルーツ:

GC ルートについて言えば、Java 言語では、次のオブジェクトを GC ルートとして使用できます。

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

2. メソッド領域内の静的メンバー。

3. メソッド領域内の定数によって参照されるオブジェクト(グローバル変数)

4. ローカル メソッド スタック内の JNI によって参照されるオブジェクト (一般にネイティブ メソッドと呼ばれる)。

注: 1 番目と 4 番目のタイプはどちらもメソッドのローカル変数テーブルを参照します。2 番目のタイプの方が意味が明確で、3 番目のタイプは主に final として宣言された定数値を参照します。

ルート検索アルゴリズムに基づいて、最新の仮想マシンの実装には、マークスイープ アルゴリズム、コピー アルゴリズム、マークコンパクトアルゴリズムという 3 つの主要なガベージ コレクション アルゴリズムがあります。これら 3 つのアルゴリズムはすべてルート検索アルゴリズムを拡張したものですが、それでも非常に理解しやすいものです。

#p#

4. マークスイープアルゴリズム:

1. マークスイープアルゴリズムの概念:

マークアンドスイープアルゴリズムは、現代のガベージコレクションアルゴリズムの思想的基礎です。マークスイープ アルゴリズムは、ガベージ コレクションをマーキング フェーズとスイープ フェーズの 2 つのフェーズに分割します。 1 つの可能な実装としては、まずマーキング フェーズでルート ノードを通過し、ルート ノードから到達可能なすべてのオブジェクトをマークすることが挙げられます。したがって、マークされていないオブジェクトは参照されていないガベージ オブジェクトであり、クリーンアップ フェーズでは、マークされていないすべてのオブジェクトがクリーンアップされます。

2. マークスイープアルゴリズムの詳細な説明:

ヒープ内の使用可能なメモリが使い果たされると、プログラム全体を停止し (ストップ ザ ワールドとも呼ばれます)、1 つ目はマーキング、2 つ目はクリアという 2 つのタスクを実行します。

  • マーキング: マーキング プロセスは、実際にはすべての GC ルートをトラバースし、GC ルートから到達可能なすべてのオブジェクトをライブ オブジェクトとしてマークすることです。

  • クリア: クリア プロセスはヒープ内のすべてのオブジェクトを走査し、マークされていないすべてのオブジェクトをクリアします

つまり、プログラムの実行中に使用可能なメモリが使い果たされると、GC スレッドがトリガーされ、プログラムが一時停止されます。次に、まだ生きているオブジェクトがマークされ、最後にヒープ内のマークされていないオブジェクトがすべてクリアされ、プログラムが再開されます

次の画像を見てください。

上図は、プログラム実行中のすべてのオブジェクトの状態を示しています。それらのフラグはすべて 0 (つまり、マークなし) です。以下のデフォルト値は、マークなしの場合は 0、マーク付きの場合は 1 です。有効なメモリ領域が使い果たされたと仮定すると、JVM はアプリケーションを停止して GC スレッドを開始し、その後マーキングを開始します。ルート検索アルゴリズムによると、マーキング後のオブジェクトの状態は次のようになります。

上の図からわかるように、ルート検索アルゴリズムによれば、ルート オブジェクトから到達可能なすべてのオブジェクトが存続オブジェクトとしてマークされ、*** ステージのマーキングが完了しています。次に、クリーニングの第 2 段階を実行します。クリーニング後、残っているオブジェクトとそのステータスは次の図のようになります。

上の図からわかるように、マークされていないオブジェクトはリサイクルされてクリアされますが、マークされたオブジェクトは残り、そのマーク ビットは 0 にリセットされます。言うまでもなく、停止したプログラム スレッドを起動して、プログラムの実行を継続するだけです。

質問: なぜプログラムを停止しなければならないのですか?

答え:

これは実際には理解するのが難しくありません。プログラムが GC スレッドと一緒に実行されると仮定して、次のようなシナリオを想像してください。

図の右端のオブジェクトをマークしたとします。これを A と呼びます。その結果、プログラム内に新しいオブジェクト B が作成され、A オブジェクトは B オブジェクトに到達できるようになります。ただし、この時点でオブジェクト A のマーキングは完了していますが、オブジェクト B はマーキング段階を逃したため、マーク ビットはまだ 0 のままです。したがって、クリーンアップフェーズになると、新しいオブジェクト B は削除されます。この場合、GC スレッドによってプログラムが正常に動作しなくなるという結果になることは想像に難くありません。

上記の結果は、もちろん受け入れられません。新しいオブジェクトを作成したばかりですが、GC 後に突然 null になりました。どうすれば再生できるでしょうか?

3. マークアンドスイープアルゴリズムの欠点:

(1)まず、その欠点は、比較的非効率(再帰とフルヒープオブジェクトのトラバーサル)であり、その結果、ストップザワールド時間が比較的長くなり、特に対話型アプリケーションでは受け入れられないということです。ウェブサイトでゲームをプレイしていて、1 時間のうち 5 分間だけウェブサイトがダウンしたとしたら、それでもプレイしますか?

(2)2つ目の大きな欠点は、この方法でクリアされた空きメモリが不連続であることです。これは理解しにくいことではありません。死んだオブジェクトはメモリのさまざまな隅にランダムに現れます。これらがクリアされると、メモリレイアウトは当然乱れます。これに対処するために、JVM はメモリの空きリストを維持する必要があり、これが別のオーバーヘッドになります。さらに、配列オブジェクトを割り当てるときに、連続したメモリ領域を見つけるのは簡単ではありません。

#p#

5. コピーアルゴリズム: (新世代のGC)

レプリケーションアルゴリズムの概念:

本来のメモリ空間は2つのブロックに分割され、一度にどちらか一方のみが使用される。ガベージコレクション中、使用中のメモリ内の生き残ったオブジェクトは未使用のメモリブロックにコピーされる。その後、使用中のメモリブロック内のオブジェクトがすべてクリアされ、2つのメモリの役割が交換され、ガベージコレクションは完了する

  • マークアンドスイープアルゴリズムと比較すると、コピーアルゴリズムは比較的効率的なリサイクル方法です。

  • 古い世代のように、多数のオブジェクトが残っている状況には適していません(コピーアルゴリズムは新しい世代のGCに適しています

  • コピーアルゴリズムの最大の問題は、スペースの無駄遣いである。

コピー アルゴリズムにより、毎回半分の領域全体のみが再利用されます。メモリを割り当てるときに、メモリの断片化などの複雑な状況を考慮する必要はありません。ヒープの先頭ポインタを移動し、順番にメモリを割り当てるだけです。実装が簡単で、実行が効率的です。しかし、このアルゴリズムのコストは、メモリを元のサイズの半分に削減することであり、これは非常にひどいです。

したがって、上記の説明から、レプリケーション アルゴリズムを使用するには、オブジェクトの生存率が少なくとも非常に低くなければならず、最も重要なのは、50% のメモリの無駄を克服する必要があることがわかりにくいわけではありません。

現在の商用仮想マシンはすべて、このコレクション アルゴリズムを使用して新しい世代をリサイクルします。新しい世代のオブジェクトの 98% は「一夜にして生まれて死ぬ」ため、メモリ空間を 1:1 の比率で分割する必要はありません。代わりに、メモリは、より大きな Eden 空間と 2 つのより小さな Survivor 空間に分割され、毎回 Eden と Survivor 空間の 1 つが使用されます。リサイクルするときは、Eden と Survivor 内の生き残ったオブジェクトを一度に別の Survivor スペースにコピーし、最後に使用した Eden と Survivor スペースをクリーンアップします。 HotSpot 仮想マシンの Eden と Survivor のデフォルトの比率は 8:1 です。つまり、各新世代で使用可能なメモリ領域は、新世代全体の容量の 90% (80% + 10%) であり、無駄になるのは領域の 10% だけです。

もちろん、オブジェクトの 98% は一般的なシナリオでのみリサイクルできます。リサイクルごとに 10% を超えるオブジェクトが残らないことを保証することはできません。Survivor スペースが不十分な場合は、割り当て保証のために古い世代に依存する必要があるため、大きなオブジェクトは直接古い世代に移動します。全体のプロセスを下の図に示します。

上の図では、緑の矢印の位置は大きなオブジェクトを表しており、これは直接古い世代に入ります。

上記のレプリケーション アルゴリズムに従って、次の gc ログの数字を見てみましょう。理解できるはずです。

上記の GC ログでは、新しい世代の使用可能なスペースは 13824K (Eden 領域の 12288K + From スペースの 1536K) です。メモリアドレス計算によると、新世代の合計スペースは 15M であり、この 15M スペースは = 13824K + 1536K のスペースです。

#p#

6. マークコンパクトアルゴリズム: (旧世代の GC)

輸入:

オブジェクトの生存率が高いときにコピー操作をさらに実行すると、効率が低下します。さらに重要なのは、スペースの 50% を無駄にしたくない場合は、使用済みメモリ内のすべてのオブジェクトが 100% 有効であるという極端な状況に対処するために、割り当て保証のための追加のスペースが必要になるため、このアルゴリズムは古い世代では直接選択できないことです。

コンセプト:

マーク圧縮アルゴリズムは、古い世代など、残存するオブジェクトが多数ある状況に適しています。マークアンドスイープアルゴリズムに基づいていくつかの最適化を行います。マーク アンド スイープ アルゴリズムと同様に、マーク アンド 圧縮アルゴリズムも最初にルート ノードから開始して、到達可能なすべてのオブジェクトを 1 回マークする必要があります。ただし、その後は、マークされていないオブジェクトを単にクリーンアップするのではなく、残っているすべてのオブジェクトをメモリの一方の端に圧縮し、境界の外側のすべてのスペースをクリーンアップします

  • マーキング: 最初のフェーズはマーク/スイープ アルゴリズムとまったく同じで、GC ルートを走査し、生き残ったオブジェクトをマークします。

  • 配置: 残っているすべてのオブジェクトを移動し、メモリ アドレスの順に配置してから、終了メモリ アドレス以降のすべてのメモリをリサイクルします。したがって、第 2 段階はソート段階と呼ばれます。

上の図からわかるように、マークされたライブ オブジェクトはメモリ アドレスに従って並べ替えられ、整列されますが、マークされていないメモリはクリーンアップされます。このように、新しいオブジェクトにメモリを割り当てる必要がある場合、JVM はメモリの開始アドレスのみを保持すればよく、空きリストを維持する場合よりもオーバーヘッドがはるかに少なくなります。

マーク/スイープ アルゴリズムは、マーク/スイープ アルゴリズムの分散メモリ領域の欠点を補うだけでなく、コピー アルゴリズムでメモリを半分にすることによる高コストも排除します

  • ただし、マーク/スイープ アルゴリズムの唯一の欠点は、あまり効率的ではないことです。

すべてのライブ オブジェクトをマークする必要があるだけでなく、すべてのライブ オブジェクトの参照アドレスを整理する必要もあります。効率の点では、マーク/スイープ アルゴリズムはコピー アルゴリズムよりも低くなります。

マークスイープアルゴリズム、コピーアルゴリズム、マークコンパクトアルゴリズムの概要:

これら 3 つのアルゴリズムはすべて、オブジェクトをリサイクルする必要があるかどうかを判断するルート検索アルゴリズムに基づいています。ルート検索アルゴリズムの正常な動作の理論的根拠は、文法内の変数スコープの関連コンテンツです。したがって、メモリ リークを防ぐためには、変数のスコープを習得することが最も基本的な方法であり、C/C++ スタイルのメモリ管理は使用しないでください。

GC スレッドが開始されるか、GC プロセスが開始すると、それらはすべてアプリケーションを一時停止します (世界を停止します)。

それらの違いは次のとおりです: (> は前者が後者よりも優れていることを意味し、= は 2 つが同じ効果を持つことを意味します)

(1)効率コピーアルゴリズム>マーク/スイープアルゴリズム>マーク/スイープアルゴリズム(ここでの効率は、単に時間計算量の単純な比較であり、実際の状況では必ずしも当てはまらない。)

(2)メモリの整頓性:コピーアルゴリズム=マーク/スイープアルゴリズム>マーク/スイープアルゴリズム。

(3)メモリ使用率:マーク/スイープアルゴリズム=マーク/スイープアルゴリズム>コピーアルゴリズム。

注 1: マーク/スイープ アルゴリズムは比較的後進的なアルゴリズムであることがわかりますが、後者の 2 つのアルゴリズムはこれを基に構築されています。

注 2:時間と空間の両方を持つことはできません

#p#

7. 世代別コレクションアルゴリズム: (新世代の GC + 旧世代の GC)

現在の商用仮想マシンの GC はすべて「世代別コレクション アルゴリズム」を使用していますが、これは新しいアイデアではありません。オブジェクトの異なる生存サイクルに応じてメモリを複数のブロックに分割するだけです。一般的に、Java ヒープは新しい世代と古い世代に分かれており、寿命の短いオブジェクトは新しい世代に分類され、寿命の長いオブジェクトは古い世代に分類されます

  • 少数のオブジェクトが生き残り、レプリケーション アルゴリズムに適している:新しい世代では、 GC を実行するたびに多数のオブジェクトが死に、少数のオブジェクトのみが生き残ります。 この場合、レプリケーション アルゴリズムが使用され、生き残った少数のオブジェクトのレプリケーション コストのみを支払うことで GC を完了できます。

  • 多数のオブジェクトが生き残るため、mark-sweep/mark-compact が適しています古い世代では、オブジェクトの生存率が高く、割り当てる追加のスペースがないため、GC には「 mark-sweep 」/「mark-compact」アルゴリズムを使用する必要があります。

注:古い世代のオブジェクトの中には、新しい世代のコレクション中に保証として入ってくるオブジェクトが少数あります。ほとんどのオブジェクトは、多くの GC の後でもリサイクルされなかったために古い世代に入ります

8. アクセシビリティ:

すべてのアルゴリズムはガベージ オブジェクトを識別できる必要があるため、到達可能性の定義が必要です。

到達可能:

このオブジェクトはルート ノードからアクセスできます。

実際には、ルート ノードからスキャンするだけです。オブジェクトが参照チェーン内にある限り、到達可能です。

復活可能:

すべての参照が解放されると、復活可能な状態になります

オブジェクトはfinalize()で復活する可能性があるため

アンタッチャブル:

finalize()の後、到達不能な状態になる可能性がある

触れられないものは復活できない

リサイクルします。

オブジェクトを復活させる finalize メソッドのコード例:

  1. パッケージtest03;
  2.  
  3. /**
  4. * 2015/8/19にsmyhvaeによって作成されました。
  5. */  
  6. 公共 クラスCanReliveObj {
  7. 公共 静的CanReliveObj オブジェクト;
  8.  
  9. //GCが実行されると、finalizeメソッドが実行され、1回だけ実行されます。  
  10. @オーバーライド 
  11. 保護された  void finalize()は Throwableをスローします{
  12. スーパー.finalize();
  13. System.out.println( "CanReliveObj の終了処理が呼び出されました" );
  14. obj = this ; //GCが実行されるとfinalizeメソッドが実行され、このコード行の目的はnullオブジェクトを復活させてアクセス可能にすることです 
  15. }
  16.  
  17. @オーバーライド 
  18. パブリック文字列toString() {
  19. 戻る  「私はCanReliveObjです」 ;
  20. }
  21.  
  22. 公共 静的  void main(String[] args)はスローします 
  23. 中断例外 {
  24. obj =新しいCanReliveObj();
  25. obj = null ; // 復活できる 
  26. System.out.println( "***番目のGC" );
  27. システム.gc();
  28. スレッド.スリープ( 1000 );
  29. (オブジェクトがnull場合){
  30. System.out.println( "obj は null です" );
  31. }それ以外{
  32. System.out.println( "objは利用可能です" );
  33. }
  34. obj = null ; //復活できません 
  35. System.out.println( "2番目のGC" );
  36. システム.gc();
  37. スレッド.スリープ( 1000 );
  38. (オブジェクトがnull場合){
  39. System.out.println( "obj は null です" );
  40. }それ以外{
  41. System.out.println( "objは利用可能です" );
  42. }
  43. }
  44. }

14行目のコメントに注意する必要があります。最初は、25 行目で obj を null に設定し、GC を実行します。obj はリサイクルされると考えましたが、そうではありませんでした。GC 中に 11 行目の finalize メソッドが呼び出され、その後 14 行目で obj が復活するからです。そして34行目でobjがnullに設定されGCが実行されます。このときfinalizeメソッドは1回しか実行されないためobjはリサイクルされます。

finalize メソッドの使用の概要:

  • 経験: 不注意な操作によってエラーが発生する可能性があるため、 finalize() の使用は避けてください

  • 優先度が低く、いつ呼び出されるか不明

GC がいつ発生するかは不明なので、当然 finalize メソッドがいつ実行されるかも不明です。

  • リソースを解放するために finalize を使用する場合は、代わりに try-catch-finally を使用できます。

#p#

9. ストップ・ザ・ワールド:

1. ストップ・ザ・ワールドコンセプト:

Java におけるグローバル一時停止現象。

グローバル一時停止、すべての Java コードが停止、ネイティブ コードは実行できるが JVM と対話できない

ほとんどの場合、これは GC によって発生します

いくつかのケースでは、ダンプ スレッド、デッドロック チェック、ヒープ ダンプなどの他の状況によって発生します。

2. GC 中にグローバル一時停止が発生するのはなぜですか?

たとえば、パーティーにいて、突然GCが部屋を掃除しに来たとします。パーティーは散らかっていて、新しいゴミが出ます。部屋はいつまでたっても掃除できません。全員が活動を止めて初めて部屋を掃除することができます。

さらに、グローバル一時停止がない場合、GC スレッドに大きな負担がかかり、GC アルゴリズムの難易度が上がります。GC が何がガベージであるかを判断することが困難になります。

3.ストップ・ザ・ワールドの危険性:

サービスのダウンタイムが長く、応答がない

HA システムに遭遇すると、アクティブ/スタンバイの切り替えが発生し、実稼働環境に重大な危険をもたらす可能性があります。

注: HA: High Availability (高可用性) の高可用性クラスター。

たとえば、上記のマスター マシンとスタンバイ マシンの場合、マスターは現在動作しています。マスターが GC を実行して長時間の一時停止が発生すると、スタンバイ マシンはマスターが動作していないことを検出し、スタンバイ マシンが動作を開始します。ただし、ホストは一時的に動作していないだけです。GC が終了すると、ホストは再び動作を開始します。この場合、マスター マシンとスタンバイ マシンは同時に動作しています。メインマシンとバックアップマシンが同時に動作することは、実際には非常に危険です。アプリケーションの不整合や正常なサービスの提供の失敗が発生する可能性が高く、本番環境に影響を及ぼします。

コード例:

(1)ログを印刷するためのコード:(100msごとに1つ印刷)

  1. 公共 静的 クラスPrintThread はThreadを拡張します{
  2. 公共 静的 ファイナル 長い開始時間 = System.currentTimeMillis();
  3. @オーバーライド 
  4. 公共  void実行(){
  5. 試す{
  6. の間{
  7. long t=System.currentTimeMillis()-開始時間;
  8. System.out.println( "時間:" +t);
  9. スレッド.スリープ( 100 );
  10. }
  11. }キャッチ(例外 e) {
  12.  
  13. }
  14. }
  15. }

上記のコードは、ログを印刷し、100 ミリ秒ごとに 1 つ印刷して、印刷時間を計算します。

(2)作業スレッドのコード:(特にメモリを消費するために使用される作業スレッド)

  1. 公共 静的 クラスMyThread はThreadを拡張します{
  2. HashMap<Long, byte []> map =新しいHashMap<Long, byte []>();
  3. @オーバーライド 
  4. 公共  void実行(){
  5. 試す{
  6. の間{
  7. if (map.size()* 512 / 1024 / 1024 >= 450 ){ //マップによって消費されるメモリが450より大きい場合は、メモリをクリーンアップします 
  8. System.out.println( "=====クリーンアップの準備ができました=====:" +map.size());
  9. マップをクリアします。
  10. }
  11.  
  12. ( int i = 0 ; i < 1024 ; i++) {
  13. map.put(System.nanoTime(),新しい バイト[ 512 ]);
  14. }
  15. スレッド.スリープ( 1 );
  16. }
  17. }キャッチ(例外 e) {
  18. e.printStackTrace();
  19. }
  20. }
  21. }

次に、gc パラメータを次のように設定します。

-Xmx512M -Xms512M -XX:+UseSerialGC -Xloggc:gc.log -XX:+PrintGCDetails -Xmn1m -XX:PretenureSizeThreshold=50 -XX:MaxTenuringThreshold=1

印刷ログは次のとおりです。

上の図では、赤いフォントは進行中の GC を表しています。論理的には 100 ミリ秒ごとにログが出力されるはずですが、GC が実行されるとグループが一時停止し、時間どおりに出力できなくなります。

【声明】

転載は歓迎しますが、記事の元のソースを維持してください→_→

人生No.1: http://www.cnblogs.com/smyhvae/

出典: http://www.cnblogs.com/smyhvae/p/4744233.html

連絡先: [email protected]

<<:  さまざまなソートアルゴリズムの概要

>>:  インターフェース開発にアルゴリズムは必要ないなんて誰が言ったのでしょうか?

ブログ    
ブログ    
ブログ    
ブログ    

推薦する

ハイテク:米国は1キロメートル以内のターゲット認識を実現する長距離顔認識システムを開発

海外メディアの報道によると、最近「ニューサイエンス」誌に次のような記事が掲載された。 「米軍は1キロ...

公開鍵暗号を解読することは可能ですか?

Q: 正しくインストールされ、操作されていれば、公開鍵インフラストラクチャ (PKI) は破られな...

北京の自動運転路上試験、安全走行距離が300万キロ超え

IT Homeは5月30日、新華社通信が伝えたところによると、記者が29日に北京市インテリジェント車...

在庫 | 今年の世界の AI 事情

​​​ [[253255]]​​ 1. 2018 年の世界の AI 業界の発展は非常に爆発的でした。...

...

予想外だが妥当: ガートナーの 2020 年データ サイエンスおよび機械学習プラットフォームのマジック クアドラントの解釈

最近、ガートナーはデータ サイエンスおよび機械学習 (DSML) プラットフォームに関するマジック ...

...

機械学習におけるこれらの中核的な問題は、数学を知らなくても解決できます。

機械学習や人工知能の分野で最も重要なトピックをわかりやすく説明するにはどうすればよいでしょうか?人工...

KServe、Kubernetes環境に基づく高度にスケーラブルな機械学習デプロイメントツール

ChatGPT のリリースにより、機械学習技術の活用を避けることがますます難しくなってきています。メ...

機械学習が金融サイバー犯罪と戦う方法: 人工知能はセキュリティの洞察にとって不可欠

過去数か月間、コロナウイルス関連の請求による多大なストレスの期間中、失業保険制度から数百万ドルが盗ま...

ドローンは倉庫・物流業界の発展をどのように加速させているのでしょうか?

屋内ドローンは、新しい未知の市場でどのようにその有用性を証明できるでしょうか?ドローンは無人自律航空...

PillarNeSt: Pillar ベースの 3D オブジェクト検出のパフォーマンスをさらに向上させるにはどうすればよいでしょうか?

著者の個人的な理解に基づいて書かれた現在、自動運転の分野では、点群データを収集するためのLIDARセ...

2021年に予測される6つのテクノロジートレンド

2020年、COVID-19パンデミックは世界各国の経済に壊滅的な影響を及ぼし、業界を問わずビジネス...

自動運転までどれくらい遠いのでしょうか?

[[412592]] 2021年、北京では初めて規制に従って無人配送車両の公道走行が許可された。写...

人工知能温度測定が「スタンドガード」に登場!立ち止まる必要がなく、複数人が同時に温度を測定できます

この期間中、自宅に留まっている人々は、定期的にスーパーマーケットに行って商品を購入するという問題にも...