JVMメモリの理解

JVMのメモリレイアウトについて話しますか?
JVMメモリの理解

Java仮想マシンには、主にいくつかの領域が含まれています。

ヒープ:ヒープJava仮想マシンの最大のメモリは、スレッドによって共有されるメモリ領域です。基本的に、すべてのオブジェクトインスタンス配列には、ヒープ上のスペースが割り当てられます。ヒープエリアは、ヨンドエリアの若い世代と古いエリアの古い世代に細分されます。若い世代は、エデン、S0、S1の3つの部分に分けられます。デフォルトの比率は8:1:1です。

スタック:スタックはスレッドプライベートメモリ領域です。各メソッドが実行されると、スタックフレームがスタック上に作成されます。メソッドの呼び出しプロセスは、スタックをスタックしてポップするプロセスに対応します。各スタックフレームの構造には、ローカル変数テーブル、オペランドスタック、動的接続、メソッドリターンアドレスが含まれます。

ローカル変数テーブルは、メソッドパラメータとローカル変数を格納するために使用されます。最初のメソッドが呼び出されると、そのパラメーターは0から始まる連続ローカル変数テーブルに渡されます。

オペランドスタックは、一部のバイトコード命令をローカル変数テーブルからオペランドスタックに転送するために使用されます。また、メソッド呼び出しパラメーターを準備し、メソッドの戻り結果を受信するためにも使用されます。

動的リンクは、シンボリック参照で表されるメソッドを実際のメソッドの直接参照に変換するために使用されます。

メタデータ:Java 1.7より前は、メソッド領域の概念が含まれていました。定数プールはメソッド領域(永続的な生成)に存在し、メソッド領域自体は論理的な概念でした。1.7以降、定数プールはヒープに移動されました。内部では、1.8以降、永続的な生成の概念が削除され(メソッド領域の概念は引き続き保持されます)、実装メソッドは現在のメタデータです。クラスとランタイム定数プールのメタ情報が含まれています。

クラスファイルは、クラスとインターフェースの定義情報です。

ランタイム定数プールは、クラスとインターフェイスの定数プールのランタイムマニフェストです。

ネイティブメソッドスタック:ネイティブネイティブメソッドを実行するために主に使用される領域

プログラムカウンター:スレッドのプライベートエリアでもあり、現在のスレッドで仮想マシンによって実行されているバイトコードの命令アドレスを記録するために使用されます

新しいオブジェクトのプロセスを知っていますか?
JVMメモリの理解

仮想オポチュニティが新しいキーワードを検出すると、実現は現在のクラスがロードされているかどうかを判断します。クラスがロードされていない場合は、最初にクラスのロードメカニズムを実行し、次にスペースを割り当てて、ロードの完了後にオブジェクトを初期化します。

まず、現在のクラスがロードされているかどうかを確認します。ロードされていない場合は、クラスロードメカニズムを実行します。

読み込み:バイトコードからバイナリストリームに読み込むプロセスです

検証:もちろん、ロードが完了した後、クラスファイルが仮想マシンの仕様を満たしているかどうかを検証する必要があります。インターフェイス要求と同様に、最初に行うことはもちろんパラメータの検証です。

準備:静的変数と定数にデフォルト値を割り当てます

解決策:定数プール内のシンボル参照を(参照されるターゲットを説明するためにシンボルを使用して)直接参照(ターゲットへのポインターまたはハンドルなど)に置き換えるプロセス。

初期化:静的コードブロック(cinit)を実行して初期化します。親クラスがある場合は、最初に親クラスを初期化します。

追伸:静的コードブロックは完全にスレッドセーフであり、クラスの読み込みプロセス中にJava仮想マシンによって暗黙的に初期化および呼び出されるだけです!(ここに問題はありますか、静的コードブロックスレッドは安全ですか?)

クラスがロードされると、オブジェクトの割り当てと初期化のプロセスが実行されます

まず、オブジェクトに適切なサイズのメモリスペースを割り当てます

次に、インスタンス変数にデフォルト値を割り当てます

オブジェクトヘッダー情報、オブジェクトハッシュコード、GC生成期間、メタデータ情報などを設定します。

コンストラクター(init)の初期化を実行します

親の委任モデルを知っていますか?
クラスローダーは上から下に分かれています。

Bootstrap ClassLoaderは、クラスローダーを開始します。デフォルトでは、JAVA_HOME / libディレクトリにjarがロードされます。

拡張ClassLoader拡張クラスローダー:JAVA_HOME / lib / extディレクトリにjarをロードするデフォルト。

アプリケーションClassLoader:たとえば、Webアプリケーションは、WebプログラムのClassPathの下にあるクラスをロードします。

ユーザーClassLoaderユーザー定義クラスローダー:ユーザーが定義

