Java メモリの割り当てとリサイクルのメカニズムの詳細な説明 (図)

PHPz
リリース: 2017-04-04 10:26:25
オリジナル
2556 人が閲覧しました

この記事では主に、Javaランタイムデータ領域オブジェクト作成ガベージコレクションアルゴリズムリサイクル戦略を含む、Javaのメモリ割り当てとリサイクルメカニズムについて説明します。
PHP中国語Webサイト講座「JAVA初級入門ビデオチュートリアル」を参考に、チュートリアルの内容を元に筆者が要約して図解しただけです。内容のこの部分は、理解と記憶を容易にするために、可能な限り図、テキスト、または表の形式で提示されます。


1. 実行時データ領域

次の図は、実行時の Java 仮想マシンのメモリ図です:

Java メモリの割り当てとリサイクルのメカニズムの詳細な説明 (図)



この図から、Java メモリが次のように分割されていることがわかります。 6 つの部分:

  1. プログラム カウンター: 各スレッドには独立したプログラム カウンターがあります このカウンターは、現在のスレッドによって実行されるバイトコードの行番号インジケーターとして見ることができます。バイトコード インタプリタが動作すると、このカウンタの値を変更して、実行する次のバイトコード命令、分岐、ループ、ジャンプ、例外処理、およびスレッド回復などの基本的な機能はすべてこのカウンタに依存します。完了です。

  2. Java 仮想マシン スタック: 仮想マシン スタックはスレッドに対してプライベートであり、ライフ サイクル はスレッドと同じです。仮想マシン スタックは、Java メソッド実行のメモリ モデル を記述します。各メソッドが実行されると、ローカル 変数 テーブル、オペランド スタック、ダイナミック リンク、メソッド出口などの情報を保存するためのスタック フレームが作成されます。 各メソッドの呼び出しから実行完了までの処理は、スタックフレームを仮想マシンのスタックにプッシュしてからポップアウトするまでの処理に相当します。

  3. ローカル メソッド スタック: 仮想マシン スタックと同様の役割を果たします。違いは、仮想マシン スタックは Java メソッドの実行を提供し、

    ローカル メソッド スタックはネイティブ メソッド を提供することです。

  4. ヒープ:

    すべてのスレッドによって共有される領域。仮想マシンの起動時に作成され、ほぼすべてのオブジェクト インスタンスがヒープ上に割り当てられます。 Java ヒープは、新世代と旧世代にさらに分割することもできます。詳しくは、Eden 空間、From Survivor 空間、To Survivor 空間があります。ただし、どのように分割しても、すべてのオブジェクト インスタンスは保存されます。さらに分割する目的は、メモリをより適切に再利用したり、メモリをより速く割り当てたりすることです。

  5. メソッド領域:

    メソッド領域は各スレッドが共有するメモリ領域で、主に仮想マシンがロードするクラス情報、定数静的変数、ジャストインなどのデータを格納するために使用されます。 -time コンパイルされたコード。 この領域は、Java ヒープと同様、連続メモリを必要とせず、固定または拡張可能であり、ガベージ コレクションを実装しないことも選択できます。この領域のメモリ リサイクル ターゲットは主に定数プールのリサイクルと型のアンロードのためであり、この領域ではガベージ コレクション 動作 はほとんど発生しません。

  6. ランタイム定数プール: ランタイム定数プールはメソッド領域の一部です。

    クラスクラスのバージョン、フィールド、メソッド、インターフェースなどの説明情報に加えて、コンパイル中に生成されるさまざまなリテラルやシンボル参照を保存するために使用される定数プールもあります。コンテンツの一部は、クラスがロードされた後、メソッド領域のランタイム定数プールに保存されます。

  7. ダイレクト メモリ:
  8. ダイレクト メモリはオフヒープ メモリとも呼ばれ、仮想マシンの実行時にはデータ領域の一部ではありません

    。 NIO クラスは、JDK1.4 の後に導入されました。これは、チャネルとバッファーに基づく I/O メソッドであり、Nativefunctionライブラリを使用してヒープの外にメモリを直接割り当て、それを Java ヒープの DirectByteBuffer に保存できます。オブジェクトは、このメモリを操作するための参照として使用されます。これにより、パフォーマンスが大幅に向上し、Java ヒープとネイティブ ヒープの間でのデータの往復コピーが回避されます。

  9. 以下のように表形式にまとめます:
