プログラマーはマクロとミクロの観点から JVM 仮想マシンを分析します。

プログラマーはマクロとミクロの観点から JVM 仮想マシンを分析します。

1. 問題

  1. JAVA テキスト ファイルはどのようにして CLASS バイナリ ファイルに変換されるのでしょうか?
  2. CLASS ファイルの構造を理解するにはどうすればよいでしょうか?
  3. 仮想マシンはクラス ファイルのライフ サイクルをどのようにロードして使用しますか?
  4. 仮想マシンシリーズ診断ツールの使い方は?
  5. 仮想マシンのメモリ削除メカニズム?
  6. 仮想マシン命令セットアーキテクチャ?

2. キーワード

コンパイル、マジックナンバー、定数プール、リテラル、データテーブル、スタック、メソッド領域、プログラムカウンタ、メモリ参照、メモリオーバーフロー、ガベージコレクター、新規領域、*** 領域、命令セット

3. 全文の要約

この記事では、クラス ファイルの構造、仮想マシンがクラス ファイルをロードするメカニズム、クラス ファイルのライフ サイクル、バイトコード ロード エンジンをマクロとミクロの観点から紹介し、仮想マシンの動作に対する理解をより立体的に深めます。

4.CLASSファイル構造解析

私たちは、JAVA 言語を初めて学んだ日から、JAVA/JAVAC コマンドを実行してきました。 JAVAC は、拡張子 .java で作成したテキスト ファイルを、拡張子 .class のバイトコード ファイルにコンパイルします。前の章では、コードの本質を紹介し、JAVA 言語の文法要素について学びました。 Java ファイルをテキスト エディターで開くと、Java 言語の文法仕様に準拠した使い慣れた Java コードが含まれています。しかし、私たちは授業の内容にあまり詳しくありません。前の章では、コードがコンパイラによってマシン命令に変換されることを学びました。では、クラス ファイルも Java 仮想マシンによってマシン命令に変換される命令なのでしょうか?

実際、Java ファイルがクラス ファイルにコンパイルされると、Java 言語とは何の関係もなくなります。命令実行エンジンは JVM 仮想マシンです。 Scala、Python などの他のプログラミング言語は、クラス ファイルにコンパイルしてから、JVM に配置して実行できます。このように、クラス ファイルの性質を調査することがさらに必要になります。

4.1 CLASSファイルの例

まず、ミクロの観点からクラスファイルの構造を紹介しましょう。まず、単純な Java テキスト ファイルを作成し、それをクラス ファイルにコンパイルして、クラスの構造を観察します。

まずインターフェース ファイルを定義します。Add.java ファイルは次のようになります。

  1. パッケージ com.lzh.jvm;
  2. パブリックインターフェースAdd {
  3. 整数  int i、 int jを追加します
  4. }
  5. 基本的に日常生活でよく使用するファイル構造を含む別のインターフェース実装クラス AddImpl.java を記述します。
  6. パッケージ com.lzh.jvm;
  7. パブリッククラスAddImplはAddを実装します{
  8. 公共 静的最終整数 トップ= 100;
  9. プライベート文字列ポイント;
  10. 公共 整数  int i、 int j)を追加します
  11. i + jを返します
  12. }
  13. }

パッケージ名の定義のため、com/lzh/jvm のファイル ディレクトリを構築し、現在のディレクトリで com/lzh/jvm/Add.java ファイルと com/lzh/jvm/AddImpl.java ファイルをコンパイルする必要があります。 Add.class ファイルと AddImpl.class ファイルを取得しました。

.java バイナリ ファイルを追加します。

Add.class バイナリ ファイル:

AddImpl.java バイナリ ファイル:

AddImpl.class バイナリ ファイル:

上記の 4 つの画像は、WinHex バイナリ編集ツールで開かれています。左側はファイルのバイナリコード、右側は ASCII 標準コードなので、英国キーボードの文字のみを表すことができます。中国語の場合は文字化けして表示されます。読みやすくするために、ツールはデータを 16 進形式で表示します。 2 つの 16 進コードは 1 バイトの空間 (8 ビット) を表します。

