JVM 如何重用 interned String 子串?
How does the JVM reuse interned String substrings?
我知道你是否
for (condition) {
String s = "hi there";
}
在所有迭代中只创建一个 String
实例,不像 String s = new String("hi there");
会在每次迭代中创建一个新实例。
但是,阅读 Joshua Bloch 的有效 Java:第 2 章第 5 项(第 20 页)它指出:
Furthermore, it is guaranteed that the object will be
reused by any other code running in the same virtual machine that happens to contain the same string literal [JLS, 3.10.5].
AFAIK 没有说 恰好 是 相同的字符串文字 ,它说 包含 .
阅读 [JLS, 3.10.5] 找不到对此的任何确切参考,我有疑问。
给出这个片段:
String s1 = "hi ";
String s2 = "there";
String s3 = "hi there";
创建了多少个实例?
- 3 个实例(因此,短语并不准确)。
- 2 个实例,
s1
和 s2
(然后 s3
是重新使用 s1
和 s2
引用创建的)
JLS 不保证任何子字符串的重用。这里的 "contain" 只是意味着 class 在某处提到了完全相同的字符串文字。 不是 "substring of" 意义上的用法。
每个 class 文件都包含一个列表,其中包含该 class 中使用的所有字符串文字或其他常量(嵌入在指令流中的小数字常量除外)。如果列表中的第 19 项是字符串文字 "Freddy"
,并且局部变量 Fred
的索引为 6,那么为 Fred="Freddy";
生成的字节码可能是 ldc 19
/ astore 6
.
当加载 class 时,系统将构建一个 table 的所有常量和——对于那些引用类型——由此识别的对象。如果已知不存在字符串文字的实例,系统将向实习 table 添加一个并存储对该字符串的引用。生成机器代码时,ldc 19
将被替换为加载适当引用的指令。
重要的是,当 class 中的任何代码运行时,已经为其中的所有字符串文字创建了对象,因此像 Fred="Freddy";
这样的语句将仅存储一个引用到包含 Freddy
的现有 String
对象,而不是创建新的 String
对象。
如果 s3
重用 s1
和 s2
实例,那么 s3
将不会在物理上表示为连续字符数组,而是复合 [= String
个对象中的 14=] 个。
现在想象一下访问这样一个字符串中的单个字符对性能的影响——基于索引的访问实际上涉及将索引值与第一个字符串的大小进行比较,然后计算偏移量,这将成为第二个字符串的索引,等等
实际上,相反的可能是有意义的:只能为"hi there"
(s3
)分配一个底层字符序列,而s1
和s2
只能存储它们的长度和该字符串中第一个字符的地址。但我认为 jvm 确定 'embeddable' 候选人将是一项复杂而昂贵的工作,而且成本将超过潜在收益。
我知道你是否
for (condition) {
String s = "hi there";
}
在所有迭代中只创建一个 String
实例,不像 String s = new String("hi there");
会在每次迭代中创建一个新实例。
但是,阅读 Joshua Bloch 的有效 Java:第 2 章第 5 项(第 20 页)它指出:
Furthermore, it is guaranteed that the object will be reused by any other code running in the same virtual machine that happens to contain the same string literal [JLS, 3.10.5].
AFAIK 没有说 恰好 是 相同的字符串文字 ,它说 包含 .
阅读 [JLS, 3.10.5] 找不到对此的任何确切参考,我有疑问。
给出这个片段:
String s1 = "hi ";
String s2 = "there";
String s3 = "hi there";
创建了多少个实例?
- 3 个实例(因此,短语并不准确)。
- 2 个实例,
s1
和s2
(然后s3
是重新使用s1
和s2
引用创建的)
JLS 不保证任何子字符串的重用。这里的 "contain" 只是意味着 class 在某处提到了完全相同的字符串文字。 不是 "substring of" 意义上的用法。
每个 class 文件都包含一个列表,其中包含该 class 中使用的所有字符串文字或其他常量(嵌入在指令流中的小数字常量除外)。如果列表中的第 19 项是字符串文字 "Freddy"
,并且局部变量 Fred
的索引为 6,那么为 Fred="Freddy";
生成的字节码可能是 ldc 19
/ astore 6
.
当加载 class 时,系统将构建一个 table 的所有常量和——对于那些引用类型——由此识别的对象。如果已知不存在字符串文字的实例,系统将向实习 table 添加一个并存储对该字符串的引用。生成机器代码时,ldc 19
将被替换为加载适当引用的指令。
重要的是,当 class 中的任何代码运行时,已经为其中的所有字符串文字创建了对象,因此像 Fred="Freddy";
这样的语句将仅存储一个引用到包含 Freddy
的现有 String
对象,而不是创建新的 String
对象。
如果 s3
重用 s1
和 s2
实例,那么 s3
将不会在物理上表示为连续字符数组,而是复合 [= String
个对象中的 14=] 个。
现在想象一下访问这样一个字符串中的单个字符对性能的影响——基于索引的访问实际上涉及将索引值与第一个字符串的大小进行比较,然后计算偏移量,这将成为第二个字符串的索引,等等
实际上,相反的可能是有意义的:只能为"hi there"
(s3
)分配一个底层字符序列,而s1
和s2
只能存储它们的长度和该字符串中第一个字符的地址。但我认为 jvm 确定 'embeddable' 候选人将是一项复杂而昂贵的工作,而且成本将超过潜在收益。