字符串文字,实习和反思 [英] String literals, interning and reflection

查看:111
本文介绍了字符串文字,实习和反思的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试找到这个问题的第三个解决方案。

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 to CONSTANT_Utf8_info.
  • Each class has its own constant pool. That is, MyClass and PrintStream have their own pair of CONSTANT_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 the StringTable.
  • ^^^ 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.

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


  1. f.set(true,f.get(false))中启动文字'true'的分辨率MyClass

  2. JVM在 StringTable 中发现没有匹配序列'true'的实例,并创建一个新的 java.lang.String ,它存储在 StringTable

  3. 来自 StringTable 的字符串将通过Reflection替换。

  4. System.out.println(true) PrintStream <中启动文字'true'的分辨率/ code> class。

  5. JVM将UTF序列'true' StringTable 中的字符串进行比较,但发现没有匹配,因为该String已经有'false'值。 'true'的另一个字符串已创建并放置在 StringTable 中。

  1. f.set("true", f.get("false")) initiates the resolution of the literal 'true' in MyClass.
  2. JVM discovers no instances in StringTable matching the sequence 'true', and creates a new java.lang.String, which is stored in StringTable.
  3. value of that String from StringTable is replaced via Reflection.
  4. System.out.println(true) initiates the resolution of the literal 'true' in PrintStream class.
  5. 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 in StringTable.

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

JLS§3.10.5JVMS§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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