クラスをロードするとき、最初に親ローダーがロードされているかどうかを確認します。ロードされていない場合は、順番に確認します。ロードされていない場合は、ロードが成功するまで、現在のクラスを上から下にロードしようとします。

JVMメモリの理解

ガベージコレクションアルゴリズムとは何ですか?
Mark-clear
は、リサイクルするオブジェクトを均一にマークします。マーキングが完了すると、マークされたすべてのオブジェクトが均一にリサイクルされます。マーキングプロセスはすべてのGC ROOTをトラバースする必要があるため、クリアプロセスはヒープ内のすべてのオブジェクトもトラバースするため、mark-clearはこのアルゴリズムは非効率的であり、メモリの断片化の問題ももたらします。

コピーアルゴリズム
パフォーマンスの問題を解決するために、コピーアルゴリズムが登場しました。これは、メモリを同じサイズの2つの領域に分割し、毎回1つを使用します。一方のメモリが使い果たされると、残りのオブジェクトがもう一方のメモリにコピーされます。この領域では、現在のメモリがクリアされるため、パフォーマンスとメモリの断片化の問題を解決できます。しかし、それはまた別の問題をもたらします、使用可能なメモリスペースが半分に減少します!

したがって、現在の一般的な若い世代+古い世代のメモリ構造が生まれました:Eden + S0 + S1、IBMの調査によると、オブジェクトの98%が生きて死んでいるため、実際に生き残っているオブジェクトはそうではありませんそれらはたくさんあり、メモリの無駄の半分を使用する必要がないため、デフォルトの比率は8:1:1です。

このように、使用時には、エデンエリアとS0S1の一方のみが使用され、生き残ったオブジェクトは毎回別の未使用のサバイバーエリアにコピーされ、エデンと使用済みのサバイバーは同時にクリアされるため、メモリの浪費はわずか10%になります。 。

最後の未使用のサバイバーが生き残ったオブジェクトを置くことができない場合、これらのオブジェクトはオールドエイジに入ります。

PS:では、なぜエデン地区と2つのサバイバー地区に分かれているのかを尋ねる基本的な質問がいくつかありますか?効果は何ですか?これは、メモリを節約し、メモリの断片化の問題を解決することです。これらのアルゴリズムは、問題を解決するために作成されています。理由を理解していれば、それを覚える必要はありません。


老朽化したオブジェクトの生存率が比較的高いため、マーキングとソートは明らかに老朽化してから複製アルゴリズムを使用することには不適切です。現時点では、頻繁な複製はパフォーマンスに大きな影響を与え、処理のための追加のスペースはありません。すべての詳細を明らかにします。したがって、老後の特性を考慮して、すべての生き残ったオブジェクトがマーク編成アルゴリズムによってマークされ、すべての生き残ったオブジェクトが一方の端に移動し、境界の外側のメモリスペースがクリアされます。

では、GC ROOTとは何ですか?GCルートとは何ですか?
上記のマーキングアルゴリズム、オブジェクトが生きているかどうかをマーキングする方法は?参照カウント方法を使用して、オブジェクトへの参照カウンターを設定するだけです。オブジェクトへの参照がある場合、カウンターは+1です。それ以外の場合、カウンターは-1ですが、この単純なアルゴリズムでは循環参照の問題を解決できません。

Javaは、到達可能性分析アルゴリズムを使用して、存続するオブジェクトをマークする目的を達成します。一連のGC ROOTを開始点として定義し、開始点から下に向かって検索します。検索パスは参照チェーンと呼ばれます。オブジェクトがGC ROOTに到達すると、何もありません。参照チェーンが接続されている場合、オブジェクトはリサイクルされていると判断できます。

GCROOTとして使用できるオブジェクトは次のとおりです。

スタックで参照されるオブジェクト

静的変数および定数によって参照されるオブジェクト

ネイティブメソッドスタックのネイティブメソッドによって参照されるオブジェクト

ガベージコレクターは理解していますか?老若男女のためにどんなゴミ収集家がいますか?
JVMメモリの理解

若い世代のガベージコレクターには、Serial、ParNew、Parallellが含まれ、古い世代には、Serial Old、CMS、Parallel Old、およびJDK11の船の新しいG1コレクターが含まれます。

シリアル:コレクターのシングルスレッドバージョンであるSTW(Stop The World)は、ガベージコレクション中に使用されます。つまり、ガベージコレクション中に他のワーカースレッドを一時停止する必要があります。

ParNew:CMSと組み合わせて使用​​されるシリアルのマルチスレッドバージョン

Parallel Scavenge:並行して収集できるマルチスレッドのガベージコレクター

Serial Old:古いバージョンのSerialもシングルスレッドです

Parallel Old:古いバージョンのParallel Scavenge

