仮想マシン[JVM]バイトコード実行エンジン

概念モデル、次のように典型的なスタックフレーム構造である(スレッドスタックはつまり、各スレッドはそれ自身のスタックを有し、プライベートです)。

典型的なスタックフレーム構造
  • ローカル変数テーブル
    保存されたメソッドのパラメータとメソッド定義内のローカル変数をコンパイル時には、クラスコードは、ファイル属性プロセスに必要なデータ項目に割り当てられたmax_localsのローカル変数テーブルの最大容量を決定します。(特定のオブジェクトを含まない変数のみ)。</ BR>
    最小単位として可変内部溝(可変スロット)にローカル変数テーブル。ためbytecharfloatintshortbooleanreferencereturnAddressなどのデータ・タイプのない32ビット以上で、各ローカル変数は二重、スロットを占有し、2つの64ビット長のデータ・タイプは、2つのスロット</ BR>を必要とする
    プロセスで実行された場合実行がインスタンスメソッド(非ある場合、仮想マシンは、変数パラメータのリストを形成するために、ローカル変数のパラメータ値である、static方法)、スロット0のローカル変数テーブルのインデックスは、インスタンスが属するオブジェクトを送信するためのデフォルトの方法でありますメソッドへの参照thisの暗黙のパラメータにアクセスします。</ BR>
    バイトコードカウンタPCの現在の値が変数の範囲を超えている場合、ローカル変数テーブルスロットは、再利用可能であり、他の変数に対応する変数は、再利用をスロットできます。再利用は、スタック領域を節約できますが、それはまた、仮想マシンの動作パラメータに設定さ:(副作用を引き起こす可能性がありますプラス-verbose:gcあなたは、出力することができますgc)ログ情報
//--------------------------测试1---------------------------//
public static void main(String[] args){ byte[] placeholder = new byte[64*1000*1000]; System.gc(); } //查看日志,并未回收 [GC (System.gc()) 69437K->63438K(251392K), 0.0012879 secs] [Full GC (System.gc()) 63438K->63277K(251392K), 0.0058505 secs] //------------------------测试2-----------------------------// public static void main(String[] args) { { byte[] placeholder = new byte[64 * 1000 * 1000]; } System.gc(); } //查看日志,并未回收 [GC (System.gc()) 69437K->63420K(251392K), 0.0011785 secs] [Full GC (System.gc()) 63420K->63277K(251392K), 0.0058676 secs] //------------------------测试3-----------------------------// public static void main(String[] args) { { byte[] placeholder = new byte[64 * 1000 * 1000]; } int a = 0; System.gc(); } //查看日志,回收了 [GC (System.gc()) 69437K->63454K(251392K), 0.0011921 secs] [Full GC (System.gc()) 63454K->777K(251392K), 0.0056915 secs] 

試験1においてSystem.gc()、変数はplaceholder、内部に実質的にまだ再循環されない、試験2 System.gc()、可変placeholderより長い範囲であるが、しかしplaceholder部分的なGCルートの一部ように、まだ、スロット多重化を占有していませんでした変数テーブルは、それに関連付けられたままなので、それは回復しません。この関連の影響が大半でタイムリーにブレークされていない
若干低いですが、方法があるかどうフロントは大量のメモリを定義しているが、長い時間のかかる操作の背後にあるいくつかのコードがあり、もはや実用的でありません使用される変数は、手動でこれをnullに設定する意味があります。</ BR>
もう一つのポイントは、クラス変数、ローカル変数とは異なりデフォルト値が存在しない同じ製剤の存在を(唯一の修飾静的変数、インスタンス変数が含まれていない)、ということです。これは、ローカル変数の初期値として定義する必要があります(指定なし、コンパイラはエラーになります)。

  • オペランドスタック
    、スタック操作として知られているオペランドスタックは、スタック内の最初のものです。前記要素は、以下を含む任意のJavaデータ型、あるlongdouble32ビットのデータ容量の種類数は2ビット1,64です。任意の時点で実行される方法は、オペランドスタックの最大深さは、コードのプロパティ超えないmax_stacks設定された最大のデータ項目を。</ BR>
    メソッドが実行を開始すると、オペランドスタックは、メソッドの実行中に、スタック/スタックからバイトコード命令の様々な構造を有する、空です。例えば、演算を行うときは、方法は、他の送信パラメータを呼び出しスタックはスタックを操作することによって実行される動作させることによって行われます。</ BR>
    例えば、整数加算バイトコード命令iadd実行時に、このコマンドが実行された場合、最も近いスタックの最上位にスタックの動作は2つのint型の値に2つの要素を有する二つの意志その後int値とスタックを追加し、スタックに追加した結果。</ BR>
    概念モデル、二つのフレームスタック内の仮想マシンのスタックの要素が完全に分離されているが、最も最適化された仮想マシンは、2つのスタックフレームを行います達成するように部分的にオーバーラップ表示され、次のスタックフレームを聞かせメソッドが呼び出されるデータの共通部分は、渡されたパラメータの不必要なコピーを回避するように、枠部及びオペランドスタックローカル変数テーブルのオーバーラップの上部をスタック。

  • 動的リンクは、
    各スタックフレームはスタックフレームに関連する方法の実行時定数プールへの参照を含みます。これは、動的な接続メソッドの呼び出しをサポートするための参照を保持しています。メソッドのバイトコードの呼び出し命令(ここでは、「メソッド呼び出しは」modifierコマンドで、それは間違っていない)シンボルパラメータとして定数プールの基準点方式。これらのシンボルは、最初の時間または直接参照として使用される場合、この変換が呼び出されるのクラスローディング・フェーズの一部を引用する翻訳する静的解像度他の部分は、この部分が呼び出され、各動作中に直接参照に変換するダイナミックリンクを

  • メソッドがアドレスを返す
    方法を開始したときに、この方法のうち2つの方法があります。

  • 正常終了出口
    実行エンジンは、メソッドによって返されるバイトコード命令、呼び出し側上部方法に戻り値を検出した(かどうか戻り値の型とリターン命令の方法により決定される値を返します)

  • 異常終了出口
    例外は、メソッドの実行中に発生し、例外が(異常は、Java仮想マシン内で生成することができる、コードに使用することができる方法の本体で処理されていないathrow異常発生バイトコード命令)。</ BR>
    方法は、実際の終了現在のフレームがスタックであるので、出口がある場合の動作を実行することができるローカル変数テーブルとオペランドスタックは、呼び出し元のスタックフレームオペランドに上層方法、戻り値(もしあれば)を回復命令の次のメソッド呼び出し命令を指すようにカウンタPCの値を調整するために、スタック。

  • 追加情報:標準外の、特定の仮想マシンの実装に依存します。


