この記事では、JVMパフォーマンスチューニングについて詳しく説明します。

この記事では、JVMパフォーマンスチューニングについて詳しく説明します。

1. JVMチューニングの概念

データ型

Java 仮想マシンでは、データ型はプリミティブ型と参照型の 2 つのカテゴリに分けられます。基本型の変数には元の値が格納されます。つまり、それが表す値は値そのものです。参照型の変数は参照値を格納します。 「参照値」は、オブジェクト自体ではなく、オブジェクトへの参照を表します。オブジェクト自体は、この参照値によって表されるアドレスに格納されます。

基本的な型には、byte、short、int、long、char、float、double、Boolean、returnAddress などがあります。

参照型には、クラス型、インターフェース型、配列が含まれます。

ヒープとスタック

ヒープとスタックはプログラム動作の鍵となるため、その関係を明確に説明する必要があります。

この記事では、JVMパフォーマンスチューニングの詳細な理解と、JVMチューニングの包括的な概要を説明します。

Java オブジェクトのサイズ

基本データ型のサイズは固定されているため、ここでは詳細には触れません。非プリミティブ Java オブジェクトの場合、そのサイズは疑問です。

Java では、空の Object オブジェクトのサイズは 8 バイトで、これはヒープ内に属性がないオブジェクトのサイズと同じです。次の文を見てください。

  1. オブジェクト ob = 新しいオブジェクト();

これにより、プログラム内の Java オブジェクトの寿命は完了しますが、占有するスペースは 4 バイト + 8 バイトになります。 4 バイトは、上記のセクションで説明した Java スタックに参照を格納するために必要なスペースです。 8 バイトは Java ヒープ内のオブジェクトの情報です。すべての Java 非基本型オブジェクトはデフォルトで Object オブジェクトを継承する必要があるため、Java オブジェクトの種類に関係なく、そのサイズは 8 バイトより大きくする必要があります。

Object オブジェクトのサイズを使用して、他のオブジェクトのサイズを計算できます。

  1. クラス NewObject {
  2. 整数 カウント;
  3. ブールフラグ;
  4. オブジェクト ob;
  5. }

サイズは、空のオブジェクトのサイズ (8 バイト) + int のサイズ (4 バイト) + ブール値のサイズ (1 バイト) + 空のオブジェクトの参照のサイズ (4 バイト) = 17 バイトです。ただし、Java はオブジェクト メモリを 8 の倍数で割り当てるため、17 バイトより大きい最も近い 8 の倍数は 24 であり、このオブジェクトのサイズは 24 バイトになります。

ここで注意しなければならないのは、基本タイプの梱包形態の大きさです。これらのパッケージ タイプはオブジェクトになっているため、そのように扱う必要があります。ラッパー タイプのサイズは少なくとも 12 バイト (空のオブジェクトを宣言するために必要な最小スペース) であり、12 バイトには有効な情報が含まれません。同時に、Java オブジェクトのサイズは 8 の整数倍であるため、基本型ラッパー クラスのサイズは少なくとも 16 バイトになります。このメモリ使用量は非常に恐ろしいです。基本型の使用回数の N 倍 (N>2) になります。いくつかのタイプのメモリ使用量はさらに誇張されています (考えてみてください)。したがって、ラッパー クラスはできる限り使用しないようにする必要があります。 JDK5.0 以降では、自動型変換が追加されたため、Java 仮想マシンはストレージ内で対応する最適化を実行します。

参照タイプ

オブジェクト参照タイプは、強参照、ソフト参照、弱参照、ファントム参照に分類されます。

2. JVM チューニング - 基本的なガベージ コレクション アルゴリズム

ガベージ コレクション アルゴリズムは、さまざまな観点から分類できます。

基本的なリサイクル戦略によれば

参照カウント:

古いリサイクルアルゴリズム。原則として、このオブジェクトには参照があり、それによってカウントが増加し、参照を削除するとカウントが減少します。ガベージ コレクション中は、カウントが 0 のオブジェクトのみが収集されます。このアルゴリズムの最も致命的な問題は、循環参照の問題を処理できないことです。

