JVM全面分析之String

2022-04-07 02:37:33 字數 4326 閱讀 7037

目錄string 的string pool是乙個固定大小的hashtable,預設值大小長度是1009.如果放進string pool的string非常多,就會造成hash衝突嚴重,從而導致鍊錶會很長,而鍊錶長了後直接會造成的影響就是當呼叫string.intern時效能會大幅下降。

使用-xx:stringtablesize可設定stringtable長度

jdk6中stringtable是固定的,就是1009長度,所以如果長兩次中的字串過多就會導致效率下降很快。stringtablesize設定沒有要求

在jdk7中,stringtable的長度預設值是60013,stringtablesize設定沒有要求。

在jdk8開始,設定stringtable的長度的話,1009是可設定的最小值。

常量與常量的拼接結果在常量池中,原理是編譯期優化

常量池中不會存在相同內容的常量。

只要其中有乙個是變數,結果就在堆中。變數拼接的理由是stringbuilder。

如果拼接的結果呼叫intern()方法,則主動將常量池中還沒有的字串物件放入池中,並返回此物件位址。

public class stringtest1 

}

上述**的class檔案內容為:

從位元組碼角度看:

}通過以下**我們解析字串拼接符"+"的底層實現

public void test3()
底層位元組碼反編譯後的結果為:

0 ldc #12 2 astore_1

3 ldc #135 astore_2

6 ldc #14 8 astore_3

9 new #5 12 dup

13 invokespecial #6 >

16 aload_1

29 getstatic #9 32 aload_3

33 aload 4

35 if_acmpne 42 (+7)

38 iconst_1

39 goto 43 (+4)

42 iconst_0

43 invokevirtual #10 46 return

從反編譯結果的第9行後面看:s1 + s2 的執行細節為:(變數s是我臨時定義的)

stringbuilder s = new stringbuilder();

s.tostring() --> 約等於 new string("ab")

補充:在jdk5.0之後使用的是stringbuilder,在jdk5.0之前使用的是stringbuffer

那設計到字串變數的拼接都是這樣的過程嗎?不是的,請繼續往下看

public void test4()
從class檔案看:

位元組碼角度看:

0 ldc #12 2 astore_1

3 ldc #135 astore_2

6 ldc #14 8 astore_3

9 ldc #14 11 astore 4

13 getstatic #9 16 aload_3

17 aload 4

19 if_acmpne 26 (+7)

22 iconst_1

23 goto 27 (+4)

26 iconst_0

27 invokevirtual #10 30 return

發現此時沒有stringbuilder出現了。而都是用的字串常量池。

下面是乙個練習

public void test5()
下面再進行一次對比:

public static void test6()

long l2 = system.currenttimemillis();

system.out.println(l2-l1); // 8635

}

public static void test7() 

long l2 = system.currenttimemillis();

system.out.println(l2 - l1); // 35

}

兩個方法為何差距如此大呢?原因為:

test6方法中每次迴圈都會建立乙個stringbuilder、string類(stringbuilder呼叫tostring方法會建立乙個string類)。

總結

首先看看string原始碼中intern()方法:

public native string intern();

發現其實native型別

如果不是用雙引號宣告的string物件,可以使用string提供的intern方法:intern方法會從字串常量池中查詢當前字串是否存在,若不存在就會將當前字串放入常量池中。

題目一:new string("ab")會建立幾個物件?

public class stringtest3 

}

其位元組碼內容如下:

0 new #2 3 dup

4 ldc #3 6 invokespecial #4 >

9 astore_1

10 return

可見其創造了兩個物件:字串常量池中乙個:存放」ab「;堆空間中乙個:string。

題目而:new string("a") + new string("b")呢?

public class stringtest3 

}

位元組碼內容如下:

0 new #2 3 dup

4 invokespecial #3 >

7 new #4 10 dup

11 ldc #5 13 invokespecial #6 >

23 ldc #825 invokespecial #6 >

35 return

解析:

物件1:new stringbuilder()

物件2 :new string("a")

物件3:常量池中的"a"

物件4:new string("b")

物件5:常量池中的」b「

深入剖析:stringbuilder的tostring():

6. 物件6:new string("ab");強調一下:tostring()的呼叫,在字串常量池中,沒有生成」ab「。不信你看位元組碼

題目三:

上題變形

public class stringtest6 

}

總結string的intern()的使用

intern()使用:練習1

變形

intern()使用:練習2

JVM之top jstack分析cpu過高原因

1 用ps ef grep tomcat v3 查出tomcat執行的程序id 2 用top hp pid 查詢程序下所有執行緒的運 況 shift p 按cpu排序,shift m 按記憶體排序 3 找到cpu最高的pid,用printf x n pid 轉換為16進製制 printf x n 3...

JVM物件逃逸分析 JVM記憶體

jvm的執行模式有三種 解釋模式 interpreted mode 只使用直譯器 xint 強制jvm使用解釋模式 執行一行jvm位元組碼就編譯一行為機器碼 編譯模式 compiled mode 只使用編譯器 xcomp jvm使用編譯模式 先將所有jvm位元組碼一次編譯為機器碼,然 後一次性執行所...

原始碼分析之String

先看屬性 底層是char陣列,一目了然 可以看到,value是儲存string的內容的,即當使用string str abc 的時候,本質上,abc 是儲存在乙個char型別的陣列中的。string底層的儲存結構是乙個字元型別的陣列,同樣也是被final修飾,因此一旦這個字元陣列被建立後,value...