前言

办法区(Method Area)是线程共享的一块内存区域,JVM加载的类型信息常量动态变量即时编译器编译后的代码缓存等数据均寄存于办法区。

运行时常量池(Runtime Constant Pool)是办法区的一部分,在Class文件中有一部分内容为常量池表(Constant Pool Table),用于寄存编译期生成的各种字面量与符号援用,这部分内容在Class文件被加载到JVM后会被寄存在运行时常量池中。

字符串常量池中寄存字符串字面量,在JDK1.8中,字符串常量池存在于堆中。

本篇文章将对JDK1.8中的办法区,运行时常量池和字符串常量池的区域散布进行阐明,并着重对字符串常量池进行剖析以探索new一个字符串对象时到底会在堆上创立几个对象。

JDK版本:1.8
参考资料:《深刻了解Java虚拟机第三版》

注释

一. 办法区的区域散布

办法区只是一个逻辑概念,在JDK1.8中办法区的具体实现为元空间,而元空间应用的是本地内存。在JDK1.8中,办法区,运行时常量池和字符串常量池的区域散布示意图如下所示。

即字符串常量池存在于堆中,并且如果要设置办法区大小,须要应用-XX:MaxMetaspaceSize=指令进行设置。

JDK1.8中应用元空间作为办法区的实现以代替永恒代(PermGen),有如下起因。

  • 永恒代作为办法区的实现时,字符串常量池存在于运行时常量池中,即字符串常量池存在于办法区中,而办法区只有Full GC时才会被清理,因而容易呈现因为字符串常量池导致的内存溢出;
  • 类型信息等数据大小不容易确定,将其寄存到本地内存更为适合。

二. 字符串常量池

首先答复那个经典的问题:new一个字符串对象会创立几个对象。答案是一个或者两个

字符串常量池中会存储字符串字面量,字符串字面量实质就是对象,当在代码中呈现如下代码。

String str = "sakura";

如果字符串常量池中曾经存在sakura这个字符串字面量,那么str会指向字符串常量池中的sakura字符串字面量,反之,则会先将sakura这个字符串字面量增加到字符串常量池中,而后再将str指向字符串常量池中的sakura字符串字面量。

更甚一步,其实只有代码中呈现双引号括起来的字符串,那么就会去字符串常量池中寻找对应的字符串字面量,如果寻找不到,则创立字符串字面量并增加到字符串常量池中。

当初如果在代码中呈现如下代码。

String str = new String("sakura");

首先呈现了双引号括起来的sakura字符串,所以就会去字符串常量池中寻找对应的字符串字面量,如果寻找不到,则创立sakura字符串字面量并增加到字符串常量池中,如果寻找到,则间接应用字符串常量池中的sakura字符串字面量。最初,会在堆上创立一个字符串对象,str会指向堆上创立进去的字符串对象。所以new一个字符串对象时,能够必定的是肯定会在堆上创立一个字符串对象,然而字符串常量池中是否会创立一个字符串字面量,要取决于字符串字面量之前是否曾经存在,曾经存在则不会再反复创立。所以new一个字符串对象会创立几个对象的答案是一个或者两个。

为了加深了解,思考如下的示例。

public class StringTest {    public static void main(String[] args) {        String str1 = new String("sakura") + new String("sakura");  //步骤1        String str2 = "sakurasakura";  //步骤2        System.out.println(str1 == str2);  //步骤3    }}

当执行完步骤1后,堆上的状况如下所示。

执行完步骤2后,堆上的状况如下所示。

所以最终步骤3的打印后果肯定是false

3. String的intern()办法

首先思考如下的示例。

public class StringTest {    public static void main(String[] args) {        String str1 = new String("sakura") + new String("sakura");  //步骤1                str1.intern();  //步骤2        String str2 = "sakurasakura";  //步骤3        System.out.println(str1 == str2);  //步骤4    }}

上述示例和第2大节中的示例差不多,只不过多了一步str1.intern()Stringintern()办法会依据以后字符串对象的值去字符串常量池中进行匹配,如果字符串常量池中存在字符串字面量的值与以后字符串对象的值相等,则返回这个字符串字面量的地址,如果字符串常量池中不存在字符串字面量的值与以后字符串对象的值相等,则在字符串常量池中注册一个援用并指向以后字符串对象,并最初返回以后字符串对象的地址。那么上述示例中,执行完步骤2后,堆上的状况如下所示。

执行完步骤3后,堆上的状况如下所示。

所以最终步骤4的打印后果肯定是true

通过上述示例能够晓得,字符串常量池中除了存储字符串字面量以外,还会存储指向堆上的字符串对象的援用。

总结

办法区用于寄存JVM加载的类型信息常量动态变量即时编译器编译后的代码缓存等数据,JDK1.8中应用元空间作为办法区的实现,元空间应用的是本地内存。字符串常量池中会存储字符串字面量,字符串字面量实质就是对象,当new一个字符串对象时,肯定会在堆上创立一个字符串对象,然而字符串常量池中是否会创立一个字符串字面量,要取决于字符串字面量之前是否曾经存在,曾经存在则不会再反复创立,所以new一个字符串对象会创立几个对象的答案是一个或者两个。