【问题标题】:How does string interning work in Java 7+? [duplicate]字符串实习在 Java 7+ 中如何工作? [复制]
【发布时间】:2015-01-28 09:20:36
【问题描述】:

所以,我意识到我要问的问题与一个被一次又一次殴打致死的话题有关,但是,即使在阅读了我能找到的所有答案和文档之后,我仍然很友善对字符串实习感到困惑。可能是因为我对JVM缺乏了解;也许是由于 Java 7 中引入的更改贬低了许多上述答案和文档。无论哪种方式,我都被卡住了,我希望有人可以帮助我更清楚地理解这个概念......

String a = "text";
String b = new String("text");

在上面的例子中,我知道会创建两个 String 对象。我也知道内存中只有一个包含序列't', 'e', 'x', and 't' 的字符数组。

但是,每个字符串对象实际存储在内存中的哪个位置?

如果我读过的内容我没看错:变量a 的引用对象将存储在常量池中,而b 的引用对象将存储在堆中,对吧?

如果是这样,我对实习生池如何维护实习生字符串感到困惑。它是否跟踪常量池中定义的字符串以及从堆中手动实习(调用.intern())的字符串? JVM 是否创建常量池中定义的字符串对象并将它们加载到实习池中?我很困惑这一切是如何运作的......

再次,很抱歉提出如此令人困惑/愚蠢的问题,只是我对JVM 的结构和内部工作方式比较陌生,其中很多都让我头晕目眩。谢谢!

【问题讨论】:

  • 我建议你阅读所有答案here
  • 与旧版本相比,Java 7 和 8 中的字符串实习基本没有改变。

标签: java string jvm heap-memory string-interning


【解决方案1】:

java中有一个叫String Memory Pool的东西,当你声明时:

String str1="abc";

它进入那个内存池而不是堆。但是当你写的时候:

String str2=new String("abc");

它在堆上创建一个完整的对象,如果你再写:

String str3 = "abc"; 

它不会在池中创建更多对象,它会检查池是否已经存在这个字面量,它会将其分配给它。但写作:

String str4 = new String("abc");

将再次在堆上创建一个新对象

关键是:

一个新对象总是会在你不断写的时候在堆上创建多少次:

new String("abc");

但是如果你继续直接分配字符串而不使用关键字new,它只会从内存池中被引用(或者如果内存池中不存在则被创建)

intern() 方法查找字符串是否存在于内存池中,如果不存在,则将其添加到内存池并返回对它的引用。因此,在使用此方法后,您的 String 引用没有指向堆上的任何对象,而是指向字符串内存池中的对象(另外,请注意内存池仅包含唯一字符串)。

【讨论】:

  • 从 jdk 7 和 8 开始,实习字符串对象也存储在堆中。 java-performance.info/string-intern-in-java-6-7-8
  • 如果对象不存在,那么 new String("test").intern() 怎么办?如果 1. 文字已经在池中,或者 2. 已经在堆中怎么办?
  • @xploreraj 检查 java api 文档中的 intern(),它的目的很明确。
【解决方案2】:

当您说 new String() 时,您会得到一个新的 Object 引用,因此请考虑

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

然后首先a == b 将显示false,因为它们是不同的引用。如果我们 intern() bb = b.intern() 我们可以再次测试并得到 true。我希望这会有所帮助。从 1.0 版开始,上述代码在 Java 中的工作方式相同(今天在 Java 8 中仍然以这种方式工作)。

【讨论】:

  • 感谢您的快速回复,Elliott。我知道上述两个条件都成立。我想更准确地说,让我感到困惑的是以下内容:在常量池中指定的字符串值(我已经设法在 cmd 中使用命令“javap -c -verbose ClassName.class”读取)变为实例化 + 加载到从 Java 7 开始在堆上找到的实习池?常量池是运行时的实际实体吗? (对不起,如果我的问题令人困惑,如果需要澄清,请告诉我)
  • @KyleMart 第一个产生错误。试试吧。我试图解释intern() 池的工作原理。
  • 我的错。哈哈。我真的想说这两个条件中的前者会产生错误。写我最后的评论时完全被吓坏了。第一个条件等于 false 的原因是因为 var a 引用了实习池中的字符串,而 var b 引用了堆上的字符串,对吧?让我困惑的是实习生池和常量池之间的关系。
  • @KyleMart 考虑System.out.println(new Integer(1024) == Integer.valueOf(1024));
  • 那是错误的,对吧?左边的操作数创建一个新的整数对象,而右边的 valueOf 方法创建一个 val 为 1024 的新整数对象,缓存它,并返回对缓存对象的引用。
猜你喜欢
  • 2013-05-02
  • 1970-01-01
  • 2011-04-22
  • 1970-01-01
  • 2013-11-03
  • 1970-01-01
  • 2012-05-21
  • 2017-12-23
  • 2023-03-03
相关资源
最近更新 更多