クラスファイル構造の詳細な説明

最前線で書かれていること:クラスファイル構造を学ぶことは、JVMメモリ構造やガベージコレクターを学ぶこととは異なります。コードを書くときに大いに役立ちます。JVMメモリ構造を学んだ後、仮想を構成するときに、より包括的な考慮事項があります。マシンパラメータ。、コードを書くとき、コードの最適化スペースに気づき、ガベージコレクターを学習することができます。これにより、サーバー構成に応じて、プログラムの最大スループットに適したコレクターをより適切に選択できます。サーバーのハードウェア構成。適切なパラメーター、学習クラス、それは理由と理由を私たちに知らせるためのものです。私たちが書いたコードがJVMでどのように実行されるかを私たちに知らせてください。コンテンツのこの部分は比較的退屈でありふれたものになります。クラスファイルの構成[クラスファイル構造、バイトコード命令]を詳細に説明するために、主に2つの部分に分かれています。

1.クラスファイルの構造

コンテンツのこの部分は退屈ですが、JDK1.0以降、コンテンツのこの部分があまり変更されていない方が友好的です。この部分をマスターしている限り、これはすべてのことと言えます。 JVMメモリ分散とは異なり、コレクターが変更されても、JDKバージョンの各世代が前のバージョンと互換性があるため、コレクターのように常に更新されるわけではありません。前のプログラムがで実行可能であることを確認する必要があります。 JDKの新しいバージョン。これにより、クラスファイル構造の相対的な安定性も保証されます。クラスファイルのさまざまな構造と機能を見てみましょう。

  1. クラスファイルとは何ですか?
    JVMはjava形式のファイルを認識しません。実行できるのはクラスファイルだけです。javaプログラムはjavacコンパイラを使用してjavaファイルをクラスファイルに変換し、仮想マシンで実行できます。すべての仮想マシンはクラスファイルを実行します。、これも1回のコンパイル後に、Javaファイルを他のマシンで実行可能にします。つまり、JavaファイルはどこでもJava言語で実行されます。クラスファイルは、8バイトに基づくバイナリストリームのセットであり、各データ項目(マジックナンバー、バージョン...)は厳密な順序で配置され、中央にスペーサーはなく、8バイトを超えるストレージが検出されます。その際、上位順に数8バイトに分割して格納します。これがクラスファイルです。つまり、クラスファイルとは、Javaの後にJVMが認識できる情報を格納するファイルです。コンパイル。その中のデータは8バイトです。1つの単位として、各データ項目は間隔なしで整然と配置されます。

  2. 符号なし番号とは何ですか?テーブル?セットする?
    クラスファイルの特定の構造について説明する前に、まずこれら3つの構造を紹介する必要があります。これは、クラス内の情報がこれら3つの構造に格納されているためです。
    符号なし数値:名前が示すように、符号なし数値は、基本的なデータ型である符号なしデータのグループです。符号なし数値には、1バイト(u1)、2バイト(u2)、および4バイト(u4)があります。)、 8バイト(u8)これらの構造は、主に数値、インデックス参照、数量値、または文字列値(UTF-8エンコード後)を格納するために使用されます。これは、クラスファイルに格納されるデータの最小単位です。
    テーブル:符号なし数値は、クラスファイルに格納されるデータの最小単位です。テーブルは、複数の符号なし数値で構成されるデータ型です(複数のテーブルで構成されるデータ項目はテーブルとも呼ばれます)。テーブルの命名規則は「_info」で終わります。 "。主に、階層関係を持つ複合構造データを記述するために使用されます。クラスファイル全体を実際にはテーブルと見なすことができます(すべての種類の情報は階層的に格納されます)。
    セット:セットは実際には符号なしの数値またはテーブルです。複数の符号なしの数値またはテーブルを格納するため、セットと呼ばれます。複数のストレージがあるため、仮想マシンは特定の符号なしの数または格納されているテーブルの数を知る必要があります。したがって、コレクションの先頭にu2タイプのデータがあり、容量と呼ばれるコレクション内のデータ項目の数を格納します。セットを構成するカウンター。名前が示すように、コレクションは同じタイプの複数のデータを格納するために使用されます。
    これらの3つの構造を要約すると、実際に格納される最小の構造は符号なしの数値であることがわかります。複数の符号なしの数値がテーブルを形成し、複数の符号なしの数値と容量前のカウンターがセットを形成します。それらの本質は、符号なしの数値の変更です。

  3. クラスファイルのマジックナンバーとバージョン番号
    マジックナンバー(マジックナンバー):最初の番号はマジックの固有の役割です。マジックナンバーu4構造は、ファイルが実行可能クラスであるかどうかを区別するために、仮想マシンのクラスファイルに格納されます。ファイルの種類を識別するためのサフィックスはありませんか?マジックナンバーを使用するのは冗長ではありませんか?実際、冗長ではありません。ファイルの識別にマジックナンバーを使用すると、サフィックス名を変更できるため、セキュリティが向上します。クラスファイルのマジックナンバーは0xCAFEBABEであり、クラスファイルだけでなくマジックナンバーもあります。たとえば、jpg、jpeg、png、gif、zip、jarなどの一般的なサフィックスにはマジックナンバーがあります。次の図では、WinHex 16進数を使用しています。システムエディタはクラスファイルを開いて、それが0xCAFEBABEであるかどうかを確認します。これは明らかです。
    ここに画像の説明を挿入します

    バージョン番号バージョン番号はマジックナンバーの横に格納されます。バージョン番号はu4を占有します。最初の2つのu2はマイナーバージョンを格納し、後者のu2はメジャーバージョンを格納します。マイナーバージョンは実際にはJDK1.2とJDK1.2の間では役に立ちません。 JDK12。。JDK12以降でのみ、Javaファイルでプレビュー機能が使用されている場合、マイナーバージョン番号は生成されたクラスファイルに65535として保存されます。これは、現在のバージョンの唯一の有用性でもあります。メインバージョンはより重要な項目であり、JDKのバージョン番号を格納します。この値はJDK1.0およびJDK1.1で使用されます。45。その後、JDKはラージバージョンをリリースします。この値はそれに応じて1ずつ増加します。一般的に使用されるJDK8の場合、値は52です。JDKのバージョン番号を指定する必要があるのはなぜですか?仮想マシンは下位互換性が必要なため、前の仮想マシンによってコンパイルされたファイルは現在の仮想マシンで実行可能である必要があります仮想マシン。さらに、仮想マシンは、現在の仮想マシンのバージョンよりも高いクラスファイルの実行を拒否します。つまり、JDK8仮想マシンはJDK9によってコンパイルされたクラスファイルを実行できませんが、JDK7、JDK6などの前に仮想マシンによってコンパイルされたクラスファイルは実行できます。
    次のバージョンとメジャーバージョンを以下に示します。これは16進数で開かれたファイルです。34を10進数に変換すると、52になります。
    ここに画像の説明を挿入します
    マジックナンバーとバージョン番号をまとめると、仮想マシンが修正されるとこのコンテンツが修正され、ファイルが異なるために同じ仮想マシンに違いがないことがわかります(JDK12より前)。クラスファイルがグループであること8バイトに基づくバイナリファイルの場合、マジックナンバーとバージョン番号は最初の8バイトを占めるデータ項目です。

  4. コンスタントプールコンスタントプール
    とは何ですか?
    ここで説明する定数プールはクラス定数プールです。一般的な定数プールには、クラス定数プール、ランタイム定数プール、および文字列定数プールが含まれます。これらの3つの定数プールは3つです。簡単に言うと、クラス定数プールはクラスファイルに格納される静的データであり、メソッド領域のランタイム定数プールはロード後にクラスファイルのリテラルとシンボルを格納します。 、ヒープ内の文字列定数プール。文字列定数の格納専用です。家の近くでは、定数プール(クラス定数プール)とは何ですか?定数プールは、その名前が示すように、定数を格納するためのプールです。クラスファイル内のすべての定数は、このプールに格納されます。これは、クラスファイルのリソースウェアハウスであり、他のアイテムと最も交差する構造です。格納される主な定数は、リテラル、シンボル参照です。リテラルは、クラスで定義された定数、文字列などであり(ローカル変数intなどの定数、クラスはロード後にローカル変数テーブルに格納され、文字列は文字列定数プールに入ります)、シンボリック参照はクラスまたはインターフェイスの完全修飾名、メソッドとフィールドの名前と記述子、メソッドのハンドルタイプ、およびその他の情報。これは定数プールです。定数プール
    の特徴:
    定数プールは、クラスファイルの最初のテーブル構造データです。前述のように、テーブルは複数の符号なし数値または複数のテーブル構造で構成されています。定数プールは多数のテーブルで構成されており、複数のテーブルで構成されているため、定数プール内のテーブル数を格納するタイプu2の容量カウンターから始まります。定数プールの容量カウンターは他とは異なります。 1からの実際のカウントは、各定数のインデックスを表します。0はテーブルを指しませんが、「定数プール内のアイテムを参照しない」という意味を表します。次の図では、定数プールの数は0x2Bであり、これは10進数を表します43は、定数プールに42個のテーブル構造データがあることを示します。
    ここに画像の説明を挿入します
    javap -verboseの後にクラスファイル名を使用して、クラスファイルのバイトコードコンテンツを表示できます。ファイルのバイトコード情報定数プールが42個の定数であるかどうかを見てみましょう。情報は次のとおりです。定数プールには合計42個の定数(テーブル)が格納されていることがはっきりとわかります。
    ここに画像の説明を挿入します
    最初に上記の定数ビリヤード台の構造を説明します。最初の列#1、#2などはインデックス番号とストレージ番号です。3番目の列Methodref、String、Fieldrefはテーブルタイプを格納し、4番目の列はインデックス番号。現在のテーブルに格納されている情報です。5列目の二重スラッシュの後、現在の構造に格納されている特定の値はコメントに相当します。上の図から、42個あることがわかります。図に示すように、定数プールには定数テーブルがあります。テーブルにはUtf8、String、Fieldref、Methodrefなどのテーブルがあります。定数プールにはどのようなテーブル構造がありますか?
    定数プールのテーブル構造は何ですか?
    定数プール(JDK13現在)には合計17のテーブル構造があり、クラス内のリテラルおよびシンボリック参照を格納するために使用されます。この情報は、仮想マシンのインデックス番号に従って仮想マシンにロードされます。これらの17種類のテーブル構造はすべてのjava情報をカバーし、すべてのテーブルは次のとおりです。
    ここに画像の説明を挿入します
    各テーブル構造のストレージ構造が異なるため、各テーブル構造を導入することは現実的ではありません。私の意見では、テーブル構造を上手に習得するために必要なのは、これらのテーブル構造と何を格納するかを知っていることだけです。これらのテーブル構造の詳細な説明は、必要なときに参照できるように以下に添付されています。
    ここに画像の説明を挿入します
    ここに画像の説明を挿入します
    ここに画像の説明を挿入します


  5. 定数プールのu2型データの隣のフラグビットはフラグビットです。フラグビットは、現在のクラスまたはインターフェイスのアクセスフラグを格納するために使用されます。パブリックかどうか、抽象かどうか、抽象かどうか。はファイナルなどです。全部で9種類あります。変更情報は下図のようになります。
    ここに画像の説明を挿入します
    フラグACC_SUPERは特殊で、JDK1.0.2以降はtrueである必要があるため、フラグビットの最小値は0x0020です。複数のフラグが真の場合、フラグビットはそれをどのように表しますか?複数のフラグビットが真の場合、対応するフラグ値が追加され、取得された値はフラグビットの表示値です。どのフラグがフラグビットによって表示される値を介してtrue。

  6. クラスインデックス、親クラスインデックス、およびインターフェイスインデックスコレクションの
    保存情報は、主に、現在のクラスの完全修飾名、親クラスの完全修飾名、および実装されたインターフェイスの完全修飾名を決定するためのものです。ここで、誰かが間違いなく質問をします。定数プールで宣言された情報のこの部分ではありませんか?これらはすべてシンボリック参照です。前述のように、定数プールは、クラスの完全修飾名が存在するリソースウェアハウスと同等です。、親クラスの完全修飾名、およびインターフェースの完全修飾名が参照されますすべては定数プール内の情報です。クラスインデックスと親クラスインデックスはそれぞれu2タイプのデータストレージを使用します。Javaは複数の実装をサポートしているため、インターフェイスインデックスコレクションはストレージに複数のu2タイプのデータを使用します。

  7. フィールドテーブルコレクション
    これはデータのコレクション構造です。コレクションの定義はすでに紹介しました。コレクションは、同じデータ構造または複数のテーブルの符号なしの数値と、事前容量カウンターで構成されます。フィールドテーブルのコレクション構造は当然このようになり、容量カウンターと複数のフィールドテーブルで構成されます。
    フィールドテーブルとは何ですか?
    フィールドテーブルは、クラスまたはインターフェイスで宣言された変数を記述するために使用されます。Java言語では、変数はデフォルトでクラス変数とインスタンス変数を参照し、ローカル変数を含みません。したがって、フィールドテーブルは間違いなくローカル変数を格納しません。 。フィールドテーブルは3つの部分に分割され、各部分はu2構造を占めます。アクセスフラグ、フィールドの単純名(フィールド名)、フィールド記述子(説明フィールドタイプ)です。下の図に示すように:
    ここに画像の説明を挿入します
    上の図はフィールドテーブルの構造図ですが、フィールドテーブルであろうとメソッドテーブルであろうと、対応する属性テーブルセットが背後にある場合があります。このセットについて詳しく説明します。以下で、属性テーブルを知る必要があるだけです。コレクションはフィールドテーブルまたはメソッドテーブルの補足であり、単独では存在しません。さらに、フィールドテーブルの3つの部分を個別に記載する必要があります。
    アクセスフラグ:このアクセスフラグは、クラスファイルのアクセスフラグと非常によく似ており、どちらもアクセス修飾子を示すために使用されます。フィールドテーブルのアクセス修飾子は次のとおりです。対応するu2データから、フィールドテーブルに記述されているフィールドのアクセス修飾子を簡単に推測できます。
    ここに画像の説明を挿入します
    フィールドの単純な名前(name_index):name_indexは、定数プールにインデックス番号を格納します。このインデックス番号に対応する定数は、フィールドの単純な名前です。
    フィールド記述子(descriptor_index):Descriptor_indexは、定数プールにインデックス番号を格納し、このインデックス番号に対応する定数は、記述子の識別子を格納します。記述子と識別子の関係を次に示します。
    ここに画像の説明を挿入します
    上記のように、定義された型がバイト型の場合、定数プールに実際に格納されているフィールド記述子の値はBです。
    フィールドテーブルコレクションの概要上記から、フィールドテーブルの構造は比較的複雑であることがわかります。ここでは、フィールドテーブルの構造をより鮮明に示す例を示します。以下に示すように:
    ここに画像の説明を挿入します
    ここに画像の説明を挿入します
    上の図はフィールドテーブルの情報です。fields_countが1の場合、容量カウンターが1であることを示し、フィールドテーブルが1つしかないことを示し、access_flagsが0x0002であり、オブジェクトがプライベートであり、name_indexが定数に格納されていることがわかります。上の2番目の図でわかるように、フィールドの単純な名前はmであり、descriptor_indexはインデックス番号を定数プールに格納します。上の2番目の図から、定数がIであり、比較されていることがわかります。フィールド記述子の識別子を使用してテーブルから、Iがintを表すことがわかります。したがって、このフィールドテーブルにはインスタンス変数private intmが格納されます。

  8. Fangは、
    テーブルのフィールドマスターセット、実際にはテーブルのセットもメソッドをマスターした場合にコレクションを公開しました。これは、テーブルの周囲の情報フィールドが比較に格納されているため、またはマスターするのに少し時間がかかるためです。テーブルセットテーブルメソッドもOKです。基本的にこれら2つの項目に違いはありません。メソッドテーブルセットは、容量カウンター、複数のメソッドテーブル、および属性テーブルのセットで構成されます。メソッドテーブルには、アクセスフラグ、メソッド名インデックス、およびメソッド記述子インデックスの3つの部分(フィールドテーブルと同じ)もあります。基本的にはフィールドテーブルと同じですが、メソッドのコードはコードの属性テーブルに格納されるため、各パーティが独自の属性テーブルのセットを持つという矛盾があります(コードのないメソッドはほとんどありません)。
    以下は、メソッドテーブルのすべてのアクセスフラグ
    ここに画像の説明を挿入します
    です。ここでは、メソッドテーブルの3つの部分について詳しくは説明しません。また、フィールドテーブルとの違いはありません。

  9. 属性テーブルコレクション
    属性テーブルコレクションは、クラスファイル構造に導入される最後のアイテムです。属性テーブルコレクションは、単独では存在しません。フィールドテーブルおよびパーティパブリケーションと組み合わせて使用​​されます。これらのテーブルの補足として、たとえば、共通メソッドのコンパイル済みコードはコード属性テーブルに格納され、メソッドで定義された例外は例外テーブルに格納されます。同じ内容も多く、単独で実装されたコンパイラは、新しい属性をサポートします。属性テーブルコレクションに加えて、上記で紹介したいくつかの構造に格納されていない情報は、基本的にこの属性テーブルにあります。すべての属性テーブルは、参照用に以下にリストされています。私の意見では、各属性テーブルが何をすべきかを知るために必要なのは、ここに保存されている情報だけです。
    ここに画像の説明を挿入します
    ここに画像の説明を挿入します