直感的に、Java ファイルが占めるストレージ スペースはクラスが占めるストレージ スペースよりもはるかに少ないことがわかります。これは、前の章で紹介したコード変換プロセスと一致しています。基本的に、コンピューターは Java ファイルの内容を認識しません。 Java は高級言語であり、その構文は人間の言語に近いですが、コンピューターが理解するのは困難です。したがって、Java ファイルの内容を JVM が認識できるファイル形式に変換する必要があります。

高級言語は言語要素を高度に抽象化しており、それを機械命令に変換するには、コード文を段階的に実行するようにコンピューターを誘導するためのより多くの労力が必要です。次のセクションでは、JVM がクラスの内容をどのように理解して実行するかを理解できるように、クラス ファイルの構造について説明します。

4.2 クラスファイル構造の説明

このセクションでは、上図に示す AddImpl.class を例に、クラス構造を紹介します。構造的には、クラス ファイルには基本フィールドとテーブルの 2 種類のデータのみが格納されます。

  • 基本フィールド: 数値、参照、値、または文字列を記述するために使用される符号なし数値。タイプは u1、u2、u4、u8 で、占有されているバイト数を示します。
  • テーブル: 行が 1 つだけあり、列の数が可変であるテーブル構造。各フィールドは、別のテーブルの基本フィールドまたはインデックスにすることができます。

4.2.1 マジックナンバー

ファイルの種類を判別するために使用されます。通常、ファイルの種類を判断するためにファイルのサフィックスを使用しますが、サフィックスが変更されると、セキュリティ上の問題が発生します。クラスは、クラスの種類を示す 4 バイトのスペースで始まります。 CA FE BA BE はクラス タイプのファイルを示します。

4.2.2 バージョン番号

マジックナンバーの後に、JDK バージョン番号を表す 4 バイトが続きます。

  • マイナーバージョン番号: 最初の2つのフィールドは0x0000です
  • メジャーバージョン番号: 最後の2つのフィールドは0x0035で、10進数では53で、jdk1.9に相当します。

4.2.3 定数プール

名前が示すように、定数プールは文字列定数を格納するために使用されます。文字列定数には次のものがあります。

  • リテラル: 文字列、定数
  • 参照は、完全修飾クラス/インターフェース名、フィールド/メソッド名、修飾子に準拠します。

クラスの本質はテーブルのコレクションであることはわかっていますが、定数プールも例外ではありません。唯一の違いは、定数プールに格納されるテーブルには特定のタイプがあることです。次の表に示すように、合計 11 種類あります (図は「Java 仮想マシン JVM の高度な機能とベスト プラクティスの詳細な理解」から引用)。

各テーブルのテーブル構造は次のように記述されます。

これら 11 種類のテーブルの最初のフィールドはフラグ フィールド タグとして統合されており、1 バイトを占め、テーブルに格納されているデータのタイプを示すために使用されます。

定数プールに入る最初の 2 バイト (u2) は、定数プールの長さ、つまりテーブルの数を表します。

この例では、定数プールの数は 0x0017 であり、これは 10 進数では 23 であることがわかります。 0番目のテーブルは予約インデックスなので、文字列は参照されないということなので、実際のテーブルインデックスは1から計算され、つまり1から23までの22個のテーブルがあることになります。

まず、AddImpl.class 定数プールを観察し、最初のテーブルのテーブル構造を分析しましょう。表から、テーブル番号の直後の u1 の位置は 0A であり、これは 10 進数で 10 であることがわかります。テーブルタイプは CONSTANT_Methodref_info です。テーブル構造から、次の 2 つの u2 位置がテーブルのフィールドに属していることがわかります。どちらのフィールドもテーブル インデックス タイプです。 0x0003 は 3 番目のテーブルを参照することを意味し、0x0013 は 19 番目のテーブルを参照することを意味します。

その後、表は終了し、2 番目の表、最初の表が続きます。テーブル タグは 07 で、CONSTANT_Class_info タイプです。 2 番目のスペースは u2 で、フィールド値は 0x0014 であり、20 番目のテーブルを参照します。

