私が学んだのJavaバイトコードの解析から、

引数とパラメータ

public int sum(int x,int y) {
	return x+y;
}

sum(2,3);
复制代码

和上記のコードxの()メソッド、yはパラメータであり、この方法は、和(2,3)2を呼び出し、3引数です。メソッド定義の段階でのパラメータが、引数が実際にメソッド呼び出し段階です。

ビューバイトコード

コールの種類の基本的なパラメータ

private static int intStatic = 222;

public static void main(String[] args) {
    method(intStatic);
    System.out.println(intStatic);
}

public static void method(int intStatic) {
    intStatic = 777;
}

复制代码

方法()メソッド(上記バイトコードjavap -verbose XXX.class)を次のように

public static void method(int);
  descriptor: (I)V
  flags: ACC_PUBLIC, ACC_STATIC
  Code:
    stack=1, locals=1, args_size=1
       0: sipush        777
       3: istore_0
       4: return
    LineNumberTable:
      line 13: 0
      line 14: 4
    LocalVariableTable:
      Start  Length  Slot  Name   Signature
          0       5     0 intStatic   I
复制代码

sipush:プッシュスタックに短い一定の値を整形します

半角は、スタックプッシュsipushに一定値(-128-127)を示すプッシュbipush形態-1-5 ICONST int型スタックは、短整数の定数値(-32768-32767)はスタックにプッシュされ表します

istore_0:最初のローカル変数にスタックint型の値の最上位。

上記バイトコード777がスタックにプッシュし、ローカル変数intStaticに割り当てていることを意味します。だから、私たちの出力の結果が代入()メソッドは、ローカル変数の代入であるので、近接の原則のJava変数である静的変数の値を変更しません、222で、当然のことながら、このような方法Class.intStatic表示文で使用することができます。

呼ばれる不変オブジェクトパラメータ

private static String stringStatic = "old string";

public static void main(String[] args) {
    method(stringStatic);
    System.out.println(stringStatic);
}
public static void method(String stringStatic) {
    stringStatic = "new string";
}

复制代码

上記の結果が出力されold string、同じ外観の逆コンパイルバイトコード

Constant pool:
  #6 = String     #35          // new string
  #7 = String     #36          // old string
  #10 = Utf8      stringStatic
  #35 = Utf8      new string
  #36 = Utf8      old string

public static void method(java.lang.String);
  descriptor: (Ljava/lang/String;)V
  flags: ACC_PUBLIC, ACC_STATIC
  Code:
    stack=1, locals=1, args_size=1
       0: ldc           #6       // String new string
       2: astore_0
       3: return
    LocalVariableTable:
        Start  Length  Slot     Name             Signature
          0      4      0    stringStatic   Ljava/lang/String;
复制代码

LDC:定数プールから整数、浮動小数点、文字列定数の値はスタックにプッシュされます。astore_0:最初のローカル変数に格納された積層型の基準値の最上位。

これは、バイトコード値#6(符号)をスタックにプッシュし、第1の参照型のローカル変数(stringStatic)にその値を割り当てることを意味します。

ここでは、#6は、データの種類を表して見ることができる定数プールCONSTANT_Utf8_infoを指す文字列は、(略称UTF8、クラス、メソッド、フィールドのファイルはので、名前を説明するために参照する必要があります)を入力され、この定数は、クラス(またはインタフェース)を表し完全修飾名、実行時間、クラスのインスタンスの完全修飾名に基づいて、JVM、時間を直接参照(アドレスがメモリ内にある)に変換されたシンボルへの参照。

ランタイム定数プール

上記と言われている定数プール、そして最終的には定数プールはどのようなものがありますか?

私たちは皆、各スレッドは、仮想マシン、定数、静的変数にロードされたクラスの情報を格納するために使用された共有メモリ領域、であるとしてメソッド領域とJavaヒープは、タイムコンパイラがコードをコンパイルすることを知っています。クラス、フィールド、メソッド、およびインターフェース記述情報に加えて、クラスファイルのバージョンを、定数プール(定数プールテーブル)があり、様々なリテラル符号を格納するため、コンパイルこの部分の意志を生成クラス時定数プールの記憶領域の操作方法にロードした後。クラス定数プールへの実行時定数プールファイルの相対的なもう一つの重要な特徴は、ダイナミック装備され、Java言語が必ずしも唯一のコンパイラが生成定数必要としない、それはゾーン法を入力する定数プールコンテンツのクラスファイルを事前に設定されていません実行時定数プールはまた、動作中にプールに新しい定数とすることができる、String.intern()メソッドは、典型的な代表です。