CMS(Concurrent Mark Sweep):CMSコレクターは、最短の休止時間を取得することを目的としたコレクターです。他のコレクターと比較して、STW時間は短く、並行して収集できます。また、マークスイープアルゴリズムに基づいています。 GCプロセス全体は4つのステップに分かれています。

初期マーク:GCROOTを関連付けることができるオブジェクトをマークします。STWが必要です

同時マーキング:STWを使用せずに、GCRootsの直接関連付けられたオブジェクトからオブジェクトグラフ全体をトラバースするプロセス

再マーキング:同時マーキング中にユーザープログラムの継続操作によって変化するマーキングを修正するには、STWが必要です。

同時クリーンアップ:マーキングフェーズで判断されたデッドオブジェクトをクリーンアップして削除します。STWは必要ありません。

プロセス全体の観点から見ると、同時マーキングと同時クリアに最も時間がかかりますが、ユーザースレッドを停止する必要はありません。最初のマーキングとリマーキングにかかる​​時間は短くなりますが、ユーザースレッドを停止する必要があります。一般に、プロセス全体が原因で発生します。一時停止時間は短く、ほとんどの場合、ユーザースレッドで機能します。

G1(ガベージファースト):G1コレクターはJDK9のデフォルトのガベージコレクターであり、収集のために若い世代と古い世代を区別しなくなりました。

G1の原理を理解していますか?
JVMメモリの理解

G1は、JDK9以降のサーバーのデフォルトのコレクターとして機能し、ガベージコレクションの若い世代と古い世代を区別しなくなりました。メモリを複数の領域に分割し、各領域のサイズを-XX:G1HeapRegionSizeで設定できます。サイズは1〜です。 32M、大きなオブジェクトの保存については、Humongousの概念が導き出されます。リージョンの半分のサイズを超えるオブジェクトは大きなオブジェクトと見なされ、リージョン全体のサイズを超えるオブジェクトは超大きなオブジェクトと見なされ、連続したNに保存されます。巨大地域では、G1はリサイクル時にバックグラウンドで優先リストを維持し、ユーザーが設定した収集一時停止時間に従って、収益が最も高い地域が最初にリサイクルされるたびに行われます。

G1のリサイクルプロセスは、次の4つのステップに分かれています。

初期マーク:GCROOTを関連付けることができるオブジェクトをマークします。STWが必要です

同時マーキング:GCRootsの直接関連付けられたオブジェクトからオブジェクトグラフ全体をトラバースするプロセス。スキャンが完了した後、同時マーキングプロセス中に変更されたオブジェクトが再処理されます。

最終マーク:ユーザースレッドを一時停止し、再度処理します。STWが必要です

スクリーニングとリサイクル:地域の統計を更新し、各地域のリサイクル値とコストを並べ替え、ユーザーが設定した一時停止時間に基づいてリサイクル計画を策定します。次に、空のリージョンにリサイクルする必要があるリージョン内の残りのオブジェクトをコピーし、古いリージョンをクリーンアップします。STWが必要

一般に、同時マーキングに加えて、他のいくつかのプロセスでも短いSTWが必要です。G1の目標は、制御可能な一時停止と遅延を使用してスループットを可能な限り向上させることです。

YGCとFGCはいつトリガーされますか?対象はいつ老年期に入りますか?
新しいオブジェクトがメモリスペースに適用されると、エデンエリアがメモリ割り当て要件を満たせない場合、YGCがトリガーされ、使用中のサバイバーエリアとエデンエリアのライブオブジェクトが未使用のサバイバーエリアに送信されます。YGC後にまだ十分なスペースがない場合、次に、古い世代の割り当てを直接入力します。古い世代がスペースを割り当てることができない場合は、FGCをトリガーします。それでもFGCがスペースを配置できない場合は、OOM例外が報告されます。

JVMメモリの理解

YGC後、残ったオブジェクトは未使用のサバイバーエリアにコピーされ、Sエリアを置くことができない場合は、直接旧世代に昇格します。サバイバーエリアで前後にコピーされたオブジェクトの場合、交換しきい値は-XX:MaxTenuringThresholdを介して構成され、デフォルトは15回であり、数を超えると、古い時代に入ります。

さらに、MaxTenuringThresholdを待たずに旧世代を昇格させることができる動的な年齢判断メカニズムがあります。サバイバースペース内の同じ年齢のすべてのオブジェクトの合計サイズがサバイバースペースの半分より大きい場合、この年齢以上のオブジェクトは直接古い年齢に入ることができます。

頻繁なFullGCのトラブルシューティング方法は?
この種の問題を解決する最善の方法は、特定の例を使用して分析することです。そうでない場合は、一般的な分析手順について説明します。FGCは、不当なメモリ割り当てが原因で発生する可能性があります。たとえば、Eden領域が小さすぎて、オブジェクトが頻繁に古い時代に入る原因になります。これは、起動パラメータの構成で確認できます。さらに、メモリリークが発生している可能性があります。これは、次の手順で確認できます。