次に、3 番目の表を分析します。同じ方法で、定数プールのテーブル構造を分析できます。定数プールの機能は、ソースコードのすべてのテキストデータを定数プールに集中させることです。さまざまなテーブルが相互に参照し、テキスト データを統一的に管理します。テーブル間の参照により、ほとんどのテキスト データは CONSTANT_Class_info テーブルに格納され、このテーブルではテキスト長の長さフィールドが u2 型であることが規定されており、これは 2 バイト (2 の 16 乗、65536/1024 = 64K) を占めるため、Java 変数またはメソッド名のサイズは 64K を超えることはできません。

4.2.4 アクセスフラグ

クラスまたはインターフェースを変更するための資格マーク

定数プールの終了後、2 バイトのアクセス フラグがあり、合計 32 個のフラグ ビットがあります。

4.2.5 クラス/スーパークラス/インターフェースインデックスコレクション

クラス インデックス、親クラス インデックス、およびインターフェイス インデックス コレクション: 定数プールの CONSTANT_Class_info テーブルを指し、次に CONSTANT_Class_info テーブル内のインデックスは、特定の CONSTANT_Utf8_info テーブルのバイト フィールドのリテラル値を指します。

4.5.6 フィールドテーブルコレクション

フィールドテーブルコレクション:

フィールドテーブルの構造は次のようになります

