为什么每次我们使用 new 关键字创建字符串时 jvm 都会创建新的字符串对象

Why jvm create new string Object each time we create string using new keyword

如果 jvm 创建 string pool 用于内存优化,那么为什么每次我们使用 new 关键字创建字符串时它都会创建新对象,即使它存在于 string pool 中?

要利用字符串池,您需要使用 String#intern 而不是 new。

为了提供原始的声明风格和性能设计者引入了字符串文字。

但是当您使用 new 关键字时,您是在堆上而非常量池中显式创建对象。

当对象在堆上创建时,无法相互共享内存,与常量池不同,它们变得完全陌生。

打破堆和常量池之间的障碍String interning会帮助你。

string interning is a method of storing only one copy of each distinct string value, which must be immutable

请记住,常量池也是堆的一小部分,在可以共享内存的情况下还有一些额外的好处。

以下对象将存储在字符串池中:

String s = "hello";

并且以下对象将存储在堆中(而不是字符串池中):

String s  = new String ("hello")

写的时候

String str = new String("mystring"); 

然后它会在堆中创建一个字符串对象,就像您创建的其他对象一样。字符串字面量 "mystring" 存储在字符串常量池中。

来自Javadocs

A pool of strings, initially empty, is maintained privately by the class String.

When the intern method is invoked, if the pool already contains a string equal to this String object as determined by the equals(Object) method, then the string from the pool is returned. Otherwise, this String object is added to the pool and a reference to this String object is returned.

It follows that for any two strings s and t, s.intern() == t.intern() is true if and only if s.equals(t) is true.

强制执行垃圾收集!。如果你只需要一次字符串,那么将它保存在内存中是没有意义的(对于 almost 永远。常量池中的 Strings 就是这种情况).不在常量池中的字符串可以像任何其他对象一样进行 GC。因此,您应该只将 经常使用的字符串 保留在常量池中(通过使用文字或驻留它们)。

以字符串字面量(String s = "string";)形式创建的字符串存储在字符串池中,但使用new(String s = new String("string");)调用String构造函数创建的字符串不存储在字符串池中。

... why does Java create new Object each time we create a string using the new keyword even though it exists in string pool?

因为你明确告诉它! new 运算符 always 创建一个新对象。 JLS 15.9.4 说:

"The value of a class instance creation expression is a reference to the newly created object of the specified class. Every time the expression is evaluated, a fresh object is created."


郑重声明,调用 new String(String) 几乎总是错误的……但在不明确的情况下它可能会有用。可以想象,您可能需要 equals returns true== 给出 false 的字符串。调用 new String(String) 会给你那个。


对于旧版本的 Java,substringtrim 和可能的其他 String 方法会给你一个共享后备存储的字符串与原件。在某些情况下,这可能会导致内存泄漏。例如,调用 new String(str.trim()) 可以防止内存泄漏,但代价是创建修剪后的字符串的新副本。 String(String) 构造函数保证分配一个新的后备数组并为您提供一个新的 String 对象。

substringtrim 的这种行为在 Java 7 中发生了变化。