【问题标题】:Method Local Strings Memory Allocation?方法本地字符串内存分配?
【发布时间】:2014-08-08 17:24:37
【问题描述】:

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

Java 6 中的 String.intern()

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

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

Java 7 中的 String.intern()

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

Source

好的,到目前为止,我对字符串在内存中的存储方式感到满意,直到我遇到了这个工具 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);
   }


}

我得到了以下结果:

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

【问题讨论】:

  • “在我的代码中,我创建了一个存储在堆栈上的 String 对象”——不,你没有。重复您自己引用的文档:“所有字符串现在都位于堆中”。 (这是在 Java 7 中;在早期版本中,字符串可能在堆中或 PermGen 中,但从不在堆栈中。)
  • @TedHopp 好的,我纠正了自己“我创建了一个应该存储在堆上的字符串对象,但它在堆栈上,正如您在可视化器输出中看到的那样”抱歉我的英语不好..编辑相同..
  • 您需要通过test = test.intern(); 记住对实习版本的引用。
  • Java 运行时存储对象的方式与 String#intern 和引用的语义无关。例如,VM 可以进行逃逸分析,并且根本不将本地分配/使用的临时对象放入堆中。对于 Java 程序员来说,这种神奇的优化在大多数情况下是显而易见的(基于错误理解的优化通常会抑制这样的事情)。
  • Visualizer 很好,但它不能正确地表示现实(既不是逻辑模型也不是物理模型)。它不将字符串视为对象,也不知道字符串池。

标签: java string string-pool


【解决方案1】:

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

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

【讨论】:

  • docs.oracle.com/javase/7/docs/technotes/guides/vm/… 怎么样?逃逸分析?我的代码没有发生这种情况吗?
  • @DanglingPiyush - 转义分析特定于 HotSpot Java 编译器。 Java Visualizer 工具不使用该编译器。据我所知,它有自己的内部编译器(可能绝对没有优化)。
猜你喜欢
  • 1970-01-01
  • 2020-02-01
  • 1970-01-01
  • 2011-03-22
  • 2021-07-27
  • 1970-01-01
  • 2020-03-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多