可以说这是HotSpot JVM错误。
问题在于字符串字面量的内部机制 。
java.lang.String
在常量池解析期间,将懒惰地创建字符串文字的实例。- 最初,字符串常量在常量池中由
CONSTANT_String_info
指向的结构表示CONSTANT_Utf8_info
。 - 每个类都有自己的常量池。也就是说,
MyClass
而且PrintStream
有自己的一双CONSTANT_String_info
/CONSTANT_Utf8_info
CPOOL条目文字 “真” 。 - 当
CONSTANT_String_info
首次被访问时,JVM启动解决进程。字符串实习是此过程的一部分。 - 为了找到被禁派文字的匹配项,JVM将的内容
CONSTANT_Utf8_info
与中的字符串实例的内容进行比较StringTable
。 - ^^^这就是问题所在。将来自cpool的原始UTF数据与Java
char[]
数组内容进行比较,该内容可以由用户通过Reflection欺骗。
那么,您的测试中发生了什么?
f.set("true", f.get("false"))启动字面的决议 “真” 在MyClass
。- JVM在
StringTable
匹配序列 “ true”时 未发现任何实例,并创建了一个新实例,并将java.lang.String
其存储在中StringTable
。 value
该字符串来自的字符串StringTable
通过反射替换。System.out.println(true)
在类中启动字面量 “ true” 的解析PrintStream
。- JVM将UTF序列 “ true” 与来自的字符串进行比较
StringTable
,但未找到匹配项,因为该字符串已具有 “ false” 值。创建另一个 ‘true’ 字符串并将其放置在中StringTable
。
为什么我认为这是一个错误?
JLS§3.10.5和JVMS§5.1需要包含相同的字符序列串文字必须指向的同一个实例
java.lang.String。
但是,在下面的代码中,具有 相同 字符序列的两个字符串文字的解析导致 不同的 实例。
public class Test { static class Inner { static String trueLiteral = "true"; } public static void main(String[] args) throws Exception { Field f = String.class.getDeclaredField("value"); f.setAccessible(true); f.set("true", f.get("false")); if ("true" == Inner.trueLiteral) { System.out.println("OK"); } else { System.out.println("BUG!"); } }}JVM的一个可能解决方法是将指向原始UTF序列的指针
StringTable与
java.lang.String对象一起存储,以便内部处理过程不会将cpool数据(用户无法访问)与
value数组(可通过Reflection访问)进行比较。



