2016-07-15 66 views
19

我知道如果你讓JVM重用如何實現字符串子字符串?

for (condition) { 
    String s = "hi there"; 
} 

只是一個String實例在所有的迭代創建的,不像String s = new String("hi there");,將創建在每個迭代一個新的實例。

但是,從約書亞布洛赫閱讀有效的Java:第2章第5項(第20頁),它規定:

此外,還保證了對象將 在同一運行的任何其他代碼重用發生的虛擬機包含相同的字符串文字[JLS, 3.10.5]

AFAIK不說恰好相同的字符串字面,它說:包含

閱讀[JLS, 3.10.5]找不到任何確切的參考,我有疑問。

給予這個片段:

String s1 = "hi "; 
String s2 = "there"; 
String s3 = "hi there"; 

創建了多少個實例?

  • 3個實例(因此,短語不是很確切)。
  • 2的情況下,s1s2(然後s3創建重用s1s2引用)
+1

他大概的意思是「虛擬機包含..」,而不是字符串包含另一個字符串 –

+1

我不確定,所以一個評論,而不是一個答案。但我認爲這個「包含」是部分錯誤的,你的例子確實產生了三個實例。 – glglgl

+0

@glglgl實際上是我的邏輯*所說的內容,但可以使用JVM來創建's3'作爲對's1' +'s2'的引用? –

回答

17

的JLS不保證子串進行任何改革重用。這裏的「包含」僅僅意味着類別在某處提到完全相同的字符串文字。在「意義的子串」中使用的是而不是

+2

具體來說_「任何其他代碼[..]碰巧包含**相同的字符串**」_(重點是我的) –

+1

當您說*不保證重新使用子字符串*意味着它有時可能發生? –

+3

@JordiCastilla:我不認爲任何當前的VM重用了子字符串,但它是可能的(例如,當兩個字符串是彼此的子字符串時,以前的OpenJDK迭代有時會共享底層的char [])。請注意,您仍會*觀察單獨的字符串實例,並且沒有公共API來檢測是否發生了這種情況(即,如果沒有某種反射技巧,您將無法辨別)。 –

3

每個類文件都包含該類中使用的所有字符串文字或其他常量(嵌入在指令流中的小數字常量除外)的列表。如果列表中的項目19是字符串文字"Freddy",並且本地變量Fred的索引爲6,則爲Fred="Freddy";生成的字節碼可能爲ldc 19/astore 6

當一個類被加載時,系統將建立一個包含所有常量和 - 對於那些引用類型的表 - 由此識別的對象。如果已知不存在字符串文字的實例,則系統將向實習表格添加一個實體並存儲對該實例的引用。當生成機器碼時,ldc 19將被替換爲加載相應參考的指令。

最重要的是,到時候任何一類的運行,已經爲所有的字符串文字在其中創建的對象的代碼,所以像Fred="Freddy";聲明將僅僅是一個參考存儲包含Freddy已經存在的String對象而不是創建新的String對象。

2

如果s3重用s1s2實例,則s3不會在物理上表示爲一個連續的字符數組,但寧願String的S對象的複合String

現在想象一下,在訪問這樣的字符串中的單個字符時的性能影響 - 基於索引的訪問實際上涉及將索引值與第一個字符串的大小進行比較,然後計算將成爲第二個字符串的索引的偏移量等。

事實上,相反的可能意義:只有一個潛在的字符序列可以分配給"hi there"s3),以及s1s2可能只是存儲它們的長度和字符串中的第一個字符的地址。但是我認爲,確定'可嵌入'候選人將是一項複雜而昂貴的工作,並且成本將超過潛在收益。

+1

那麼,在Java 7之前,'substring'方法以一種方式實現,它返回一個由原始字符串的字符數組支持的字符串,但即使這樣也會因爲它造成更多的傷害而不是好的(大文本可能會例如,通過持有對某個微小子字符串的引用來保持活力) – Hulk

+1

@Hulk:它已經[Java7update6中的更改](http://bugs.java.com/bugdatabase/view_bug.do?bug_id=4513622)。這不僅是一個gc問題,它需要每個字符串攜帶一個「offset」和「length」字段,僅用於單個操作「substring」。此外,最近的JVM的字符串重複數據刪除功能受益於簡化的對象佈局,因爲'value'字段上的單個'cas'就足夠了。 – Holger