为什么在调试字符串连接时会弹出StringBuilders?

Miq*_*uel 4 java debugging bytecode

我知道String是不可变的,以及何时使用StringBuilder或StringBuffer.我还读到这两个片段的字节码最终会相同:

//Snippet 1
String variable = "text";
this.class.getResourceAsStream("string"+variable);

//Snippet 2
StringBuilder sb = new StringBuilder("string");
sb.append("text");
this.class.getResourceAsStream(sb.toString());
Run Code Online (Sandbox Code Playgroud)

但我显然有些不对劲.在eclipse中通过Snippet 1进行调试时,实际上我被带到StringBuilder构造函数和append方法.我想我错过了有关如何解释字节码以及调试器如何引用源代码中的行的详细信息; 如果有人能解释一下,我真的很感激.另外,也许你可以指出什么是JVM特定的和什么不是(我是运行Oracle的v6),谢谢!

Ste*_*n C 8

为什么在调试字符串连接时会弹出StringBuilders?

因为字符串连接(通过'+'运算符)通常编译为使用StringBufferStringBuilder进行连接的代码.JLS明确允许此行为.

"实现可以选择在一个步骤中执行转换和连接,以避免创建然后丢弃中间String对象.为了提高重复字符串连接的性能,Java编译器可以使用StringBuffer类或类似技术来减少数量通过评估表达式创建的中间String对象." JLS 15.18.1.

(如果您的代码使用的是StringBuffer而不是a StringBuilder,则可能是因为它是使用非常旧的Java编译器编译的,或者是因为您指定了一个非常旧的目标JVM.StringBuilder该类是Java的相对补充.旧版本的JLS曾经提到StringBuffer而不是StringBuilderIIRC.)


此外,也许你可以指出什么是JVM特定的,什么不是.

生成的字节码"string" + variable"取决于Java编译器如何处理串联.(实际上,所有生成的字节码都是Java编译器在某种程度上依赖的.JLS和JVM规范没有规定必须生成什么字节码.规范更多地是关于程序应该如何表现,以及单个字节码的作用.)


@supercat评论:

我想知道为什么字符串连接不会使用例如一个String构造函数重载,它接受两个String对象,分配一个适当组合大小的缓冲区,并加入它们?或者,当连接更多字符串时,带有String []的重载?创建包含对要连接的字符串的引用的String []应该不比创建StringBuilder更昂贵,并且能够一次性创建完美大小的后备存储应该是一个简单的性能获胜.

也许......但我想说可能不是.这是一个涉及复杂权衡的复杂领域.所选择的字符串连接实现策略必须适用于各种不同的用例.

我的理解是,在查看了许多方法之后选择了原始策略,并进行了一些大规模的静态代码分析和基准测试,以试图找出哪种方法最好.我他们考虑了你提出的所有替代方案.(毕竟,他们是聪明人......)

话虽如此,您可以使用Java 6,7和8的完整源代码库.这意味着您可以下载它,并尝试自己的一些实验,看看您的理论是否正确.如果他们......并且你可以收集他们的确凿证据......然后向OpenJDK团队提交补丁.