生成と永久的なメソッド領域は、多くの人が混同されます。メソッド領域は、単に永久に達成を代入し、JVMの仕様です。定数プールからJDK7の開始は、メソッド領域にスタックから移動されました

最終的な一定値として宣言された文字列は、リテラルであり、シンボルは、次の3つのクラスおよびインタフェースの完全修飾名1. 2.フィールド名と記述子の記述子3の方法と名前を定数参照します

Constant pool:
  #1 = Methodref    #9.#28     // java/lang/Object."<init>":()V
  #2 = Fieldref     #8.#29     // com/generalthink/kafka/ParamDemo.stringStatic:Ljava/lang/String;
  #3 = Methodref    #8.#30     // com/generalthink/kafka/ParamDemo.method:(Ljava/lang/String;)V
  #8 = Class        #37        // com/generalthink/kafka/ParamDemo
复制代码

そのため、コンパイラのシンボルはstringStatic参照リテラル古い文字列は定数プールのクラスファイルに追加して、ロード・フェーズを入力され、2つの定数は、実行時定数プールに入ります。

変数パラメータオブジェクト

渡された上記のパラメータは、ここで再び我々の次の目標変数の分析となり、不変オブジェクトであります

private static StringBuilder stringBuilderStatic = new StringBuilder("old stringBuilder");

public static void main(String[] args) {
    method(stringBuilderStatic);
    System.out.println(stringBuilderStatic);
}

public static void method(StringBuilder stringBuilderStaticParam) {
    stringBuilderStaticParam.append(" first append");

    stringBuilderStaticParam = new StringBuilder("new stringBuilder");
    stringBuilderStaticParam.append(" new method's append");
}
复制代码

次のようにコードセクションに対応する表示キーワード:

Constant pool:
  #6 = String     #41            //  first append
  #8 = Class      #43            // java/lang/StringBuilder
  #9 = String     #44            // new stringBuilder
  #11 = String    #46            // new method's append
  #12 = String    #47            // old stringBuilder
  #15 = Utf8      stringBuilderStatic
  #30 = Utf8      stringBuilderStaticParam
  #41 = Utf8      first append
  #43 = Utf8      java/lang/StringBuilder
  #44 = Utf8      new stringBuilder
  #46 = Utf8      new method's append
  #47 = Utf8      old stringBuilder

public static void method(java.lang.StringBuilder);
  descriptor: (Ljava/lang/StringBuilder;)V
  flags: ACC_PUBLIC, ACC_STATIC
  Code:
    stack=3, locals=1, args_size=1
       0: aload_0
       1: ldc           #6    // String  first append
       3: invokevirtual #7    // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
       6: pop
       7: new           #8    // class java/lang/StringBuilder
      10: dup
      11: ldc           #9    // String new stringBuilder
      13: invokespecial #10   // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
      16: astore_0
      17: aload_0
      18: ldc           #11   // String new method's append
      20: invokevirtual #7    // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      23: pop
      24: return
    LocalVariableTable:
      Start  Length  	Slot  		Name   						Signature
        0      25      0 		stringBuilderStaticParam   Ljava/lang/StringBuilder;
复制代码

aload_0:最初のローカル参照型の変数は、スタックにプッシュ。LDC:定数プールから整数、浮動小数点、文字列定数の値はスタックにプッシュされます。INVOKEVIRTUAL:メソッドを呼び出すためのいくつかの例は、インスタンスの初期化方法を含む特別な処理を必要とし、親プライベートメソッドメソッドポップは:スタック値(長くないか、double型)新しいのトップポップ:オブジェクトを作成し、基準値スタックDUPに:レプリケーションとスタックastore_0にスタック値の値をコピー:スタックの最上部を局所型変数リターンにおける第一の基準値:現在の方法からの戻り空隙

aload_0 0を指すことに留意されたいLocalVariableTableパラメータのスロット0、stringBuilderStaticParamと呼びます。これは静的変数はローカル変数テーブル、仮想マシンのスタックフレームに割り当てられているを参照することです。

上記バイトコードstringBuilderStaticParamがスタックにプッシュし、その後第一、スタックに一定の値を追加する方法は、スタック最終的StringBuilder.append結果が得られたコール、およびを押すことを意味します。次に、新しいStringBuilderを、スタックに戻りアドレスのコピー、stringBuilderStaticParamにこのアドレス。Aload再度スタック操作appendメソッドを呼び出すことで、その後、最終的にvoidを返す、その後、(ここでは値が上書き、クラスstringBuilderStaticParam stringBuilderStatic何もの静的変数のため、その後の追加操作されています)。

