java中String对象的创建过程和JVM中的状态分析

对于String对象的创建过程和存储状态进行分析

作者 jooop 日期 2017-03-13
JVM
java中String对象的创建过程和JVM中的状态分析

java中String对象的创建过程和JVM中的状态分析

String s = new String("xyz"); 创建了几个String Object?

以下是在网上查到的关于上述问题流传较为广泛的说法:

  • 两个或一个,”xyz”对应一个对象,这个对象放在字符串常量缓冲区,常量”xyz”不管出现多 少遍,都是缓冲区中的那一个。New String 每写一遍,就创建一个新的对象,它一句那个 常量”xyz”对象的内容来创建出一个新 String 对象。如果以前就用过’xyz’,这句代表就不会 创建”xyz”自己了,直接从缓冲区拿。

对上述问题的修正:String s = new String("xyz");运行时涉及几个实例

在对该问题的答案的寻找中,在RednaxelaFX大牛的博客中看到了对该问题的质疑,总结下来认为若按第一种问法是没有标准答案的。
而若按java虚拟机规范定义的来看,将问题改为String s = new String("xyz"); 在运行时涉及几个实例
答案则应该为两个,一个是字符串字面量"xyz"所对应的、驻留(intern)在一个全局共享的字符串常量池中的实例,另一个是通过new String(String)创建并初始化的、内容与"xyz"相同的实例

什么是String池,它在哪里?

上面问题的回答中都有涉及到字符串常量池,流传较广的说法中更具体的称为String池
对于String池是什么,它在虚拟机的内存中是怎么分配的一直较为迷惑。
最近刚好阅读《深入理解JAVA虚拟机》一书,书中对于java虚拟机运行时数据区是这么划分的:
Alt text

  • 流行的对java内存中存放数据的描述,一般较为粗糙的划分为“堆”和“栈”。
    “栈”即指虚拟机栈中的局部变量表部分。局部表量表中存放着编译期可知的各种基本数据类型、引用对象和returnAddress类型(指向了一条字节码指令的地址)。
    “堆”虚拟机启动时创建,它的唯一目的是存放对象实例。
  • “方法区”:用于存储被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。另外,方法区在Java虚拟机规范中被描述为一个逻辑分区。
  • 运行时常量池是方法区的一部分。Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池,用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后进入方法区的运行时常量池存放。
  • 对于HotSpot虚拟机来说,方法区是用“永久代”来实现的。因为容易遇到内存溢出问题。在JDK1.7的HotSpot中,已经把原本存放在永久代的字符串常量池移除

根据上面描述,可推出“String池”即字符串常量池在JDK1.7之前是存在于方法区的。
Alt text

自JDK1.7开始,字符串常量池又去了哪里?

根据官方描述JEP 122: Remove the Permanent Generation

  • Description
    Move part of the contents of the permanent generation in Hotspot to the Java heap and the remainder to native memory.
    Hotspot’s representation of Java classes (referred to here as class meta-data) is currently stored in a portion of the Java heap referred to as the permanent generation. In addition, interned Strings and class static variables are stored in the permanent generation. The permanent generation is managed by Hotspot and must have enough room for all the class meta-data, interned Strings and class statics used by the Java application. Class metadata and statics are allocated in the permanent generation when a class is loaded and are garbage collected from the permanent generation when the class is unloaded. Interned Strings are also garbage collected when the permanent generation is GC’ed.
    The proposed implementation will allocate class meta-data in native memory and move interned Strings and class statics to the Java heap.

根据描述,永久代中的字符串常量池被移到堆中,而元信息则被移到本地内存中的”元空间(Metaspace)”中。并且会被垃圾回收机制回收。