【搞定Java基础】第3篇:Java 中的常量池:字符串常量池、class 常量池、运行时常量池 【解惑文章】

本文转发自:https://blog.csdn.net/zm13007310400/article/details/77534349

这几天在看 Java 虚拟机方面的知识时,看到了有几种不同常量池的说法,然后我就去 CSDN、博客园等上找资料,里面说的内容真是百花齐放,各自争艳,因此,我好好整理了一下,将我自认为对的理解写下来与大家共同探讨:

在 Java 的内存分配中,总共 3 种常量池:字符串常量池、class 常量池、运行时常量池

1、字符串常量池(String Constant Pool)

1.1  字符串常量池在 Java 内存区域的哪个位置?

在 JDK6.0 及之前版本,字符串常量池是放在 Perm Gen 区(也就是方法区)中。

在 JDK7.0 版本,字符串常量池被移到了堆中了。至于为什么移到堆内,大概是由于方法区的内存空间太小了。

1.2  字符串常量池是什么?

在 HotSpot VM 里实现的 String Pool 功能的是一个 StringTable 类,它是一个 Hash 表,默认值大小长度是 1009。这个 StringTable 在每个 HotSpot VM 的实例只有一份,被所有的类共享。字符串常量由一个一个字符组成,放在了 StringTable上。

在 JDK6.0 中,StringTable 的长度是固定的,长度就是1009,因此如果放入 String Pool 中的 String 非常多,就会造成hash 冲突,导致链表过长,当调用 String#intern() 时会需要到链表上一个一个找,从而导致性能大幅度下降。

在JDK7.0中,StringTable 的长度可以通过参数指定:

-XX:StringTableSize = 66666

1.3  字符串常量池里放的是什么?

在 JDK6.0 及之前版本中,String Pool 里放的都是字符串常量。

在 JDK7.0 中,由于 String#intern() 发生了改变,因此 String Pool 中也可以存放放于堆内的字符串对象的引用。

需要说明的是:字符串常量池中的字符串只存在一份! 如:

String s1 = "hello, world !";
String s2 = "hello, world !";

即执行完第一行代码后,常量池中已存在 “hello,world!”,那么 s2不会在常量池中申请新的空间,而是直接把已存在的字符串内存地址返回给 s2。这里具体的字符串如何分配就不细说了。

2、class 常量池(Class Constant Pool)

2.1  class 常量池简介

我们写的每一个 Java 类被编译后,就会形成一份 class 文件。class 文件中除了包含类的版本、字段、方法、接口等描述信息外,还有一项信息就是常量池(Constant Pool Table),用于存放编译器生成的各种字面量(Literal)和符号引用(Symbolic References)。

每个 class 文件都有一个 class 常量池。

2.2  什么是字面量和符号引用?

  • 字面量包括:

1、文本字符串;

2、八种基本类型的值;

3、被声明为 final 的常量等。

  • 符号引用包括:

1、类和方法的全限定名;

2、方法的名称和描述符;

3、字段的名称和描述符。

3、运行时常量池(Runtime Constant Pool)

运行时常量池存在于内存中,也就是 class 常量池被加载到内存之后的版本。不同之处是:它的字面量可以动态的添加(String#intern()),符号引用可以被解析为直接引用。

JVM 在执行某个类的时候,必须经过加载、连接、初始化,而连接又包括验证、准备、解析三个阶段。而当类加载到内存中后,JVM 就会将 class 常量池中的内容存放到运行时常量池中。由此可知,运行时常量池也是每个类都有一个。在解析阶段,会把符号引用替换为直接引用,解析的过程会去查询字符串常量池,也就是我们上面所说的 StringTable,以保证运行时常量池所引用的字符串与字符串常量池中是一致的。

猜你喜欢

转载自blog.csdn.net/pcwl1206/article/details/86535125