字符串文字,实习和反思 [英] String literals, interning and reflection
问题描述
我正在尝试找到这个问题的第三个解决方案。
I'm trying to find a third solution to this question.
我无法理解为什么这不打印 false
。
I can't understand why this doesn't print false
.
public class MyClass {
public MyClass() {
try {
Field f = String.class.getDeclaredField("value");
f.setAccessible(true);
f.set("true", f.get("false"));
} catch (Exception e) {
}
}
public static void main(String[] args) {
MyClass m = new MyClass();
System.out.println(m.equals(m));
}
}
当然,由于字符串实习,true
正在修改的实例与 PrintStream $的
print
方法中使用的实例完全相同c $ c>?
Surely, because of string interning, the "true"
instance being modified is exactly the same one used in the print
method of PrintStream
?
public void print(boolean b) {
write(b ? "true" : "false");
}
我缺少什么?
编辑
@yshavit的一个有趣点是,如果你添加行
An interesting point by @yshavit is that if you add the line
System.out.println(true);
在尝试
之前,输出为
true
false
推荐答案
这可以说是一个HotSpot JVM错误。
This is arguably a HotSpot JVM bug.
问题在于字符串文字实习机制。
-
java.lang.String
在常量池解析期间,字符串文字是懒惰创建的。 - 最初,字符串文字在常量池中由
CONSTANT_String_info
指向CONSTANT_Utf8_info
。 - 每个类都有自己的常量池。也就是说,
MyClass
和PrintStream
拥有自己的一对CONSTANT_String_info
/CONSTANT_Utf8_info
文字'true'的cpool条目。 - 当
时首次访问CONSTANT_String_info
,JVM启动解析过程。字符串实习是此过程的一部分。 - 要查找正在实习的文字的匹配项,JVM会将
CONSTANT_Utf8_info
的内容与StringTable
中的字符串实例的内容。 - ^^^这是问题所在。将来自cpool的原始UTF数据与Java
char []
数组内容进行比较,这些数据内容可由用户通过Reflection进行欺骗。
java.lang.String
instances for the string literals are created lazily during constant pool resolution.- Initially a string literal is represented in the constant pool by
CONSTANT_String_info
structure that points toCONSTANT_Utf8_info
. - Each class has its own constant pool. That is,
MyClass
andPrintStream
have their own pair ofCONSTANT_String_info
/CONSTANT_Utf8_info
cpool entries for the literal 'true'. - When
CONSTANT_String_info
is accessed for the first time, JVM initiates the process of resolution. String interning is the part of this process. - To find a match for a literal being interned, JVM compares the contents of
CONSTANT_Utf8_info
with the contents of string instances in theStringTable
. - ^^^ And here is the problem. Raw UTF data from cpool is compared to Java
char[]
array contents that can be spoofed by a user via Reflection.
那么,你的测试中发生了什么?
-
f.set(true,f.get(false))
在中启动文字'true'的分辨率MyClass
。 - JVM在
StringTable
中发现没有匹配序列'true'的实例,并创建一个新的java.lang.String
,它存储在StringTable
。 -
值
来自StringTable
的字符串将通过Reflection替换。 -
System.out.println(true)
在PrintStream <中启动文字'true'的分辨率/ code> class。
- JVM将UTF序列'true'与
StringTable
中的字符串进行比较,但发现没有匹配,因为该String已经有'false'值。 'true'的另一个字符串已创建并放置在StringTable
中。
f.set("true", f.get("false"))
initiates the resolution of the literal 'true' inMyClass
.- JVM discovers no instances in
StringTable
matching the sequence 'true', and creates a newjava.lang.String
, which is stored inStringTable
. value
of that String fromStringTable
is replaced via Reflection.System.out.println(true)
initiates the resolution of the literal 'true' inPrintStream
class.- JVM compares UTF sequence 'true' with Strings from
StringTable
, but finds no match, since that String already has 'false' value. Another String for 'true' is created and placed inStringTable
.
为什么我认为这是一个错误?
JLS§3.10.5和JVMS§5.1要求包含相同字符序列的字符串文字必须指向到 java.lang.String
的相同实例。
JLS §3.10.5 and JVMS §5.1 require that string literals containing the same sequence of characters must point to the same instance of java.lang.String
.
但是,在下面的代码中解析了两个字符串带有相同字符序列的文字会产生不同的实例。
However, in the following code the resolution of two string literals with the same sequence of characters result in different instances.
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的可能修复方法是在 StringTable
中存储指向原始UTF序列的指针以及 java.lang.String
对象,以便实习过程将不要将cpool数据(用户无法访问)与值
数组进行比较(可通过Reflection访问)。
A possible fix for JVM is to store a pointer to original UTF sequence in StringTable
along with java.lang.String
object, so that interning process will not compare cpool data (inaccessible by user) with value
arrays (accessible via Reflection).
这篇关于字符串文字,实习和反思的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!