stringBuilderStaticだけで、それはメモリアドレスのみメモリ内の特定のアドレスへのポインタへのポインタではないことに注意してください。Javaの仕様では、と宣言し、Javaのすべてが値によって渡され、参照コードによって渡されることはありませんこのようなものは、その後、実行メソッドメソッドの後に、結果を出力する必要があります今、参照によって渡されることを想定し、テストの仕上げのための唯一の基準ですnew stringBuilder new method's append、しかし、結果は出力されませんので、渡された引数は値によって渡され、この値は唯一のオブジェクトへのポインタです。

これはどのように行われています

int m = 1;
public static void main(String[] args) {
    ParamDemo demo = new ParamDemo();
    System.out.println(demo.method());
}
public  int method() {
    return m + 1;
}
复制代码

私たちは、メートルのメソッド()メソッドを呼び出して、ここでは暗黙のうちに実際には、this.m.あり、これを使用しています したがって、この式は、それを達成するためにどのように?

public int method();
  descriptor: ()I
  flags: ACC_PUBLIC
  Code:
    stack=2, locals=1, args_size=1
       0: aload_0
       1: getfield      #2     // Field m:I
       4: iconst_1
       5: iadd
       6: ireturn
    LineNumberTable:
      line 15: 0
    LocalVariableTable:
      Start  Length  Slot  Name   Signature
          0       7     0  this   Lcom/generalthink/kafka/ParamDemo;
}
复制代码

我々LocalVariableTableが、これは引数であることが判明することができ、その実装は非常に単純な、javacコンパイラは、共通のアクセスメソッドのパラメータには、このキーワードを訪問する時間をコンパイルし、その後、自動的に仮想マシンインスタンスメソッドが呼び出さこのパラメータが渡されます。

それは確かにスーパーの考えるこれになると、スーパーでは、実際にinvokespecial命令によって実現通常のメソッド呼び出し、です。

String.intern()原則

インターン()メソッドを使用している場合、あなたはリテラル定数プールを実行している追加の動的メソッドの役割だと思います。同じ文字列の実行時定数プールは既に(equalsメソッドの決意)存在する場合、オブジェクト・プールは、それが一定のプールに追加されているそれ以外の場合は対応する参照した後に、戻されます。

String s1 = "Hello";
String s2 = new String("Hello").intern();

//true
System.out.println(s1 == s2);
复制代码

出力は、コンパイル「ハロー」定数プールに追加されたリテラル、動作中に、呼び出されるインターン()メソッドは、次にハローアドレスの参照定数プールを返すequalsメソッドに従って等しい二つの文字列と判断された時、真でありますしたがって、この場合にはS1、S2は、実際に同じアドレスを指します。

私たちは、何かをするコードを変更します

String s1 = "Hello";
String s2 = "World";

String s3 = s1 + s2;

String s4 = "Hello" + "World";

System.out.println(s3 == s4);

System.out.println(s3.intern() == s4);

复制代码

結果は偽と真に出力されます。私たちは、さまざまS3およびS4のほとんどは、S3は、同じビューバイトコード変数を追加することによって得られることに注意します

#2 = String     #37     // Hello
#3 = String     #38     // World
#4 = Class      #39     // java/lang/StringBuilder
#5 = Methodref  #4.#36  // java/lang/StringBuilder."<init>":()V
#6 = Methodref  #4.#40  // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
#7 = Methodref  #4.#41  // java/lang/StringBuilder.toString:()Ljava/lang/String;
#8 = String     #42     // HelloWorld

public static void main(java.lang.String[]);
  descriptor: ([Ljava/lang/String;)V
  flags: ACC_PUBLIC, ACC_STATIC
  Code:
    stack=3, locals=5, args_size=1
      0: ldc           #2        // String Hello
      2: astore_1
      3: ldc           #3        // String World
      5: astore_2
      6: new           #4        // class java/lang/StringBuilder
      9: dup
      10: invokespecial #5        // Method java/lang/StringBuilder."<init>":()V
      13: aload_1
      14: invokevirtual #6        // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      17: aload_2
      18: invokevirtual #6        // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      21: invokevirtual #7        // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      24: astore_3
      25: ldc           #8        // String HelloWorld
      27: astore        4
      ...
复制代码

実際に取得するStringBuilder.Append S3()メソッドと呼ばれるバイトコードからわかるように、S4の値は、直接コンパイル時に決定することができ、それは、正確な値であるので、この場合の定数プールのHelloWorldそれに存在していました。

おすすめ

転載: juejin.im/post/5d4cd6b0e51d4561ba48fdaa