作者:手机用户2502940275 | 来源:互联网 | 2023-07-06 17:05
请考虑以下类:
class Eq {
public static void main(String[] args) {
System.out.println("" == ".".substring(1));
}
}
该示例应该显示内存中可能存在空字符串的多个副本。我仍然有一个旧的 OpenJDK 11,程序false
按预期输出。在 OpenJDK 15 下,程序输出true
. 为类文件生成的字节码看起来相似(即使它们的寄存器值不同):
爪哇11:
public static void main(java.lang.String[]);
Code:
0: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #13 // String
5: ldc #15 // String .
7: iconst_1
8: invokevirtual #17 // Method java/lang/String.substring:(I)Ljava/lang/String;
11: if_acmpne 18
14: iconst_1
15: goto 19
18: iconst_0
19: invokevirtual #23 // Method java/io/PrintStream.println:(Z)V
22: return
爪哇 15:
public static void main(java.lang.String[]);
Code:
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #3 // String
5: ldc #4 // String .
7: iconst_1
8: invokevirtual #5 // Method java/lang/String.substring:(I)Ljava/lang/String;
11: if_acmpne 18
14: iconst_1
15: goto 19
18: iconst_0
19: invokevirtual #6 // Method java/io/PrintStream.println:(Z)V
22: return
我试图通过阅读“.”来排除静态编译器优化。来自标准输入,但这不会改变结果。我试图禁用 JIT via-Djava.compiler=NONE
并尝试通过调整字符串表大小-XX:StringTableSize=100000
。我现在有以下问题:
- 有人可以重现这个问题吗(即我做对了吗?如果有帮助,我可以提供类文件)
- 我如何找出不同行为的确切原因?
- (在您看来)不同行为的根源是什么?
我认为只是策略来解决如何找到不回答问题的行为的原因也可能很有趣。
回答
这在JDK 15 发行说明 中提到。
根据 JDK-8240094 的要求对其进行了更改:
JDK-8240094:优化空子串处理
String.substring
""
在某些情况下返回,但可以改进以在子字符串长度为零的所有情况下都这样做。
有关的:
JDK-8240225:优化空子串处理
优化String.substring
和相关操作,如stripLeading
,stripTrailing
避免重复创建新的空字符串。
子任务:
JDK-8251556:发行说明:优化的空子字符串处理
的实施String.substring
和相关方法stripLeading
和stripTrailing
在此版本中已更改为避免冗余创建一个新的空字符串。这可能会影响依赖于未指定行为和空子字符串标识的代码。
@fluffy `""` is the default empty string constant, no need to create a constant field for it, since string literals are always interned for you.
@fluffy The string constant pool is synthesized by the JVM at runtime and merges all constant strings.
@fluffy it doesn’t matter whether you use `""` or `SomeClass.CONSTANT`. The important point is that the `substring` method needs to perform an explicit test whether the result string has length zero and use one of those constants instead of calling `new String(…)` in that case. Without that test, declaring a constant for empty strings doesn’t help.
@fluffy having public constructors in the string class is a historical design mistake on its own (including `String()`, encouraging developers to create redundant strings even when known to be empty). However, for the string class itself, that makes little difference as even `private` constructors are accessible. Consistently using factory methods that may perform this check would help, but there is a different point of view. *Is this optimization really an improvement?* We now have an additional conditional in every `substring` operation, serving the rare corner case of an empty result…
Just curious: .NET has `String.Empty` since the earliest version, Apache Commons suggest a similar final reference `StringUtils.EMPTY`. Those JDK items and issues would probably never exist if an empty string constant would exist from the very beginning. Was there any idea of introducing the "default" empty string constant to Java?
@fluffy I had such scenarios in my application, where I wished I could constrain the use of `private` members even further, ala “this member is only for method abc and xyz”. However, in case of OpenJDK’s String implementation they were not even trying to use/enforce factory methods, internally, the constructors are used all over the place.