2

我了解 String pool 和 intern() 方法在 java 中的工作原理。这里有一个简单的介绍。

Java 6 中的 String.intern()

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

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

Java 7 中的 String.intern()

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

资源

好的,到目前为止,我对字符串在内存中的存储方式感到满意,直到我遇到这个工具Java Visualizer。我编写了以下简单的 Java 类来可视化程序中的内存分配方式。

public class A {

   String iWillAlwaysBeOnHeap="I am on heap...!!!";


   class Dummy{

      private int dummyNumber;

      Dummy(int dummyNumber)
      {
         this.dummyNumber=dummyNumber;
      }
   }

   public static void main(String[] args) {
      A a=new A();
      a.lonelyMethod();
   }


   public void lonelyMethod()
   {
      String lostString="dangling";

      String test=new String("dangling");

      test.intern();

      String duplicateLiteral="dangling"; 

      Dummy dummy=new Dummy(4);
   }


}

我得到了以下结果:

展示台输出

如您所见,字符串文字和具有相同值的对象被重复并存储在堆栈中,而堆空间不会出现在方法本地字符串的图片中。起初我很困惑,但后来我搜索并发现了JDK 7 自动完成的逃逸分析。但是在我的代码中,我创建了一个 String 对象,它应该存储在堆上,但它在堆栈上,正如您在可视化器输出中看到的那样,但我的 Dummy 类对象存储在堆上。我无法真正掌握这种行为。方法本地字符串与其他对象和实例级字符串的处理方式有何不同?

4

1 回答 1

1

我认为您误解了可视化器输出。堆栈上的是三个对字符串对象的引用,对应于lonelyMethod. 似乎“为了您的方便”,Java Visualizer 显示了引用对象的字符串表示形式,但这并不是堆栈上的实际内容。被引用的对象看起来都是一样的,但是其中两个引用了内部字符串“dangling”,第三个(实际上是第二个,对应于 variable test)引用了堆上的一个单独的String对象。

我想 Java Visualizer 可以显示堆栈上每个对象引用的标识哈希码,但这(通常)用处不大。也许可视化器中有一个设置可以启用这种行为。这将有助于澄清发生了什么。

于 2014-08-08T17:55:34.400 回答