java - 字符串文字、实习和反射

标签 java string reflection jvm

我正试图找到 this question 的第三种解决方案.

我不明白为什么不打印 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" 实例与 PrintStreamprint 方法中使用的实例完全相同?

public void print(boolean b) {
    write(b ? "true" : "false");
}

我错过了什么?

编辑

@yshavit 的一个有趣观点是,如果你添加这条线

System.out.println(true);

try之前,输出是

true
false

最佳答案

这可以说是 HotSpot JVM 错误。

问题出在字符串文字驻留机制

  • java.lang.String字符串文字的实例是在常量池解析期间延迟创建的。
  • 最初,字符串文字在常量池中表示为 CONSTANT_String_info 。指向 CONSTANT_Utf8_info 的结构.
  • 每个类都有自己的常量池。即 MyClassPrintStream有自己的一双CONSTANT_String_info/CONSTANT_Utf8_info文字 'true' 的 cpool 条目。
  • 何时CONSTANT_String_info第一次访问,JVM发起解析过程。字符串实习是这个过程的一部分。
  • 为了找到与驻留文字的匹配项,JVM 比较 CONSTANT_Utf8_info 的内容包含 StringTable 中字符串实例的内容.
  • ^^^ 这就是问题所在。来自 cpool 的原始 UTF 数据与 Java char[] 进行比较用户可以通过反射欺骗的数组内容。

那么,您的测试中发生了什么?

  1. f.set("true", f.get("false"))MyClass 中启动文字 'true' 的解析.
  2. JVM 在 StringTable 中没有发现任何实例匹配序列 'true',并创建一个新的 java.lang.String , 它存储在 StringTable 中.
  3. value来自 StringTable 的那个字符串通过反射替换。
  4. System.out.println(true)PrintStream 中启动文字 'true' 的解析类。
  5. JVM 将 UTF 序列 'true' 与来自 StringTable 的字符串进行比较,但找不到匹配项,因为该字符串已经具有 'false' 值。 'true' 的另一个字符串被创建并放置在 StringTable 中.

为什么我认为这是一个错误?

JLS §3.10.5JVMS §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 的一个可能修复方法是在 StringTable 中存储指向原始 UTF 序列的指针。连同 java.lang.String对象,以便实习进程不会将 cpool 数据(用户无法访问)与 value 进行比较数组(可通过反射访问)。

关于java - 字符串文字、实习和反射,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36366692/

相关文章:

java - 反射安全

java - 如果加载时间超过特定时间,Android Timeout webview

c#追加字典替换的结果

arrays - 将表示为字符串的字节数组转换为字节数组

将字符串转换为特殊字符串

c# - 如何从 dapper 返回的复杂对象中修剪所有字符串

java - 如果仅从垃圾中引用一个对象,它就是垃圾吗?

Java ProcessBuilder 开始在 Linux 中顺序执行多个命令

java - Eclipse:同时替换多个不同的正则表达式

c# - 通过 Expression.Call 调用 Expression.GreaterThanOrEqual 时出现 ArgumentException