【问题标题】:when string intern() method is getting called当字符串实习生()方法被调用时
【发布时间】:2013-09-30 08:33:28
【问题描述】:

案例一:

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

案例 2:

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

跟进问题:

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

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

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

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


问题4的答案如下:

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

文档是这样写的:

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

更多详情:

Java 6 中的 String.intern()

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

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() 的原因。 但还有其他原因。

【问题讨论】:

  • Java 文字字符串(包含在".." 中的字符串)总是在加载关联的类时隐式地被嵌入。实习一个已经实习过的字符串是无操作的。
  • 我猜是因为编译器会自动实习这两个字符串常量。
  • intern() 在这种情况下无效。尝试使用编译器,例如使用str2 = new StringBuilder("Stack").append("OverFlow").tostring()。这样,str2 需要在运行时构造,并且应该不同于(通过引用等于)str1

标签: java string memory-management


【解决方案1】:

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

不需要对strintern() 调用,因为它也是一个文字。当您需要使用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

【讨论】:

  • 你能帮我一一找到答案。这会有很大帮助。谢谢。
【解决方案2】:

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

嗯,是的,也不是。

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

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

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

因为这两个字符串已经被interned了。

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

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

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

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

【讨论】:

  • +1 太棒了。你想回答第四个问题吗?谢谢。
【解决方案3】:

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

没有。

并将str的引用分配给str1?

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

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

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

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

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

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

没有。

具体在哪里?

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

【讨论】:

    【解决方案4】:

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

    一个是

    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(字符串池)的机制

    【讨论】:

    • 这个答案不正确。 (1) 字面量does 存在于字符串池中。它是由编译器放在那里的。 (2) 'PermGem' 是堆的一部分,不是字符串池。 -1
    • 你的第一条评论是什么意思。我并不是说 perm gen 是字符串池,而是字符串池是 perm gen 的一部分。 Java 中的所有内容都存储在堆中(尽管有一个 hack 可以在堆内存上使用),我只是想说字符串池不是伊甸园空间的一部分。
    • 尽管从 Java 7 开始,String pool 的实现发生了变化。但是,在 Java 6 之前,字符串池是 perm 生成的一部分。
    • (1) 我写的东西对我来说似乎非常清楚。由于文字位于字符串池中,根据定义,将其分配给变量的操作是无条件的,这与代码中的注释相反。 (2) 由于 PermGen 是堆的一部分,“将字符串对象从堆中移动到 PermGen”是没有意义的。
    • @EJP 似乎 permGen 在堆空间之外。请参考这个docs.oracle.com/javase/6/docs/technotes/guides/management/…
    猜你喜欢
    • 2011-03-27
    • 2012-01-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-10
    • 2022-06-17
    • 2010-09-25
    相关资源
    最近更新 更多