マークスイープ:

このアルゴリズムは 2 段階で実行されます。最初のフェーズでは、参照ルート ノードから始まるすべての参照オブジェクトをマークし、2 番目のフェーズではヒープ全体を走査してマークされていないオブジェクトをクリアします。このアルゴリズムではアプリケーション全体を一時停止する必要があり、メモリの断片化が発生します。

コピー:

このアルゴリズムは、メモリ空間を 2 つの等しい領域に分割し、一度に 1 つの領域のみを使用します。ガベージ コレクション中、現在使用されている領域が走査され、使用中のオブジェクトが別の領域にコピーされます。このアルゴリズムは、使用中のオブジェクトのみを毎回処理するため、コピーコストは比較的小さくなります。同時に、コピー後にメモリを適切にソートできるため、「断片化」の問題は発生しません。もちろん、このアルゴリズムの欠点も明らかです。つまり、メモリスペースが 2 倍必要になるということです。

マークコンパクト:

このアルゴリズムは、「マーク アンド スイープ」アルゴリズムと「コピー」アルゴリズムの両方の利点を組み合わせたものです。これも2段階に分かれています。最初のステージでは、ルート ノードから始まるすべての参照オブジェクトをマークします。 2 番目のステージでは、ヒープ全体を走査し、マークされていないオブジェクトをクリアし、生き残ったオブジェクトをヒープの 1 つの部分に「圧縮」して、順番に並べます。このアルゴリズムは、「マークアンドスイープ」アルゴリズムの断片化の問題を回避し、「コピー」アルゴリズムのスペースの問題も回避します。

治療方法によって分けられる

増分収集: リアルタイム ガベージ コレクション アルゴリズム。つまり、アプリケーションの実行中にガベージ コレクションが実行されます。何らかの理由で、JDK5.0 のコレクターはこのアルゴリズムを使用しません。

世代別収集: オブジェクトのライフ サイクルの分析に基づくガベージ コレクション アルゴリズム。オブジェクトは若い世代、古い世代、永久世代に分けられ、異なるライフサイクルでオブジェクトをリサイクルするために異なるアルゴリズム (上記の方法のいずれか) が使用されます。現在のガベージ コレクター (J2SE1.2 以降) はすべてこのアルゴリズムを使用します。

システムスレッド別

シリアル コレクション: シリアル コレクションでは、単一のスレッドを使用してすべてのガベージ コレクション作業を処理します。マルチスレッドのやり取りが不要なため、実装が簡単で、比較的効率的です。ただし、複数のプロセッサを活用できないという制限も明らかであるため、このコレクションはシングルプロセッサ マシンに適しています。もちろん、このコレクターは、データ量が少ない (約 100 MB) マルチプロセッサ マシンでも使用できます。

並列コレクション: 並列コレクションでは、複数のスレッドを使用してガベージ コレクション作業を処理するため、処理が高速かつ効率的になります。理論的には、CPU の数が多いほど、並列コレクターが発揮できる利点は多くなります。

同時実行コレクション: シリアル コレクションや並列コレクションと比較すると、前 2 つはガベージ コレクションを実行するときに動作環境全体を一時停止する必要があり、ガベージ コレクション プログラムのみが実行されます。したがって、ガベージ コレクション中にシステムは明らかに一時停止し、ヒープが大きくなるにつれて一時停止時間は長くなります。

3. ガベージコレクションが直面する問題

ゴミの分別方法

