String s1 == String s2 (true) 但 FieldOffset 不同

Posted

技术标签:

【中文标题】String s1 == String s2 (true) 但 FieldOffset 不同【英文标题】:String s1 == String s2 (true) but FieldOffset is different 【发布时间】:2013-05-27 10:24:10 【问题描述】:

在学习 java 时,我了解到比较 2 个字符串的正确方法是使用 equals 而不是“==”。这一行

静态字符串 s1 = "a"; 静态字符串 s2 = "a"; System.out.println(s1 == s2);

将输出 true,因为 jvm 似乎已经优化了此代码,以便它们实际上指向相同的地址。我试图用我在这里找到的一篇很棒的帖子来证明这一点

http://javapapers.com/core-java/address-of-a-java-object/

但地址似乎不一样。我错过了什么?

导入 sun.misc.Unsafe; 导入 java.lang.reflect.Field; 公共类 SomeClass 静态字符串 s1 = "a"; 静态字符串 s2 = "a"; 公共静态 void main (String args[]) 抛出异常 System.out.println(s1 == s2); //真的 不安全不安全 = getUnsafeInstance(); 字段 s1Field = SomeClass.class.getDeclaredField("s1"); System.out.println(unsafe.staticFieldOffset(s1Field)); //600 字段 s2Field = SomeClass.class.getDeclaredField("s2"); System.out.println(unsafe.staticFieldOffset(s2Field)); //604 private static Unsafe getUnsafeInstance() 抛出 SecurityException, NoSuchFieldException,IllegalArgumentException,IllegalAccessException 字段 theUnsafeInstance = Unsafe.class.getDeclaredField("theUnsafe"); theUnsafeInstance.setAccessible(true); return (Unsafe) theUnsafeInstance.get(Unsafe.class);

【问题讨论】:

见***.com/questions/513832/… 阅读问题。他说他知道如何比较字符串。这是内存管理和 JVM 的问题,而不是字符串比较。 是的,请阅读问题 【参考方案1】:

你没有错过任何东西。 Unsafe 库正在报告实际发生的情况。

字节码:

static ;
  Code:
   0:   ldc #11; //String a
   2:   putstatic   #13; //Field s1:Ljava/lang/String;
   5:   ldc #11; //String a
   7:   putstatic   #15; //Field s2:Ljava/lang/String;
   10:  return

请注意,两个字符串都放在内存中的不同位置,13 和 15。

变量在内存中的存储位置(需要单独的地址)与是否将新对象放入堆中是有区别的。在这种情况下,它为两个变量分配了两个单独的地址,但它不需要创建一个新的字符串对象,因为它可以识别相同的字符串字面量。所以此时两个变量都引用了同一个字符串。

如果你想得到地址,你可以使用在这个问题中找到的答案,How can I get the memory location of a object in java?。确保您在使用前阅读了注意事项,但我做了一个快速测试,它似乎有效。

【讨论】:

但是如果他们在不同的位置,为什么==返回true? @aardvarkk 因为 staticFieldOffset 为您提供了s1s2SomeClass 中的位置。这些是不同的引用,它们甚至有不同的名称,s1s2。然而,这两个引用都指向同一个字符串对象,这就是== 将评估的事实。 @greedybuddha 您写道“但是这两个引用都指向同一个字符串对象”。我知道这是准确的,但是有没有办法通过输出一些地址来证明它(不查看字节码)。 查看我的更新答案,我将答案与代码链接以获取地址【参考方案2】:

在上面的代码中,您不是比较字符串的地址,而是比较它们的“存储分配中给定字段的位置”,即持有对(相同)字符串的引用的变量的位置。

【讨论】:

【参考方案3】:

我认为您对 staticFieldOffset 返回的内容感到困惑。它将 pointer 的偏移量返回到 String 实例,而不是 String 本身的地址。因为有两个字段,所以它们有不同的偏移量:即两个指针,它们恰好具有相同的值。

仔细阅读Unsafe javadoc 可以看出:

报告给定字段在其存储分配中的位置 班级。不要期望对此执行任何类型的算术运算 抵消;它只是一个传递给不安全堆内存的 cookie 访问器。

换句话说,如果您知道实际的 Class 实例在内存中的位置,那么您可以将此方法返回的偏移量添加到该基地址,结果将是您可以在内存中找到的位置指向String 的指针的值。

【讨论】:

@AndrewW 至少在你玩 Unsafe 之前! @AndrewW 了解 C 确实非常重要,因为大多数当前的编程语言都从 C 派生了许多语义和机制。但那是 C,而不是 C++。像那样混合它们不是很准确。相反,C++“教”了太多 C++ 特有的东西。 @Theodoros 我不是指语义。 (尽管我同意它对此很有用。)我的意思是学习指针是如何工作的,而 C 和 C++ 都适用于此。 @AndrewW 哦,是的,为了理解偏移是如何工作的,我绝对同意。无论语言如何,每个人都应该了解指针运算及其应用(即使它是“隐式”完成的)。【参考方案4】:

Java 代码中声明的字符串会自动interned

因此结果与您手动调用String.intern() 相同。

    String a = "aa";
    String b = new String(a);
    System.out.println("aa" == "aa");
    System.out.println(a == b);
    System.out.println(a.equals(b));
    System.out.println(a.intern() == b.intern());

输出:

是的

是的

是的

【讨论】:

问题是关于 FieldOffset

以上是关于String s1 == String s2 (true) 但 FieldOffset 不同的主要内容,如果未能解决你的问题,请参考以下文章

关于以下程序段的输出正确的是:String s1=“abc“+“def“;String s2=new String(s1); if(s1.equals(s2))System.out.println(代

为什么 String s1="hello" String s2 = new String("hello") s1==s2 为flase

String s1 == String s2 (true) 但 FieldOffset 不同

string

(动态规划递归) leetcode 87. Scramble String

LeetCode Interleaving String