データ領域 概要 スレッド共有
プログラムカウンター 現在のスレッドによって実行されたバイトコードの行番号インジケーター いいえ
仮想マシンスタック Javaメソッド実行用ローカル変数、オペランド スタック、ダイナミック リンク、メソッド出口、その他の情報を保存するスタック フレームを作成します いいえ
ローカル メソッド スタック 仮想マシン スタックと同様に、ネイティブ メソッドを提供します いいえ
スタック オブジェクト インスタンスを格納します
メソッド領域です ロードされたクラス情報、定数、静的変数、オンザフライでコンパイルされたコード、および仮想マシンのその他のデータを格納します
ランタイム定数プール コンパイル中に生成されたリテラルとシンボル参照を格納するメソッド領域の一部 は、
ヒープの外に割り当てられたダイレクトメモリ メモリであり、パフォーマンスが高く、Javaのサイズによって制限されませんヒープ

2. オブジェクトの作成とメモリのレイアウト

1. オブジェクトの作成

Java メモリの割り当てとリサイクルのメカニズムの詳細な説明 (図)

Java オブジェクトの作成


上の図はオブジェクト作成の完全なフローチャートであり、以下で詳しく説明します。

  1. 仮想マシンがnew命令を受け取ると、この命令のパラメータが定数プール内のクラスのシンボリック参照を見つけられるかどうかをチェックし、このシンボリック参照によって表されるクラスがロードされているかどうかをチェックします。そして解析され初期化されます 。そうでない場合は、クラスのロード プロセスを最初に実行する必要があります。

  2. クラスのロードが完了すると、オブジェクトの割り当てに必要なスペースを決定できます。 Java ヒープ内のメモリが完全に規則的で、一方の側に使用済みメモリが保存され、もう一方の側に空きメモリが保存され、境界点のインジケータとして中央にポインタがある場合、メモリの割り当ては単に移動するだけです。ポインタを一定量だけ空き領域に向けて配置します。この配置方法は「ポインタ衝突」と呼ばれます。 Java ヒープ内のメモリが規則的ではなく、空きメモリと使用済みメモリがインターリーブされている場合、仮想マシンは使用可能なメモリ ブロックを記録するリストを維持し、オブジェクト インスタンスとメモリを割り当てるときに、リストから割り当てる十分な領域を見つけ出す必要があります。 リストのレコードを更新します。この割り当て方法は「フリーリスト」と呼ばれます。どの割り当て方法が使用されるかは、通常、仮想マシンのガベージ コレクターにコンパクション機能があるかどうかによって決まります。

  3. 利用可能なスペースを分割するとき、オブジェクトインスタンスにスペースを割り当てるときにスレッド

    セーフであるかどうかも考慮する必要があります。スレッドの安全性を確保するには、2 つのオプションがあります。 1 つは、メモリ領域を割り当てるアクションを同期することです。実際、仮想マシンは更新操作のアトミック性を確保するために、失敗による再試行を伴う CAS を使用します。もう 1 つは、メモリ割り当てアクションをスレッドごとに異なる領域に分割することです。各スレッドは、ローカル スレッド割り当てバッファ(スレッド ローカル割り当てバッファ、TLAB) と呼ばれる、Java ヒープ内の小さなメモリを事前に割り当てます。メモリを割り当てたいスレッドは、そのスレッドの TLAB にメモリを割り当てます。同期ロックは、TLAB が使い果たされ、新しい TLAB が割り当てられる場合にのみ必要になります。

    メモリ割り当てが完了すると、仮想マシンは
  4. に割り当てられたメモリ空間をゼロ値
  5. (オブジェクト ヘッダーを除く) に初期化し、オブジェクトのインスタンス フィールドが初期値を割り当てずに Java コードで直接使用できるようにします。価値観。

    仮想マシンは、オブジェクトの
  6. 情報をオブジェクトのオブジェクト ヘッダー
  7. に置きます。

  8. コンストラクターを実行します
  9. 2. オブジェクトのメモリ レイアウト

