我了解 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 类对象存储在堆上。我无法真正掌握这种行为。方法本地字符串与其他对象和实例级字符串的处理方式有何不同?