前述の「参照カウント」方式は、オブジェクトの生成時および削除時に参照回数を統計的に制御して判断を行います。ガベージ コレクターは、カウントが 0 のオブジェクトを収集します。ただし、この方法では循環参照を解決できません。したがって、後で実装されたガベージ検出アルゴリズムはすべて、プログラムが実行されているルート ノードから開始され、オブジェクト参照全体を走査して、残っているオブジェクトを検索しました。では、この実装ではガベージ コレクションはどこから始まるのでしょうか?つまり、システムで現在使用されているオブジェクトをどこから探し始めるのでしょうか?上記で分析したヒープとスタックの違いは、スタックはプログラムが実際に実行される場所であるため、どのオブジェクトが使用されているかを取得するには、Java スタックから開始する必要があることです。同時に、1 つのスタックは 1 つのスレッドに対応するため、複数のスレッドがある場合は、それらのスレッドに対応するすべてのスタックをチェックする必要があります。

同時に、スタックに加えて、システム実行中にプログラム実行データも保存されるレジスタもあります。このように、スタックまたはレジスタ内の参照から開始してヒープ内のオブジェクトを見つけ、これらのオブジェクトからヒープ内の他のオブジェクトへの参照を見つけることができます。この参照は徐々に拡張され、最終的には null 参照または基本型で終了し、Java スタック内の参照に対応するオブジェクトをルート ノードとするオブジェクト ツリーが形成されます。スタック内に複数の参照がある場合、最終的には複数のオブジェクト ツリーが形成されます。これらのオブジェクト ツリー内のオブジェクトはすべて、現在のシステム操作に必要なオブジェクトであり、ガベージ コレクションできません。残ったオブジェクトは参照できないオブジェクトとみなされ、ゴミとしてリサイクルすることができます。

したがって、ガベージ コレクションの開始点は、いくつかのルート オブジェクト (Java スタック、静的変数、レジスタなど) です。最も単純な Java スタックは、Java プログラムによって実行されるメイン関数です。このリサイクル方法は、前述の「マークスイープ」リサイクル方法でもあります。

瓦礫の処理方法

さまざまな Java オブジェクトの生存時間は不確実であるため、プログラムを一定期間実行した後にメモリがクリーンアップされない場合、散在したメモリ フラグメントが発生します。断片化の最も直接的な問題は、大きなメモリ空間ブロックを割り当てることができなくなり、プログラム操作の効率が低下することです。したがって、上記の基本的なガベージ コレクション アルゴリズムでは、「コピー」方式と「マーク コンパクト」方式の両方で断片化の問題を解決できます。

オブジェクトの作成とリサイクルを同時に行う問題を解決する方法

ガベージ コレクション スレッドはメモリをリサイクルしますが、プログラム実行スレッドはメモリを消費 (または割り当て) します。 1 つはメモリをリサイクルし、もう 1 つはメモリを割り当てます。この観点から見ると、両者は矛盾している。そのため、従来のガベージコレクション方式では、ガベージコレクションを実行する前に、アプリケーション全体を一時停止(つまり、メモリ割り当てを一時停止)してからガベージコレクションを実行し、コレクションが完了した後にアプリケーションを続行することが一般的に必要でした。この実装方法は、両者の矛盾を解決する最も直接的かつ効果的な方法です。

しかし、この方法には明らかな欠点があります。ヒープ領域が増加し続けると、ガベージ コレクション時間もそれに応じて増加し続け、対応するアプリケーションの停止時間もそれに応じて増加します。一部のアプリケーションでは、最大停止時間が数百ミリ秒という要件など、応答時間に対する要件が非常に高くなります。ヒープ領域が数 GB を超える場合、この制限を超える可能性が非常に高くなります。この場合、ガベージコレクションがシステム動作のボトルネックになります。この矛盾を解決するために、並行ガベージ コレクション アルゴリズムが存在します。このアルゴリズムを使用すると、ガベージ コレクション スレッドはプログラム実行スレッドと同時に実行されます。この方法では、一時停止の問題は解決されますが、新しいオブジェクトが生成される間はオブジェクトをリサイクルする必要があるため、アルゴリズムの複雑さが大幅に増加し、システムの処理能力がそれに応じて低下し、「断片化」の問題を解決することがより困難になります。

4. 世代別ガベージコレクションの詳細説明(1)

なぜ世代なのですか?

