1

情况1:

  String str = "StackOverFlow";
  String str1 = "StackOverFlow";
  if(str==str1){
      System.out.println("equal");//prints equal
  }

案例二:

  String str = "StackOverFlow";
  String str1=str.intern();
  if(str==str1){
      System.out.println("equal");//prints equal
  }

跟进问题:

  1. 我想知道对于第一种情况JVM是否在intern()内部调用并将引用分配strstr1

  2. 在第一种情况下两个引用如何相等?

  3. 第一种情况是否意味着每当您声明一个字符串,就像String str = "StackOverFlow";它添加到字符串池中一样与intern()方法相同?

  4. 是否由堆外使用String str = "StackOverFlow";并分配的字符串池?intern()如果是,具体在哪里?


对于问题 4,答案如下:

在 Java 6 及更早版本中,实习字符串也存储在永久代中。在 Java 7 中,interned 字符串存储在主对象堆中。

这是文档中的内容:

在 JDK 7 中,interned 字符串不再分配在 Java 堆的永久代中,而是与应用程序创建的其他对象一起分配在 Java 堆的主要部分(称为年轻代和年老代)中. 此更改将导致更多数据驻留在主 Java 堆中,而永久代中的数据更少,因此可能需要调整堆大小。由于此更改,大多数应用程序只会看到相对较小的堆使用差异,但加载许多类或大量使用该String.intern()方法的大型应用程序将看到更显着的差异。

更多细节来自这里:

Java 6 中的 String.intern()

在过去的美好时光里,所有的实习字符串都存储在 PermGen 中——堆的固定大小部分,主要用于存储加载的类和字符串池。除了显式嵌入的字符串之外,PermGen 字符串池还包含您程序中之前使用的所有文字字符串(这里使用了重要的词——如果从未加载/调用过类或方法,则不会加载其中定义的任何常量)。

The biggest issue with such string pool in Java 6 was its location – the PermGen. PermGen has a fixed size and can not be expanded at

运行。您可以使用 -XX:MaxPermSize=96m 选项进行设置。据我所知,默认 PermGen 大小在 32M 到 96M 之间变化,具体取决于平台。您可以增加它的大小,但它的大小仍然是固定的。这种限制需要非常小心地使用 String.intern——你最好不要使用这种方法来实习任何不受控制的用户输入。这就是为什么在 Java 6 时代字符串池主要是在手动管理的映射中实现的。

Java 7 中的 String.intern()

Oracle 工程师对 Java 7 中的字符串池逻辑进行了极其重要的更改——将字符串池重新定位到堆中。这意味着您不再受单独的固定大小内存区域的限制。所有字符串现在都位于堆中,就像大多数其他普通对象一样,这允许您在调整应用程序时仅管理堆大小。从技术上讲,仅此一项就足以成为重新考虑在 Java 7 程序中使用 String.intern() 的充分理由。但还有其他原因。

4

4 回答 4

4

引用是相等的,因为它们都是字符串文字。

intern()不需要调用 on ,str因为它也是文字。一个你需要使用的例子intern()(顺便说一下,它的速度要慢得多equals(),所以不要使用它)是在构造带有字节或字符数组的字符串时。

例如:

final String str1 = "I am a literal";
final String str2 = new String(str1.toCharArray());

final boolean check1 = str1 == str2;          // false
final boolean check2 = str1 == str2.intern(); // true
于 2013-09-24T22:36:47.990 回答
2

1)我想知道对于第一种情况JVM是否在内部调用intern()并将str的引用分配给str1?

嗯,是的,也不是。

是的,该intern()方法在内部调用的。但是在运行该代码时不会发生调用。事实上,它发生在加载该代码时。加载器然后保存对实习字符串的引用。

但是在这种情况下,加载过程只需要进行一次实习。这两个文字(在这种情况下)实际上将由正在加载的类中的单个“常量池条目”表示。(Java 编译器会在编译时发现类中的重复文字......并消除它。)

2)在第一种情况下两个引用如何相等?

因为这两根弦都被扣留了。

3)第一种情况是否意味着每当您声明像 String str = "StackOverFlow"; 这样的字符串时 它与 intern() 方法一样添加到字符串池中吗?

是的...以运行包含声明的代码时不会发生实习为模。

4) 是否在堆外使用String str = "StackOverFlow";和分配的字符串池?intern()如果是,具体在哪里?

答案在某种程度上取决于系统。一般来说,字符串池在堆中。在某些系统上,堆被划分为具有不同垃圾收集策略的区域或空间,字符串池分配在所谓的“permgen”空间中,该空间的大小与堆的其余部分无关。但这并不总是正确的。

于 2013-09-24T22:51:41.103 回答
1

我想知道对于第一种情况 JVM 是否在内部调用 intern()

不。

并将str的引用分配给str1?

是的,但是因为该值是文字,而不是因为实习。首先在 .class 文件中只有一个实例。

在第一种情况下两个引用如何相等?

这不是另一个问题,只是重复了同样的问题。

第一种情况是否意味着每当您声明像 String str = "StackOverFlow"; 这样的字符串时 它与 intern() 方法一样添加到字符串池中吗?

是的,但它是由编译器完成的,而不是 intern()。

String str = "StackOverFlow"; 使用的字符串池是否 和 intern() 是在堆外分配的吗?

不。

究竟在哪里?

在加载类的常量池中,也就是堆中。

于 2013-09-25T00:17:45.953 回答
-1

主要有两种创建字符串对象的方法。

一个是

String str = "test-String"; // This string is created in string pool or returned from
string pool if already exists

第二个

String str = new String("test-string");// This string object will be created in heap memory and will be treated as any other object

当您在使用 new 运算符创建的字符串实例上调用 string.intern 时...此字符串将在字符串池中创建或从池中返回(如果存在)。这是一种将字符串对象从堆移动到 perm Gen(字符串池)的机制

于 2013-09-24T22:40:08.970 回答