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 方法中使用的实例完全相同?
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);
在try之前,输出是
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用于文字 的 cpool 条目'真实'. - 当
CONSTANT_String_info第一次被访问时,JVM启动解析过程.字符串实习是这个过程的一部分. - 为了找到匹配的文本,JVM 将
CONSTANT_Utf8_info的内容与StringTable中的字符串实例的内容进行比较. - ^^^ 这就是问题所在.来自 cpool 的原始 UTF 数据与 Java
char[]数组内容进行比较,用户可以通过反射进行欺骗.
java.lang.Stringinstances for the string literals are created lazily during constant pool resolution.- Initially a string literal is represented in the constant pool by
CONSTANT_String_infostructure that points toCONSTANT_Utf8_info. - Each class has its own constant pool. That is,
MyClassandPrintStreamhave their own pair ofCONSTANT_String_info/CONSTANT_Utf8_infocpool entries for the literal 'true'. - When
CONSTANT_String_infois 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_infowith 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"))启动MyClass中文字'true'的解析代码>.- JVM在
StringTable中没有发现匹配序列'true'的实例,并创建一个新的java.lang.String,将其存储在StringTable中. value被反射替换.System.out.println(true)启动PrintStream类中文字'true' 的解析.- JVM 将 UTF 序列 'true' 与来自
StringTable的字符串进行比较,但没有找到匹配项,因为该字符串已经具有 'false' 值.'true' 的另一个字符串被创建并放置在StringTable中.
StringTable 中该字符串的 f.set("true", f.get("false"))initiates the resolution of the literal 'true' inMyClass.- JVM discovers no instances in
StringTablematching the sequence 'true', and creates a newjava.lang.String, which is stored inStringTable. valueof that String fromStringTableis replaced via Reflection.System.out.println(true)initiates the resolution of the literal 'true' inPrintStreamclass.- 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 的一个可能修复方法是将指向原始 UTF 序列的指针与 java.lang.String 对象一起存储在 StringTable 中,这样实习进程就不会比较 cpool带有 value 数组(可通过反射访问)的数据(用户无法访问).
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).
这篇关于字符串文字、实习和反射的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!
本文标题为:字符串文字、实习和反射
- 将log4j 1.2配置转换为log4j 2配置 2022-01-01
- Safepoint+stats 日志,输出 JDK12 中没有 vmop 操作 2022-01-01
- C++ 和 Java 进程之间的共享内存 2022-01-01
- Eclipse 插件更新错误日志在哪里? 2022-01-01
- Java包名称中单词分隔符的约定是什么? 2022-01-01
- Spring Boot连接到使用仲裁器运行的MongoDB副本集 2022-01-01
- 从 finally 块返回时 Java 的奇怪行为 2022-01-01
- value & 是什么意思?0xff 在 Java 中做什么? 2022-01-01
- Jersey REST 客户端:发布多部分数据 2022-01-01
- 如何使用WebFilter实现授权头检查 2022-01-01