世代別ガベージ コレクション戦略は、異なるオブジェクトのライフ サイクルが異なるという事実に基づいています。そのため、ライフサイクルの異なる物体をさまざまな方法で収集し、リサイクル効率を向上させることができます。

Java プログラムの実行中に、多数のオブジェクトが生成されます。その一部は、セッション オブジェクト、スレッド、HTTP 要求内のソケット接続など、ビジネス情報に関連しています。これらのオブジェクトはビジネスに直接リンクされているため、ライフサイクルが比較的長くなります。ただし、プログラムの実行中に生成される一時変数を中心に、ライフ サイクルが比較的短いオブジェクトもいくつかあります。たとえば、String オブジェクトは不変のクラス特性を持つため、システムは大量のオブジェクトを生成し、一部のオブジェクトは 1 回の使用後にリサイクルすることもできます。

オブジェクトの生存時間を区別せずに、各ガベージ コレクションでヒープ領域全体がリサイクルされ、比較的長い時間がかかることを想像してください。同時に、各コレクションはすべての残存オブジェクトをトラバースする必要がありますが、実際には、ライフサイクルが長いオブジェクトの場合、このトラバースは効果的ではありません。何度もトラバースされている可能性があるにもかかわらず、まだ存在しているからです。そのため、世代別ガベージコレクションは分割統治の考え方を採用し、世代を分割し、ライフサイクルの異なるオブジェクトを異なる世代に配置し、それぞれに最も適したガベージコレクション方法を使用して異なる世代をリサイクルします。

世代を分ける方法

図に示すように

仮想マシンは、若い世代、古い世代、永続的な世代の 3 つの世代に分かれています。永続世代には主に Java クラスのクラス情報が格納されますが、これはガベージ コレクションによって収集される Java オブジェクトとはほとんど関係がありません。若い世代と古い世代の区別は、ガベージコレクションに大きな影響を与えます。

若い世代:

新しく生成されたオブジェクトはすべて、まず若い世代に配置されます。若い世代の目標は、ライフサイクルの短いオブジェクトをできるだけ早く収集することです。

若い世代は3つの領域に分かれています。エデンエリアが 1 つ、サバイバーエリアが 2 つ (一般的に)。ほとんどのオブジェクトはエデンエリアで作成されます。 Eden エリアがいっぱいになると、生き残ったオブジェクトは Survivor エリア (2 つのうちの 1 つ) にコピーされます。このサバイバー エリアがいっぱいになると、このエリア内の生き残ったオブジェクトは別のサバイバー エリアにコピーされます。この Survivor 領域もいっぱいになると、最初の Survivor 領域からコピーされ、その時点でまだ生きているオブジェクトが「tenured 領域」にコピーされます。注意すべき点は、Survivor の 2 つの領域は対称的で順序がないため、Eden からコピーされたオブジェクトと以前の Survivor からコピーされたオブジェクトが同じ領域に同時に存在する可能性がある一方で、最初の Survivor からコピーされたオブジェクトのみが古い領域にコピーされるということです。さらに、常に空のサバイバーゾーンが 1 つあります。同時に、プログラムのニーズに応じて、Survivor 領域を複数 (2 つ以上) に設定することができ、これにより若い世代のオブジェクトの存在時間を増やし、古い世代に配置される可能性を減らすことができます。

旧世代:

若い世代で N 回のガベージ コレクションを生き残ったオブジェクトは、古い世代に配置されます。したがって、古い世代に保存されているオブジェクトは、ライフサイクルが長いオブジェクトであると考えられます。

永久世代:

静的ファイル、現在は Java クラス、メソッドなどを保存するために使用されます。永続的な生成はガベージ コレクションに大きな影響を与えませんが、Hibernate などの一部のアプリケーションでは、一部のクラスを動的に生成したり呼び出したりすることがあります。この場合、操作中に新しく追加されたクラスを保存するために、比較的大きな永続生成スペースを設定する必要があります。永続世代のサイズは -XX:MaxPermSize= によって設定されます。設定します。