2.バイトコード命令の紹介

誰かが紹介のこの部分を非常に注意深く読んだ場合、私はまだそれを賞賛します。この部分は本当に退屈なので、上記の部分を読んでも心配しないでください。次の部分もありますが、これも比較的退屈です。

  1. バイトコード命令とは何ですか?
    バイトコード命令はオペレーティングシステムの概念であり、通常、①オペコード②オペランドの2つの部分で構成されます。オペコードは、1バイトを占有し、特定の特別な意味を表す2進数です。値は、特定の操作を実行するために事前定義されています。オペランドは通常、オペコードの後に​​続き、メソッドの入力パラメーターと同様に、0以上にすることができます。それらが一緒になってバイトコード命令を形成します。JVM仮想マシンで使用されるのは、オペランドスタック指向アーキテクチャです。したがって、オペコードのみが存在し、オペランドは通常、オペランドスタックに格納され、オペコードは仮想マシンの命令を完了するために使用されます。

  2. オペコードの種類
    私たちが作成するすべてのプログラムは、オペコードで完了する必要があります。オブジェクトの作成、型変換、データの加算、減算、乗算、除算の処理、メソッド呼び出し、例外処理、同期など、通常作成するプログラムを思い出すことができます。メソッド、コードブロックの同期など。これらの考えられる操作は、オペコードとオペランドの協力によって完了します。さらに、オペコードの命名は、可能な限り明確にするために、一般的に可能な限りデータ型に関連しています。たとえば、int型に対応する加算、減算、乗算、除算のオペコードは、iadd、isub、imul、idivです。したがって、これらのオペコードはint型で動作することが一目でわかります。一般的に使用されるオペコードをいくつか示します。

  3. オブジェクト作成関連の命令
    通常のオブジェクト:新しい
    配列オブジェクト:newarray、anewarray、multianewarray
    変数値の設定および変数値へのアクセス命令:getfield、putfield、getstatic(クラス変数)、putstatic(クラス変数)
    値を(オペランドスタックに)格納します配列指示:bastore、catore、sastore、iastore、fastore。
    配列長の命令を取得する:arraylength
    javaのシンタックスシュガーによって実装されていない関数である限り、実際には対応する命令があります。これらの命令は多数あり、完全に投稿する必要はありません。私の意見では、私たちはいくつかの一般的なものを習得する必要があるだけです。いくつかの一般的なコマンドは、引き続き以下にリストされます。

  4. データ演算
    関連する命令追加:iadd、fadd、ladd、dadd
    減算:isub、fsub、lsub、dsub
    乗算:imul、fmul、lmul、dmul
    除算:idiv、fdiv、ldiv、ddiv
    剰余:irem、frem、lrem、drem
    逆:ineg、fneg、lneg、dneg
    など、byte、short、boolean、charはすべてintを使用する操作命令であり、4つには個別の操作命令はありませんが、格納されます(ローカル変数テーブル、オペランドstack In)では、まだ保存されているのは独自のタイプです。

  5. メソッド呼び出しとメソッド戻り命令
    は比較的重要です。Javaでの解析とディスパッチはメソッド呼び出し命令と密接に関連しているため、この部分を覚えておく必要があります。以下では、これらの命令について詳しく説明します(解析とディスパッチについて言及する必要があります。ここでは説明しません)。
    ①仮想命令の呼び出し:オブジェクトのインスタンスメソッドを呼び出し、オブジェクトの実際のタイプに従ってディスパッチするために使用されます(仮想メソッドディスパッチ)。
    このコマンドをテストし、次のコードを記述しました。

    public class TestSuper {
          
          
    
    	public TestSuper(){
          
          
    
    	}
    
    	public void test(){
          
          
    
    	}
    
    	public static void mian(String[] args){
          
          
    		TestSuper testSuper = new TestSuper();
    		testSuper.test();
    	}
    
    }
    

    次の図に示すように、我々は、クラスのバイトコード情報を見ててjavap命令を使用し、このコード、および、INVOKEVIRTUAL命令の説明によれば、我々はtestSuper.test()を実行するとき、この命令を使用しなければならない
    ここに画像の説明を挿入します
    から上の図では、次のことができます。テストメソッドを呼び出すときにinvokevirtual命令が使用されていることは簡単にわかります。
    ②Invokeinterface命令:インターフェースメソッドを呼び出すために使用され、実行時にインターフェースの実装オブジェクトを検索し、呼び出すのに適したメソッドを見つけます。上記の方法は検証に使用でき、ここでは繰り返し作業は行われません。
    ③Invokespecial命令:コンストラクター、プライベートメソッド、スーパークラスコンストラクター、スーパークラスメソッドなど、特別な処理を必要とする一部のインスタンスメソッドを呼び出すために使用されます。これらはすべて、このコマンドに依存して実行する必要があります。これも十分に検証されていますが、必要です。次に、小さな拡張機能を作成しましょう。これとsuperがJavaのキーワードであることは誰もが知っています。このキーワードの実装は暗黙的なパラメーターの受け渡しであるため、superについては、thisキーワードを解析して暗黙のパラメーターの受け渡しを検証したり、superが暗黙的なパラメーターの受け渡しの検証ではないことを検証したりしないでください。これを理解したい場合は、関連情報を確認するか、プライベートで私とチャットすることができます。superの実現メカニズムは、暗黙的なパラメーターの受け渡しではないとだけ言っておきましょう。コンストラクターを持つinvokevirtual命令のコードを見てみましょう。このコンストラクターに対応するバイトコード情報も見てみましょう。
    ここに画像の説明を挿入します
    図で赤でマークされているのは、superキーワードの基になる実装です。この命令行から、現在の命令がObjectのパラメーターなしのコンストラクターを呼び出していることがわかります。コンパイラがコンパイルされた後、それはinvokespecial命令として解釈され、親クラスコンストラクタを呼び出すためのパラメータを運びます。これはsuperの実装メカニズムです。これとsuperが暗黙的に渡されるわけではありません。 。
    ④呼び出し静的命令:名前から、この命令は特にクラスメソッドを呼び出すために使用されることを誰もが理解する必要があります。ここで検証を繰り返さないでください。
    ⑤Invokedynamic命令:実行時にコールポイント修飾子によって参照されるメソッドを動的に分析し、メソッドを実行するために使用されます。私たちが使用するJDK8のlamdbaは、この命令に依存することによってのみ実装されます。

  6. 同期命令
    一般的に使用される同期キーワードなど、コードを記述するときに同期操作を使用することがよくあります。仮想マシンは同期命令に対応するキーワードも提供しますが、メソッドの同期は完了する命令に依存しません。代わりに、メソッドが同期されているかどうかを示す修飾子ACC_SYNCRONIZEDがメソッドテーブルにあります。 。同期メソッドが実行される場合、メソッドのスレッドはロックを保持する必要があります。ロックは実行後に解放され、他のスレッドはロックを取得する機会があります。ただし、コードブロックの同期を完了するには、バイトコード命令が必要です。仮想マシンは、キーワード同期をサポートするために、monitorenterとmonitorexitの2つの命令を提供します。

