1. 程式人生 > >Java中的String,javap&cfr.jar反編譯,javap反編譯後二進位制指令程式碼詳解,Java8常量池的位置

Java中的String,javap&cfr.jar反編譯,javap反編譯後二進位制指令程式碼詳解,Java8常量池的位置

一個例子

 

public class TestString{
    public static void main(String[] args){
            String a = "a";
            String b = a+"b";
            String c = "ab";
            String d = "a" + "b";
            System.out.println(c == d);           //true!!!!
            System.out.println(c == d.intern());  //
true System.out.println(b == c); //false System.out.println(b.intern() == c); //true!!! System.out.println(b == c.intern()); //false System.out.println(b == d); //false } }

 

 

 

結果分析:

c==d是true,是因為d="a"+"b"是兩個常量String的物件相加,返回的String物件就是常量String;

b==c是false,是因為b=a+“b”,java 過載了“+”,反編譯java位元組碼可以發現“+”其實是呼叫了StringBuilder 所以使用了+”其實是生成了一個新的物件;

b.intern() == c 是true, 因為,String物件呼叫intern方法後,會從常量池中找出與b通過equals()方法true的常量字串物件。

後續把上面的猜想挨著驗證;

 

先編譯:javac TestString.java;

在使用cfr-0.137.jar將java反編譯:java -jar cfr-0.137.jar TestString.class --stringbuilder false ;
反編譯結果如下:

 

/*
 * Decompiled with CFR 0.137.
 */
import java.io.PrintStream;

public class TestString {
    public static void main(String[] arrstring) {
        String string = "a";
        String string2 = new StringBuilder().append(string).append("b").toString();
        String string3 = "ab";
        String string4 = "ab";
        System.out.println(string3 == string4);
        System.out.println(string3 == string4.intern());
        System.out.println(string2 == string3);
        System.out.println(string2.intern() == string3);
        System.out.println(string2 == string3.intern());
        System.out.println(string2 == string4);
    }
}

 

可以看到,兩個字串相加,如果其中一個不是常量字串(即不是通過變數名引用的字串如"LuoTiany"),那麼就會編譯器編譯後,JVM執行就會通過new StringBuilder物件操作,如果在一個迴圈中,就會產生多個StringBuilder物件,所以字串相加,儘量使用StringBuilder物件的append(非同步)或StringBuffer物件的append(同步);

 

使用javap -c TestString.class反編譯結果如下(javap反編譯引數):

Compiled from "TestString.java"
public class TestString {
  public TestString();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: ldc           #2                  // String a
       2: astore_1
       3: new           #3                  // class java/lang/StringBuilder
       6: dup
       7: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V
      10: aload_1
      11: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      14: ldc           #6                  // String b
      16: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      19: invokevirtual #7                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      22: astore_2
      23: ldc           #8                  // String ab
      25: astore_3
      26: ldc           #8                  // String ab
      28: astore        4
      30: getstatic     #9                  // Field java/lang/System.out:Ljava/io/PrintStream;
      33: aload_3
      34: aload         4
      36: if_acmpne     43
      39: iconst_1
      40: goto          44
      43: iconst_0
      44: invokevirtual #10                 // Method java/io/PrintStream.println:(Z)V
      47: getstatic     #9                  // Field java/lang/System.out:Ljava/io/PrintStream;
      50: aload_3
      51: aload         4
      53: invokevirtual #11                 // Method java/lang/String.intern:()Ljava/lang/String;
      56: if_acmpne     63
      59: iconst_1
      60: goto          64
      63: iconst_0
      64: invokevirtual #10                 // Method java/io/PrintStream.println:(Z)V
      67: getstatic     #9                  // Field java/lang/System.out:Ljava/io/PrintStream;
      70: aload_2
      71: aload_3
      72: if_acmpne     79
      75: iconst_1
      76: goto          80
      79: iconst_0
      80: invokevirtual #10                 // Method java/io/PrintStream.println:(Z)V
      83: getstatic     #9                  // Field java/lang/System.out:Ljava/io/PrintStream;
      86: aload_2
      87: invokevirtual #11                 // Method java/lang/String.intern:()Ljava/lang/String;
      90: aload_3
      91: if_acmpne     98
      94: iconst_1
      95: goto          99
      98: iconst_0
      99: invokevirtual #10                 // Method java/io/PrintStream.println:(Z)V
     102: getstatic     #9                  // Field java/lang/System.out:Ljava/io/PrintStream;
     105: aload_2
     106: aload_3
     107: invokevirtual #11                 // Method java/lang/String.intern:()Ljava/lang/String;
     110: if_acmpne     117
     113: iconst_1
     114: goto          118
     117: iconst_0
     118: invokevirtual #10                 // Method java/io/PrintStream.println:(Z)V
     121: getstatic     #9                  // Field java/lang/System.out:Ljava/io/PrintStream;
     124: aload_2
     125: aload         4
     127: if_acmpne     134
     130: iconst_1
     131: goto          135
     134: iconst_0
     135: invokevirtual #10                 // Method java/io/PrintStream.println:(Z)V
     138: return
}

 上面如果看不懂,可以在這裡找,javap編譯後二進位制指令程式碼詳解:http://www.blogjava.net/DLevin/archive/2011/09/13/358497.html#Post

 

 

JVM記憶體中的常量池的位置

@Test
    public void constantPool(){
        List<String> list = new ArrayList<String>();
        int i=0;
        while(true){
            list.add(String.valueOf(i++).intern());
        }
    }

在執行的時候先設定VM啟動引數:

在原來的VM options :-ea 後面追加-Xmx128m -Xms64m -Xmn32m -Xss16m -XX:-UseGCOverheadLimit;

這裡的-XX:-UseGCOverheadLimit關閉GC佔用時間過長時報的異常,-Xmx等引數見:https://www.cnblogs.com/theRhyme/p/9120705.html

執行上面的單元測試,等了數十秒:

說明在Java8中,字串常量池JVM的堆中。參考:https://blog.csdn.net/u014039577/article/details/50377805