配列は[で表され、フィールドテーブルはクラス内のすべての変数を表すために使用されます(メソッド内のローカル変数を除く)

4.5.7 メソッドテーブルコレクション

メソッドテーブルコレクション:

メソッドテーブルの構造は次のようになります

4.5.8 プロパティテーブルコレクション

プロパティ テーブル コレクション

メソッド本体の内容はコード属性にコンパイルされます。コード表の構造は次の通りである

コード、例外、行番号テーブル、ローカル変数テーブル、ソース ファイル、定数値、内部クラス、非推奨、合成

クラスファイルは製品の型のようなものです。型を作成するプロセスは、クラスを JVM メモリにロードし、JVM がクラス型の外観に従ってオブジェクトを印刷するプロセスです。重要なポイントは金型の設計にあります。実際、金型自体を製造するには金型セットが必要です。これはクラスの厳密な構造仕様です。クラス ファイル構造仕様では、さまざまな側面での要件が規定されています。この要件に従って作られた金型だけが使用可能であり、製品の製造に使用できます。そうしないと、JVM がクラスが仕様を満たしていないと判断してロードを拒否するのと同じように、製品ラインに載せることさえできません。

5. クラスファイルのライフサイクル

クラスの読み込みタイミング

ほとんどの場合、クラスは受動的に初期化され、必要がないときには初期化されません。

クラスのロードプロセス

  • 読み込み: 完全修飾名取得バイナリ バイト ストリーム (クラス ファイルだけではなく) -> メソッド領域に読み取り -> ヒープ上にクラスに対応するオブジェクトを生成
  • 検証: ファイル形式の検証 (クラス ファイル仕様への準拠) -> メタデータの検証 (セマンティック分析) -> バイトコードの検証 (メソッド本体の検証) -> シンボル参照の検証。 -Xverify:none を使用すると、クラスの読み込み検証をスキップできます。
  • 準備: クラス変数には初期値を設定するメモリが割り当てられますが、割り当ては実行されません。
  • 解析: クラス インターフェイス、フィールド、メソッドへの修飾参照を解析して一致させます。クラス解析、インターフェース解析、フィールド解析、クラスメソッド解析、インターフェースメソッド解析、
  • 初期化: クラスコンストラクタを実行する

() メソッドは、すべての静的ステートメントをソース コードの順序で実行します。静的変数または静的ステートメントのないクラスには () はありません。

クラスローダー

ブートクラスローダー、拡張クラスローダー、アプリケーションクラスローダー

クラス ローダーは、親委任メカニズムを使用してクラス ファイルを読み取るため、親委任モデルが破壊されます。たとえば、OSGI サービスはカスタム クラス ローダー メカニズムによって実装されます。各OSGIモジュール(バンドル)には独自のローダーがあります

6. 仮想マシン診断ツール

仮想マシンのパフォーマンス監視およびトラブルシューティング ツール。システムの問題を特定する場合、知識と経験が基礎となり、データが基礎となり、ツールがデータを処理する手段となります。

JDK コマンドラインツール

  • 仮想マシンプロセスステータスツール: jps -lvm
  • 仮想マシン統計監視ツール: jstat -gc pid interval count
  • Java 構成情報ツール: jinfo -flag pid
  • Java メモリイメージングツール: jmap -dump:format=b,file=java.bin pid

ヒープダンプファイルを生成する

  • 仮想マシンのヒープダンプスナップショット分析ツール: jhat ファイルはヒープダンプファイルを分析し、ブラウザ経由で分析ファイルにアクセスします。
  • Javaスタックトレースツール: jstack [ オプション ] vmid

現時点での仮想マシンのスレッドスナップショット、threaddump、またはJavacoreを生成するために使用されます。

JDK 視覚化ツール

  • jコンソール
  • jvisualvm

7. 仮想マシンのメモリ削除メカニズム

このセクションでは、JVM のメモリ構造、メモリ割り当て操作戦略、およびガベージ コレクション メカニズムをマクロの観点から説明します。

7.1 仮想マシンのメモリ配分

Java メモリ領域とメモリオーバーフロー

JVM メモリ領域: メソッド領域、仮想マシン スタック、ローカル メソッド スタック、ヒープ、プログラム カウンター。

  • プログラムカウンタ: バイトコード行番号インジケータ。各スレッドにはプログラムカウンタが必要です。
  • 仮想マシン スタック: メソッドが実行されると、スタック フレームが作成されます (ローカル変数、操作スタック、動的リンク、メソッド終了を格納するため)。占有スペースのサイズはコンパイル時に決定できます。スレッドによって要求されたスタック深度が JVM 実行深度を超えると、StackOverflowError がスローされます。 jvm スタックが空きメモリを適用できない場合、OutOfMemoryError がスローされます。初期メモリを構成するには、-Xss、-Xsx を使用します。
  • ローカルメソッドスタック: オペレーティングシステムAPIインターフェースなどのローカルメソッドを実行します。
  • ヒープ: オブジェクトを格納するためのスペース。ヒープ サイズを構成するには、-Xmx と -Xms を使用します。ヒープがメモリに適用できない場合、OutOfMemoryError がスローされます。
  • メソッド領域: ストレージ クラス データ、定数、定数プール、静的変数、MaxPermSize パラメータによって構成されます
  • オブジェクト アクセス: オブジェクトを初期化し、その参照をスタック フレームに格納し、オブジェクトをヒープ メモリに格納します。オブジェクトには、属性情報とオブジェクトの親クラス、インターフェイス、その他の型データが含まれます (型データはメソッド領域スペースに格納され、オブジェクトには型データのアドレスがあります)。

7.2 メモリ再利用アルゴリズム

メモリリサイクルの概要:

コンパイル後、仮想マシン スタック、ローカル スタック、プログラム カウンターによって必要なメモリ領域を決定できます。プログラムの実行後、すべてのメモリ空間は自動的に解放されるため、動的リカバリの最適化は必要ありません。

JVM メモリ チューニングは、主にヒープ領域とメソッド領域という 2 つの主要領域のメモリを対象とします。

参照: 強い、柔らかい、弱い、幻影。強い参照はリサイクルされません。ソフト参照はメモリがオーバーフロー境界に達したときにリサイクルされます。弱い参照は各リサイクル サイクルでリサイクルされ、ファントム参照はリサイクルされたオブジェクトとして明確にマークされます。

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

  • オブジェクトはまず Eden 領域に割り当てられます。
  • 新しいオブジェクトコレクション戦略 マイナー GC (頻繁)
  • 古い世代のオブジェクト回復戦略 フル GC/メジャー GC (低速)
  • 大きなオブジェクトは直接古い世代に移動します。

3mより大きいオブジェクトは、古いエリアに直接配置されます -XX:PretenureSizeThreshold=3145728(3M)

  • 長期在庫品が高齢者エリアに入ります:

サバイバー スペース内のオブジェクトはマイナー GC の 1 年後に老化し、15 年以上経過したオブジェクトはオールド スペースに入ります。

-XX:最大テンリングしきい値=15

  • 動的オブジェクト年齢決定:生存者エリアの半分以上のスペースを占めるオブジェクトを老齢エリアに入るように設定します

ガベージコレクションアルゴリズム

マークスイープ、コピー、マークコンパクト、世代別コレクション(新生児用はコピー、高齢者用はマークコンパクト)

7.3 メモリコレクター

  • シリアルコレクター: シングルスレッド、主にクライアントモードで使用される
  • ParNew コレクター: 主にサーバー モードで使用されるシリアルのマルチスレッド バージョン
  • 並列スカベンジコレクター: スレッド制御スループット (ユーザーコード時間/ユーザーコード時間 + ガベージコレクション時間)、自動スループット調整、ユーザー新世代メモリ領域
  • シリアル 古いコレクター: 古いバージョンのシリアル
  • パラレルオールドコレクター:旧バージョンのパラレルスカベンジ
  • CMS (Concurrent Mark Sweep) コレクター: 短い一時停止時間、同時収集
  • G1コレクター:ブロックのマーキングとソート、断片化なし

8. 仮想マシン命令セットアーキテクチャ(実行エンジン)

8.1 仮想マシンバイトコード実行エンジン

ランタイムスタックフレーム構造

各メソッド呼び出しの開始から実行完了までのプロセスは、仮想マシン スタックにおけるこのスタック フレームのプッシュからポップまでのプロセスに対応します。

  • スタックフレームには、ローカル変数テーブル、オペランドスタック、動的リンク、メソッド戻り値が含まれます。
  • メソッド呼び出し

メソッド呼び出しはメソッド実行と同じではなく、呼び出されるメソッドのバージョンを決定します。

  • メソッド呼び出しバイトコード命令:invokestatic、invokespecial、invokevirtual、invokeinterface
  • 静的ディスパッチ: 静的型、実際の型。コンパイラがオーバーロードする場合、パラメータの静的型によってメソッドのバージョンが決定されます。 (方法を選択)
  • 動的ディスパッチ:invokevirtual命令は、クラスメソッドのシンボリック参照をさまざまな直接参照に解決して、スタックの最上部にある実際のオブジェクトを決定します(オブジェクトの選択)
  • シングルディスパッチ: 静的マルチディスパッチ。同じ命令の複数のメソッドバージョンが存在します。
  • 複数ディスパッチ: 動的単一ディスパッチ。メソッド レシーバーは 1 回だけ決定できます。

スタックベースのバイトコード解釈

説明と実行:

スタックベースの命令セットとレジスタベースの命令セット:

ローカルインタープリタに基づく実行プロセス

クラスローディング実行サブシステムの例

tomcat クラスローディング、OSGI ホットスワップ、バイトコード生成技術、動的プロキシ、Retrotranslator

9. 仮想マシン実装メカニズムの進化

プログラムのコンパイルとコードの最適化

早期コンパイル(コンパイル時)

  • javac コンパイラ: 解析とシンボル テーブルの記入、注釈処理、バイトコード生成
  • Java構文シュガー: 構文シュガーはコード開発に役立ちますが、コンパイル後にはシュガーコーティングがほどかれ、基本構文のクラスバイナリファイルに復元されます。

オーバーロードでは、メソッドに異なるシグネチャ (戻り値を除く) が必要ですが、クラス ファイルでは、次のように記述がまったく同じでない限り、メソッドは共存できます。

  1. パブリック文字列foo(リスト<文字列> arg){
  2. 最終的なint var = 0;
  3. 戻る  "" ;
  4. }
  5. 公共  int foo(リスト<整数> 引数){
  6. 整数変数 = 0;
  7. 0を返します
  8. }

後期コンパイル(ランタイム)

  • HotSpot VM でのジャストインタイムコンパイル
  1. 解析モード -Xint
  2. コンパイルモード -Xcomp
  3. 混合モード
  4. 階層型コンパイル: 解釈 -> C1 (クライアント コンパイラ) コンパイル -> C2 (サーバー コンパイラ) コンパイル
  5. トリガー条件: サンプリングに基づくホットスポット検出、カウンターに基づくホットスポット検出

10. まとめ

JVM は幅広いトピックをカバーしているため、スペースの制限により、この記事では詳細を深く分析することはできません。この記事では、原材料としての CLASS ファイルの構造をミクロの観点から分析し、マクロの観点から JVM が各受信 CLASS をどのように処理するかを説明します。 JVM は論理命令セットのセットをカスタマイズします。これは、コンピューターの実行方法に関する前回の記事にも当てはまります。現代のコンピュータの性能は大きく進歩しましたが、本質的には依然として完全なノイマン アーキテクチャです。量子コンピューティングの急速な発展により、将来のコンピューティングモデルにも革命的な進歩が起こると信じています。

<<:  リン・コンプ:インテルは中国聯通の 5G エッジコンピューティングのアプリケーションシナリオの拡大と商用化の加速を支援

>>:  2019 年の Hyper-V 監視ツールとソフトウェアのトップ 10

推薦する

ウェブサイトの検索エンジン最適化(SEO)におけるキーワード調査の重要性

ウェブサイトを構築する際には、特定の分野に突っ込んだり、競合調査を省略したり、ターゲットキーワードを...

ウェブサイトの予備診断を行うための10の基本的なステップ

一般的に、プロの SEO サービス プロバイダーは、Web サイトの予備診断を行う必要があります。W...

SEO担当者が職場に入るときに知っておくべき5つのタブー

SEO 人材が日々増えるにつれ、あらゆるレベルの「人材」が大企業、中堅企業、中小企業に流れ込んできて...

美団の王慧文氏:グループ購入の転換点は中期再編に入り、加速するだろう

Meituan.com副社長、王慧文氏(写真提供:テンセントテクノロジー)テンセントテクノロジーの雷...

オペレーティングシステムzgovps/zgocloudのインストール方法に関する簡単なチュートリアル

最近、zgovps から高性能な日本の VPS を購入した人が、OS のインストール方法がわからない...

エッジコンピューティングと水道業界の変革が出会うとき、神経終末からの知恵を目の当たりにしましょう

[51CTO.comからのオリジナル記事] モノのインターネットのすべての応用シナリオの中で、産業用...

百度6月28日事件後の反省

多くのウェブマスターが、Baiduの6月28日の事件の影響から逃れられなかったと私は信じています。こ...

VPShared - 10Gbps 保護を備えた VPS ホスティング、年間 3 ドルから

VPshared が変ですか?実際、彼はヴィルマックのブランドです! VPShared は、virm...

Core 2 NehalemモバイルクアッドコアCPUの命名を引き続き使用する

インテルが今年の第3四半期に、コードネーム「Calpella」の次世代 Centrino モバイル ...

Digitalocean-.08 サンフランシスコ VPS レビュー、それがゴミかどうか検証

昨夜、私はサンフランシスコで DigitalOcean VPS を開設しました。これは、過去 6 か...

Python のデータクロール、分析、マイニング、機械学習、Python 分散コンピューティングに関するコンテンツ共有

01 データキャプチャ1. 背景調査1) robots.txt をチェックして、サイトのクロールにど...

ASO 最適化、ASO を行う際に知っておくべきこと!

1. 世界は論争の渦中にあり、CP はワインのランキングについて議論しています。ASOの世界は 2 ...

地域別求人サイトの利点、収益モデル、展望の分析

インターネットの急速な発展に伴い、ますます多くの企業や個人がインターネットを通じて採用や求職のプロセ...

無料のオープンソース Web サイト構築プログラムが登場しました。 12の強力で使いやすい

前回の記事では、最も人気のある、絶対に失敗しないセルフサービス Web サイト構築プラットフォーム ...

主人公がブランドのプライベートドメインライブ放送に戻り、ライブ放送の後半を開始します

ショートビデオ、セルフメディア、インフルエンサーのためのワンストップサービス文 | 李永華出典 | ...