3.全文要約

この記事では、クラスファイルの構造を紹介します。このセクションでは、作成したコードがコンパイラによってコンパイルされた後の各情報ストレージの方法と場所を知ることができます。次に、一般的に使用されるバイトコード命令の一部であるバイトコード命令を紹介します。メソッドは実際に操作を実装する場所であるため、メソッド本体の情報により対応します。したがって、ほとんどのバイトコード命令は、バイトコードファイルのメソッドテーブルのコード属性テーブルにも反映されます。このセクションでは、さまざまなメソッド呼び出しがどのように完了するか、加算、減算、乗算、除算がどのように実装されるかなど、メソッドの基本的な実装がどのように見えるかを理解できます。これは、バイトコード命令が与えられたときにメソッドの操作を完了する方法を補足する簡単な例です。コードは次のとおりです。

public class TestByteCode {
    
    
    public static void main(String[] args){
    
    
        int a = 1;
        int b = 2;
        int c = a + b;
        System.out.println(c);
    }
}

次のように、mainメソッドによって公開された情報を見てみましょう
ここに画像の説明を挿入します
。iconst_1は値1をオペランドスタックにプッシュすることを意味し、istore_1は定数メソッドローカル変数テーブル1をローカル変数aに
割り当てることを意味し、iconst_2は値2をオペランドにプッシュすることを意味しますスタック、istore_2は、1の定数メソッドローカル変数テーブルをローカル変数bに割り当てることを意味します
。iload_1とiload_2は、2つの部分aとbをオペランドスタックにロードして、計算の準備をします。iaddは、これら2つのint型変数を計算します。istore_3Putローカル変数テーブルのcに値3を入力すると、操作が完了して返されます(LineNumberTableは属性情報であり、これとは関係ありません)。

おすすめ

転載: blog.csdn.net/m0_46897923/article/details/114268253