Java メモリの割り当てとリサイクルのメカニズムの詳細な説明 (図)


オブジェクトのメモリ レイアウトは 3 つの部分に分かれています:


    オブジェクトヘッダー
  1. 主に 2 つの情報部分が含まれます:

  2. 1 つの部分は、ハッシュ コード、GC 生成期間、ロック
    ステータス
  • フラグ、スレッドが保持するロックなど、オブジェクト自体の

    実行時データ を保存するために使用されます。偏ったスレッドID、偏ったタイムスタンプなど。

    もう 1 つの部分は
  • type pointer
  • で、仮想マシンはこのポインターを使用して、オブジェクトがどのクラスのインスタンスであるかを判断します。オブジェクトが Java

    配列の場合、オブジェクトヘッダーに配列の長さを記録するデータが存在する必要があります。

インスタンスデータ部
  • は、オブジェクトが実際に格納する有効な情報であり、プログラムコード内で定義された各種フィールドの内容でもあります。親クラスから継承され、サブクラスで定義されたものはすべて記録する必要があります。

    パディングの整列

    はプレースホルダーとして機能するだけです。 HotSpot VM の自動メモリ管理システムでは、オブジェクトの開始アドレスが 8 バイトの整数倍である必要があるため、オブジェクト サイズも 8 バイトの整数倍である必要があります。オブジェクトインスタンスデータ部分が整列していない場合は、整列パディングによって補完する必要があります。
  • 3. メモリのリサイクル

    1. オブジェクトの生存判定

    Java 仮想マシンは、到達可能性分析 を通じてオブジェクトが生存しているかどうかを判定します。このアルゴリズムの基本的な考え方は、「GC ルート」と呼ばれる一連のオブジェクトを開始点として使用し、オブジェクトが GC ルートに到達したときにこれらのノードから下方向に探索するパスを参照チェーンと呼びます。参照がない場合、チェーンが接続されている間、オブジェクトは使用できません。 図に示すように、object
    5、object6、object7は相互に関連していますが、GCルートに到達できないため、再利用可能なオブジェクトと判断されます。 もう 1 つ言及する価値があるのは、参照カウント メソッドでは、オブジェクトへの参照があるたびにカウンター値が 1 ずつ増加し、参照が失敗するとカウンター値が 1 ずつ減少します。 1 つ; いつでも カウンタが 0 のオブジェクトは使用できなくなります。参照カウンターは効率的で実装が簡単です。ただし、オブジェクト間の循環参照の問題を解決するのは困難です。ほとんどすべての主流の Java 仮想マシンは、メモリの管理に参照カウントを使用しなくなりました。


    到達可能性分析の概念図Java メモリの割り当てとリサイクルのメカニズムの詳細な説明 (図)
    到達可能性分析アルゴリズムで到達不可能なオブジェクトであっても、すぐに再利用されない場合があります。オブジェクトをリサイクルする場合、少なくとも 2 回マーキング プロセスを通過する必要があります。

    到達可能性分析後にオブジェクトに GC ルートに接続された参照チェーンがない場合、初めてマークされ、一度フィルターされます。フィルタリング条件は、このオブジェクトが

    final

    ize() メソッドを実行する必要があるかどうかです。オブジェクトが Finalize() メソッドをカバーしていない場合、または仮想マシンによって Finalize() メソッドが呼び出された場合、仮想マシンはこれら 2 つの状況を「実行する必要がない」ものとして扱います。

    このオブジェクトが Finalize() メソッドの実行に必要であると判断された場合、オブジェクトは F-Queue
    キューに配置され、その後、優先度
    の低いファイナライザー スレッドが仮想マシンによって自動的に作成されますFinalize() メソッドを実行します。 GC は、F キュー内のオブジェクトに対して 2 回目の小規模マークを実行します。そのオブジェクトが参照チェーン上のいずれかのオブジェクトに再度関連付けられると、そのオブジェクトは 2 回目のマーク中に「すぐにリサイクルされる」コレクションから削除されます。 。そうしないと、オブジェクトは実際にリサイクルされてしまいます。

    メソッドのファイナライズJava メモリの割り当てとリサイクルのメカニズムの詳細な説明 (図)
    2. メソッド領域のリサイクルの決定

    メソッド領域のリサイクルには主に、

    放棄された定数

    不要なクラス

    の2つの部分が含まれます。 破棄された定数のリサイクルは、Java ヒープ内のオブジェクトのリサイクルに似ています。

    • 役に立たないクラスを判断するための条件は、次の 3 つの条件を満たす必要があります:

    • このクラスのすべてのインスタンスはリサイクルされています。
      • このクラスをロードした ClassLoader はリサイクルされました。

      • このクラスに対応する java.

        lang
      • .Class オブジェクトはどこからも参照されておらず、リフレクションを通じてクラスにアクセスすることはできません。
      • 3. ガベージ コレクション アルゴリズム

    マーク スイープ アルゴリズム (マーク スイープ)

    :
      アルゴリズムは、「マーキング」と「クリア」の 2 つの段階に分かれています。まず、必要なオブジェクトをマークします。マーキングが完了した後、マーキングされたオブジェクトは一律にリサイクルされます。それには主な欠点が 2 つあります。1 つは効率の問題
    1. 、マーキングとクリアのプロセスがあまり効率的ではないことです。 2 つ目は、

      スペースの問題です。マークがクリアされた後、大量の不連続なメモリ フラグメントが生成されるため、より大きなオブジェクトが割り当てられたときにメモリ スペースが不足する可能性があり、ガベージ コレクション アクションが必要になります。事前に発動される。

      マーククリア

      Java メモリの割り当てとリサイクルのメカニズムの詳細な説明 (図)

      コピーアルゴリズム

      :

      コピーアルゴリズムは、容量に応じて利用可能なメモリを2つの等しいサイズのブロックに分割し、一度にそのうちの1つだけを使用します。メモリのブロックが足りなくなったら、残っている
    2. オブジェクトを別のブロックにコピーし、使用されているメモリ領域を一度にクリーンアップします。このように、毎回半分の領域全体がリサイクルされ、メモリを割り当てるときにメモリの断片化を考慮する必要がなく、ヒープの先頭ポインタを移動して順番にメモリを割り当てるだけで実装が簡単で効率的です。ただ、このアルゴリズムでは
    3. メモリが元のサイズの半分

      に削減され、その分コストが高くなります。

      Java メモリの割り当てとリサイクルのメカニズムの詳細な説明 (図)

      コピーアルゴリズム

    4. Mark-Compact(Mark-Compact)
      :
      マーキングプロセスは「マーククリア」アルゴリズムと同じですが、その後のリサイクル可能なオブジェクトのクリーニングは異なります直接実行されますが、すべての生きているオブジェクトを一方の端に移動させ、端の境界の外側のメモリを直接クリーンアップします

      Java メモリの割り当てとリサイクルのメカニズムの詳細な説明 (図)

      Mark-Complete アルゴリズム

    4.世代別コレクション アルゴリズム
    のガベージ コレクション商用仮想マシン 世代別コレクション アルゴリズムを使用して、オブジェクトの生存サイクルに従ってメモリをいくつかのブロックに分割します。 Javaヒープは新世代旧世代

    に分かれており、時代の特性に応じて適切な収集アルゴリズムが利用できるようになっています。新しい世代のガベージ コレクションのたびに多数のオブジェクトが失われるため、レプリケーション アルゴリズムを使用します。旧世代のオブジェクトの生存率は高く、割り当て保証のための余分なスペースがないため、リサイクルには「mark-clean」または「mark-organize」アルゴリズムを使用するのに適しています。

    4. メモリ割り当てとリサイクル戦略

    1. オブジェクトは Eden パーティションで優先されます:

      ほとんどの場合、オブジェクトは新世代 Eden 領域に割り当てられます。 Eden 領域に割り当てる十分なスペースがない場合、仮想マシンはマイナー GC を開始します。 GC 後、オブジェクトは Survivor 空間に配置されようとしますが、Survivor 空間にオブジェクトを配置できない場合は、空間割り当て保証メカニズムを通じて事前に古い世代にオブジェクトを転送することしかできません。

    2. ラージ オブジェクトは古い世代に直接入ります:

      ラージ オブジェクトは、大量の連続メモリ領域を必要とする Java オブジェクトを指します。仮想マシンは -XX:PretenureSizeThreshold パラメータを提供します。オブジェクトがこの設定値より大きい場合、オブジェクトは古い世代に直接割り当てられます。これにより、新世代の Eden 領域と 2 つの Survivor 領域での大量のメモリのコピーを回避できます。

    3. 長期的に存続するオブジェクトは古い世代に入ります:

      仮想マシンは、オブジェクトごとにオブジェクト経過時間カウンターを定義します。オブジェクトがエデンで生まれ、マイナー GC 後も存続し、サバイバーによって収容できる場合、そのオブジェクトはサバイバー スペースに移動され、オブジェクトの年齢は 1 に設定されます。各マイナー GC 後もオブジェクトは存続します。 Survivor エリアに、年齢を 1 つ追加するだけで、年齢が -XX:MaxTenuringThreshold パラメーターで設定された値に達すると、古い世代に移動されます。

    4. 動的な年齢判断:

      仮想マシンは、オブジェクトを古い世代に移動する前に、オブジェクトの年齢が -XX:MaxTenuringThreshold で設定された値に達する必要があるとは限りません。 Survivor 内の同じ年齢のすべてのオブジェクトのサイズの合計が Survivor スペースの半分より大きい場合、この年齢以上の年齢のオブジェクトは直接古い世代に入ることができます。

    5. スペース割り当ての保証:

      マイナー GC の前に、仮想マシンは古い世代の利用可能な最大連続スペースが新しい世代のすべてのオブジェクトの合計スペースより大きいかどうかを確認し、条件が true の場合、マイナー GC を実行します。 GCが確立されます。そうでない場合、仮想マシンは HandlePromotionFailure 設定値が保証失敗を許可するかどうかをチェックします。許可されている場合、古い世代で使用可能な最大連続領域が古い世代に移動されたオブジェクトの平均サイズより大きいかどうかのチェックが続行され、それが大きい場合は、マイナー GC が試行されます。それが小さい場合、または HandlePromotionFailure 設定値がリスクを許容しない場合、フル GC が実行されます。

    新世代 GC (マイナー GC): 新世代で発生するガベージ コレクション アクション。ほとんどの Java オブジェクトは生成されて消滅するため、マイナー GC は非常に頻繁に発生し、リサイクル速度も高速です。

    旧世代 GC (メジャー GC/フル GC): 旧世代で発生するガベージ コレクション アクション。メジャー GC には、少なくとも 1 つのマイナー GC が伴うことがよくあります。メジャー GC の速度は、通常、マイナー GC よりも 10 倍以上遅くなります。

    🎜

    以上がJava メモリの割り当てとリサイクルのメカニズムの詳細な説明 (図)の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

    ソース:php.cn
    このウェブサイトの声明
    この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
    人気のチュートリアル
    詳細>
    最新のダウンロード
    詳細>
    ウェブエフェクト
    公式サイト
    サイト素材
    フロントエンドテンプレート