メソッド呼び出し

メソッド呼び出しは、同じメソッドの実行はないコールの唯一の目的は、方法の段階のバージョンを決定することである(つまり、どのメソッド呼び出しである)が呼び出されますクラスファイル内のすべてのメソッド呼び出しは、シンボリック参照を格納している、と直接参照(実際の実行時のメモリレイアウト内のアドレス入力方法は)メソッドではありません。

仮想マシンでは、呼び出すための5つのメソッドのバイトコード命令がある:
invokestatic:静的メソッドの呼び出し;
invokespecial:インスタンスのコンストラクタを呼び出す<init>方法を、およびメソッドを継承し、プライベートメソッド;
invokevirtual:;すべての仮想メソッド呼び出し
invokeinterface、メソッド呼び出しインタフェースを: ;さらに、実行時にこのインターフェースのオブジェクトを決定
invokedynamic実行時に参照される第一の動的メソッド呼び出しポイント修飾子を解析し、この方法を実行します。

呼び出し方法は、解析コールとディスパッチ呼び出しに分けることができます。

  • 解像度コール
    解決フェーズのクラス負荷時、ターゲットメソッドが直接参照にシンボルメソッド呼び出し参照の一部となり、この部分の前提は、形質転換することができる:コールを実行する前のバージョンを決定する方法があり、かつ運転中にこのメソッドのバージョンを呼び出しては変更することはできません。分析と呼ばれるこれらのメソッドを呼び出します(またはコールを解析)。
    Javaで、上記特性(既知のコンパイラ、ランタイム不変)方法に沿って静的メソッドとプライベートメソッド。限りすることができて、命令とそれに対応するメソッドを呼び出すinvokestaticと、invokespecialメソッド呼び出し命令、解決フェーズと呼ばれる唯一のバージョンを決定することです。非仮想メソッドと呼ばれるそのような方法は、他の方法は、仮想メソッドと呼ばれます。
    Javaでは、非仮想メソッドに加えて、invokestatic及びinvokespecial外側をさらに含む、方法指示を呼び出すことができるfinal方法。</ BR>
    解像度の呼び出しは、静的なプロセスである必要があり、完全にコンパイル時に決定することができ、あなたが直接参照する方法へのシンボリック参照の中に置くことができるクラスローダの構文解析ステージは、完了するまでに遅延中に再度実行されません。

  • ディスパッチコール
    ディスパッチ呼はその継承、多態性と重大なカプセル化(特に過負荷にして書き換えを)認識されます。それを理解することが、より明確に仮想マシンがどのように正しいターゲット方法を決定することができます。

  • 静的割り当ては、
    静的な型と実用的なタイプ:最初の二つの概念を理解します。

Human human = new Man();//这里假设Man是Human的子类

上記コード:Human変数の静的な型、Man変数の実際の型。
引数の静的な型ではなく、判断の根拠として実際の型による高負荷で仮想マシン(正確にはコンパイラ)。

public class StaticDispatch {

