一個例子html
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 } }
結果分析:java
c==d是true,是由於d="a"+"b"是兩個常量String的對象相加,返回的String對象就是常量String;安全
b==c是false,是由於b=a+「b」,java 重載了「+」,反編譯java字節碼能夠發現「+」實際上是調用了StringBuilder 因此使用了「+」實際上是生成了一個新的對象;多線程
b.intern() == c 是true, 由於,若是常量池中存在當前字符串,就會直接返回當前字符串;若是常量池中沒有此字符串,會將此字符串放入常量池中後,再返回。app
後續把上面的猜測挨着驗證;異步
先編譯: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反編譯參數):ui
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
@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等參數見:http://www.javashuo.com/article/p-qaprakcz-gp.html
執行上面的單元測試,等了數十秒:
說明在Java8中,字符串常量池在JVM的堆中。參考:https://blog.csdn.net/u014039577/article/details/50377805
http://www.javashuo.com/article/p-uukdvibl-ba.html