ガベージコレクションはいつ行われますか?

オブジェクトは世代ごとに処理されるため、ガベージコレクションの領域と時間も異なります。 GC には、Scavenge GC と Full GC の 2 種類があります。

スカベンジGC

通常、新しいオブジェクトが生成され、Eden 内のスペースの適用に失敗した場合、Scavenge GC がトリガーされ、Eden 領域で GC が実行され、生き残っていないオブジェクトがクリアされ、生き残っているオブジェクトが Survivor 領域に移動します。次に、2 つのサバイバー エリアを整理します。このタイプの GC は若い世代の Eden 領域で実行され、古い世代には影響しません。ほとんどのオブジェクトは Eden 領域から開始され、Eden 領域はそれほど大きく割り当てられていないため、Eden 領域の GC は頻繁に実行されます。したがって、一般的には、Eden をできるだけ早く解放するために、ここでは高速で効率的なアルゴリズムを使用する必要があります。

フルGC

Young、Tenured、Perm を含むヒープ全体をソートします。フル GC はペア全体をリサイクルする必要があるため、スカベンジ GC よりも遅くなります。したがって、Full GC の回数は可能な限り減らす必要があります。 JVM チューニングのプロセスでは、FullGC を調整する作業が大部分を占めます。フル GC が発生する原因としては、次のことが考えられます。

  • 旧世代(終身在職権)は満員
  • パーマ世代は満員
  • System.gc() が明示的に呼び出される
  • 最後のGC後のヒープの各ドメインの割り当て戦略の動的な変更

<<:  エッジで生活し、エッジ コンピューティング ビジネスを行っていますか?

>>:  ハイブリッドクラウドの5つの利点

推薦する

5 分で gRPC を学びましょう。学びましたか?

導入長い間 Java を使ってきた開発者のほとんどは、gRPC に触れることはほとんどないと思います...

時代は発展し、促進しており、私たちは時代のペースに遅れないようにしなければなりません

21世紀に入り、経済の発展に伴い、人々の物質的、文化的ニーズが満たされ、精神的、文化的ニーズが時代の...

addressnode-$7/4IP/1gメモリ/15gSSD/1.5Tトラフィック/ダラス

addressnode サーバーはダラスにあり、openvz をベースとし、純粋な SSD ハード ...

表示: ピークサーバー - 6.99 ドル/KVM/1g メモリ/50 ハード ディスク/2T トラフィック

Peakservers は、KVM VPS の特別プロモーション バージョン、cpanel 付き V...

Pinterestモデルの台頭は中国で模倣の波を引き起こす可能性がある

Pinterest が世界で最も急成長しているウェブサイトにピンタレスト2011年11月、Admin...

MikroVPS-3.45 ユーロ/Xen/512M メモリ/25G ハード ドライブ/1.5T トラフィック/10G ポート

ハンガリーの VPS 販売業者である MikroVPS.hu は、VPS ビジネスに注力しています。...

なぜ新しいストレージとコンピューティングの分離が主流になるのでしょうか?

「世界は長い分裂の期間の後、最終的には統一され、長い統一の期間の後、最終的には分裂する」という古い格...

百度とグーグルの完全一致に関する分析

多くのウェブマスターは、Baidu と Google の差別化された最適化について独自の経験を持って...

WeChatでグループを見つけて参加するための6つのチャネルと10の実用的な方法

私はWeChatファンの成長分野を専門としています。最近、多くの友人から、より多くのターゲット顧客の...

【クラウドネイティブ】Kubernetes CRD 詳細解説(カスタムリソース定義)

1. 概要CRD (カスタム リソース定義) 自体は Kubernetes の組み込みリソース タイ...

larkyun: 常州聯通、1Gbps大帯域幅ホストテスト、プロモーション価格は非常に安い

Larkyunは現在、主に国内の従来のUnicom回線でマシンを運用しており、最大1Gbpsの超大容...