jstat -gcutilまたはgc.logログを表示して、メモリ回復を表示します

JVMメモリの理解
JVMメモリの理解
S0とS1は、それぞれ2つのサバイバーエリアの比率を表します。

Eは、図に示すように、エデン領域の割合を表します。78%が使用されています。

Oは老齢、Mはメタ空間、YGCは54回発生、YGCTはYGCが消費した累積時間を表し、GCTはGCが消費した累積時間を表します。

JVMメモリの理解

[GC [最初のFGCはガベージコレクションのタイプを表します

PSYoungGen:6130K-> 6130K(9216K)] 12274K-> 14330K(19456K)、0.0034895秒は、YGCの前後のメモリ使用量を表します

時間:user = 0.02 sys = 0.00、real = 0.00秒、userはユーザーモードで消費されたCPU時間を表し、sysはカーネルモードで消費されたCPU時間を表し、realはさまざまなウォールクロックの待機時間を表します。

これらの2つの図は単なる例であり、相関関係はありません。たとえば、図から、FGCが実行されるかどうか、FGCにかかる時間、GC後に若い世代のメモリが減少するかどうか、およびいくつかの予備情報が取得されるかどうかを確認できます。判定。

jmapコマンドjmap-dump:format = b、file = dumpfile pidなどを使用して特定の分析のためにメモリファイルをダンプし、エクスポート後にEclipse Memory Analyzerやその他のツールを使用して分析し、コードを見つけて修正します。

ここでもCPUの急上昇などの質問があるかもしれませんが、FGCについてはどうでしょうか。方法は似ています

現在のプロセスのpidを見つけ、top -p pid -Hリソースの占有を表示し、スレッドを見つけます

printf "%x \ n" pid、スレッドpidを0x32dなどの16進数に変換します

jstack pid | grep -A 10 0x32dスレッドのスタックログを表示しますが、問題が続くことがわかりません

メモリファイルをダンプし、MATやその他のツールで分析し、コードを見つけて修正します

JVMチューニングの経験はありますか?
すべてのチューニングの目的は、より少ないハードウェアコストでより高いスループットを実現することであり、JVMのチューニングも同じであり、ガベージコレクタとメモリ割り当てのチューニングを通じて最高のパフォーマンスを実現することを理解する必要があります。

単純なパラメーターの意味
最初に、いくつかの主要なパラメーターの意味を知る必要があります。

JVMメモリの理解

-Xmsは初期ヒープサイズを設定し、-Xmxは最大ヒープサイズを設定します

-XX:NewSize若い世代のサイズ、-XX:MaxNewSize若い世代の最大値、-Xmnは、-XX:NewSizeと-XX:MaxNewSizeを同じ値に構成することと同じです。

-XX:NewRatioは、若い世代と古い世代の比率を設定します。3の場合、若い世代と古い世代の比率は1:3で、デフォルト値は2です。

-XX:SurvivorRatio若い世代と2人の生存者の比率。デフォルトは8です。これは、比率が8:1:1であることを意味します。

-XX:PretenureSizeThreshold作成されたオブジェクトが指定されたサイズを超えた場合、古い世代のオブジェクトを直接割り当てます。

-XX:MaxTenuringThresholdは、Survivorで複製するオブジェクトの最大年齢しきい値を設定し、しきい値を超えて古い年齢に転送します

-XX:MaxDirectMemorySize Direct ByteBufferによって割り当てられたオフヒープメモリが指定されたサイズに達すると、フルGCがトリガーされます

チューニング
トラブルシューティングを容易にするためにログを印刷するには、GCログを有効にするのが最善です。GCログを有効にしても、パフォーマンスへの影響は最小限に抑えられますが、トラブルシューティングと問題の特定に迅速に役立ちます。-XX:+ PrintGCTimeStamps -XX:+ PrintGCDetails -Xloggc:gc.log

一般に、-Xms = -Xmxを設定して、固定サイズのヒープメモリを取得し、GCの数と時間を削減し、ヒープを比較的安定させます。

-XX:+ HeapDumpOnOutOfMemoryErrorを使用すると、メモリオーバーフローが発生したときに、JVMがメモリスナップショットを自動的に生成できます。これはトラブルシューティングに便利です。

-Xmnは若い世代のサイズを設定します。小さすぎるとYGCが増加し、大きすぎると古い世代のサイズが減少します。通常、ヒープ全体の1 / 4〜1 / 3に設定されます。

-XX:+ DisableExplicitGCを設定して、システムSystem.gc()を無効にし、FGCの手動トリガーが問題を引き起こさないようにします。

おすすめ

転載: blog.51cto.com/7218743/2544721