为什么 JVM 在字符串池内存中没有“看到”重复的字符串值?

Wra*_*per 4 java string immutability string-pool

这可能是重复的,但找不到我需要的解释。有人可以向我解释一下吗?

如果我是对的:

String s1 = "a";
String s2 = "a";
Run Code Online (Sandbox Code Playgroud)

s1和s2都会指向字符串池中的同一个地址,并且只有一个值为“a”的对象。

现在,如果我这样做:

String s1 = "a"; //line A
s1 = s1.concat("b"); //line B; I don't understand what's happening here in terms of references
String s2 = "ab";//line C
System.out.println(s1 == s2); //false
Run Code Online (Sandbox Code Playgroud)

为什么我得到false

我的看法(可能是错误的)是这样的:

after line Aa->在字符串池中创建对象(值为),由 引用s1after line B-> 在字符串池中创建具有值的对象b(没有引用),然后ab在字符串池中创建具有值的新对象(由现有的引用s1after line C->(这可能是我错了)由于具有值的现有对象ab(由现有的引用)s1) 使用 (或 +) 创建concat(),JVM 不会重用该引用指向的对象s2,而只是在 String 池中创建具有ab引用指向的值的新对象s2

我哪里错了?

Gio*_*uri 5

TL;DR - 您感到困惑的地方是字符串的 Java 内存模型,即内存的堆字符串常量池区域。


深入探讨字符串内存模型

设计动机

在 Java 中,String可能是使用最频繁的对象。因此,Java 使用特殊的内存设计策略来维护 String 对象,将它们保存在 Heap 中,或者保存在称为String Constant Pool的堆的隔离子集中。,或同时保存在两者中。

字符串常量池是Heap内存中的一块特殊空间,里面保存着唯一的String对象"literal value"。任何时候你用它的字面值创建一个字符串,JVM首先检查字符串池中是否有相同值的对象,如果是,则返回对同一对象的引用,如果不存在,则返回新对象在字符串常量池中分配,并且对于所有其他字符串文字创建一次又一次发生同样的情况。

为什么拥有常量池是一个好主意,原因是这个短语本身的语义 - 因为它存储常量不可变的String 对象,正如您所看到的,对于您可能创建许多 String 的情况来说,这是一个好主意具有相同文字内容的对象 - 在所有这些情况下,"literal value"每次只会引用一个对象,并且不会为现有的 String 文字对象创建更新的对象

请注意,这是可能的,因为 String 根据定义是不可变的。另请注意,字符串池最初为空,由 String 类私有维护。

Java 将 String 对象放在哪里?

现在事情变得有趣了。需要记住的重要一点是,每当您使用new String()指令创建 String 对象时,都会强制 Java 将新对象分配到Heap中;但是,如果您使用 创建 String 对象,它将在String Constant Pool"string literal"中分配。正如我们所说,字符串常量池存在主要是为了减少内存的使用,提高内存中已有的String对象的复用性。

所以,如果你写:

String s1 = "a";
String s2 = "a";
String s3 = new String("a");
Run Code Online (Sandbox Code Playgroud)
  1. 字符串对象将被创建到字符串常量池中,对该对象的引用将被存储到变量中s1
  2. 字符串常量池将被查找,因为存在具有相同字面值的对象("a",因此会返回对该对象的引用;
  3. String 对象将在堆区域上显式创建,并且引用将被返回并存储到变量中s3

内部弦乐

如果您希望将使用运算符创建的 String 对象移动new字符串常量池中,您可以调用方法,并且"your_string_text".intern();发生以下两种情况之一:

  1. 如果池中已包含由 equals(Object) 方法确定等于此 String 对象的字符串,则将返回池中的字符串;
  2. 否则,这个 String 对象将被添加到池中,并返回对此 String 对象的引用。

您的代码中发生了什么?

String s1 = "a";
String s2 = "a";
Run Code Online (Sandbox Code Playgroud)

s1和s2都会指向字符串池中的同一个地址,并且只有一个值为“a”的对象。

真的。最初,将创建 String 对象并将其放入String Constant Pool中。之后,由于已经存在值为 的 String "a",因此不会为其创建新对象s2,并且存储在 中的引用s1将类似地存储到 中s2

现在,我们终于来看看您的问题:

String s1 = "a"; //allocated in the String Constant Pool
s1 = s1.concat("b"); //contact() returns a new String object, allocated in the Heap
String s2 = "ab";//"ab" still does NOT exist in the String Constant Pool, and it gets allocated there
System.out.println(s1 == s2); //returns false, because one object is in the Heap, and another is in the String Constant Pool, and as there already exists the object in the pool, with the same value, existing object will be returned by `intern()`.
Run Code Online (Sandbox Code Playgroud)

但是,如果您愿意执行

System.out.println(s1.intern() == s2);
Run Code Online (Sandbox Code Playgroud)

这将会回归true,我希望现在你明白 - 为什么。因为会将通过Heapintern()引用的对象移动到String Constant Pools1