    static abstract class Human{} static class Man extends Human{} static class Woman extends Human{} public void sayHello(Human human){ System.out.println("hello, human"); } public void sayHello(Man man){ System.out.println("hello, man"); } public void sayHello(Woman woman){ System.out.println("hello, woman"); } @Test public void test(){ Human man = new Man(); Human woman = new Woman(); StaticDispatch dispatch = new StaticDispatch(); dispatch.sayHello(man); dispatch.sayHello(woman); } } //最终的打印结果如下:(重载时是以静态类型判断的) hello, human hello, human 

コンパイラのヘビーデューティーバージョンは方法を決定することができますが、多くの場合には、これは「唯一」の大型版ではありませんが、唯一の「より適切な」バージョンを確認することができます。定義されていないリテラルが必要で、それは静的に型付けされたリテラルれるこの主な理由が表示されていない、その静的な型は、言語だけのルールを理解し、採用することを推測することができます。

public class OverLoad {

        public static void sayHello(Object object){ System.out.println("hello Object"); } public static void sayHello(int a){ System.out.println("hello int"); } public static void sayHello(long a){ System.out.println("hello long"); } public static void sayHello(Character character){ System.out.println("hello Character"); } public static void sayHello(char c){ System.out.println("hello char"); } public static void sayHello(char... c){ System.out.println("hello char..."); } public static void sayHello(Serializable serializable){ System.out.println("hello Serializable"); } @Test public void test(){ sayHello('a'); } } 

このコードが印刷されますhello char
①するsayHello(char c)コメント、プリントアウト:hello int
②続けてsayHello(int a)印刷をコメントアウト:hello long
その理由は、2つのステップの文字でa自動変換(char-> INT->長期>フロートを発生します- >ダブル)
③され続けてsayHello(long a)コメントアウト、それが印刷されます。hello Character
その理由は、文字がいることをされたaように自動的にボックス化されたCharacterタイプ
④続けるsayHello(Character character)コメントし、印刷します:hello Serializable
理由はされてa自動的に箱入りCharacterタイプはまだ自動継続する方法を見つけることができません変換は、Character達成Serializableインタフェースを。
⑤され続けてsayHello(Serializable serializable)コメントアウト、それが印刷されます:hello Object
アッパー低い優先順位に近づいに関する複数の親クラス、階層内のボトムアップ検索は、存在する場合の理由は、文字の後に親クラスのボクシングの転換です。
⑥され続けてsayHello(Object object)コメントアウト、それが印刷されます:hello char...
可視:可変引数オーバーロードされた優先順位が最も低いです。

  • 動的割当て
    動作中、パラメータの実際のタイプに応じて実行される方法のバージョンを決定するプロセス。主にJavaの書き換えに対応します。
public class DynamicDispatch {

        static abstract class Human { abstract void sayHello(); } static class Man extends Human { @Override void sayHello() { System.out.println("man say hello"); } } static class Woman extends Human { @Override void sayHello() { System.out.println("woman say hello"); } } @Test public void test() { Human man = new Man(); Human woman = new Woman(); man.sayHello(); woman.sayHello(); } } //将会打印 man say hello woman say hello 

上記により、静的および動的ディスパッチディスパッチディスパッチは、異なる状況下での方法は、取られない呼び出しのいずれか、または両方異なり、また、メソッド呼び出しの直接参照の決意に起こり得る、静的割り当ての両方を使用、および動的割り当てを使用します。静的な割り当てを使用するかを決定するために使用されるオーバーロードされた方法は、書き換え方法は、動的ディスパッチであるときを決定することです。それがされた静的な型パラメータを参照して、オーバーロード、実際の型パラメータを見て書き換えますここではパラメータ、オーバーロードしたときには、メソッドのパラメータリスト内のパラメータである、書き換えがメソッドの呼び出し元を指します。

  • 単一および複数のディスパッチディスパッチ
    パラメータおよび方法レシピエント方法は、単一のディスパッチに分割し、複数のディスパッチ二種類をディスパッチすることができるか、多くの場合に基づいて割り当てられた量に応じて、方法の変数と呼びます。
    Java言語はに属し、静的複数の派遣、ダイナミック単一代入言語。

  • 仮想マシンの動的な割り当て

  • 動的型付け言語サポート:TODO


解釈実行エンジン// TODOスタックベースのバイトコード

これは、仮想マシンのバイトコード命令のメソッドを実装する方法に焦点を当てています。ここでは、二つの選択肢の多くのJavaコードの実装では、Java仮想マシンの実行エンジンが(インタプリタ実行を通じて)、実行(リアルタイムを実行するために、コンパイラによって生成されたネイティブコード)を解釈し、コンパイルされたが議論に解釈。

解釈
スタックベースの命令セットとレジスタベースの命令セット
スタックベースインタプリタ実行プロセス

著者:maxwellyue
リンクします。https://www.jianshu.com/p/3f427d7476b3
出典:ジェーンの本

おすすめ

転載: www.cnblogs.com/xiaoshen666/p/11258569.html