2016-07-15 37 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