Java常量池详解:字符串常量池、Class常量池、运行时常量池 三者关系

文章目录

  • 引入:方法区常量池概述
  • 字符串常量池
  • class常量池
  • 运行时常量池

这里介绍 字符串常量池、class常量池 和 运行时常量池 这三个常量池的概念。

引入:方法区常量池概述

方法区包含运行时常量池、自动和方法数据、构造函数和普通方法的字节码内容、一些特殊方法。这里虽然没有具体说字符串常量池,但是它也是方法区的一部分。

在JDK1.8中,使用元空间代替永久代来实现方法区,但是方法区并没有改变,变动的只是方法区中内容的物理存放位置。正如上面所说,类型信息(元数据信息)等其他信息被移动到了元空间中;但是运行时常量池和字符串常量池被移动到了堆中。但是不论它们物理上如何存放,逻辑上还是属于方法区

关于关于为什么移除永久代?

  1. 字符串常量池存在永久代中,容易出现性能问题和内存溢出。
  2. 类及方法的信息等比较难确定其大小,因此对于永久代的大小指定比较困难,太小容易出现永久代溢出,太大则容易导致老年代溢出。
  3. 永久代会为 GC 带来不必要的复杂度,并且回收效率偏低。

字符串常量池

全局字符串池里的内容是在类加载完成,经过验证,准备阶段之后在堆中生成字符串对象实例,然后将该字符串对象实例的引用值存到string pool中(string pool中存的是引用值而不是具体的实例对象,具体的实例对象是在堆中开辟的一块空间存放的)。

在HotSpot VM里实现的string pool功能的是一个StringTable类,它是一个哈希表,里面存的是字符串的引用。这个StringTable在每个HotSpot VM的实例只有一份,被所有的类共享。

这里字符串常量池里的字符串实际上是驻留字符串
驻留的英文是Interning,这和代码上new String("abs").intern()的intern方法是一个意思,就是拿到“abs”字符串在字符串常量池里的引用,这个指定abs字符串在字符串常量池的唯一引用就是驻留
通过intern()方法,实际上"abs"==new String("abs").intern()这个布尔结果就是true,而"abs"==new String("abs")的结果是false。

class常量池

准确的来说,它不叫class常量池,而是Class文件的常量池表(Constant Pool Table)。Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息就是常量池表,它用于存放编译器生成的各种字面量与符号引用。

字面量就是我们所说的常量概念,如文本字符串、被声明为final的常量值等。 符号引用是一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能无歧义地定位到目标即可(它与直接引用区分一下,直接引用一般是指向方法区的本地指针,相对偏移量或是一个能间接定位到目标的句柄)。

运行时常量池

jvm在执行某个类的时候,必须经过加载、验证、准备、解析、初始化这样的类加载过程。而当类加载到内存中后,jvm就会将class常量池中的内容存放到运行时常量池中。由此可知,同class文件的常量池表一样,运行时常量池也是每个类都有一个

运行时常量池是运行时加载的,而class文件常量池表是在编译时期确认的,当字节码文件被加载进入内存并经过类的解析阶段之后,这个类的运行时常量池才会产生。而经过解析(resolve)之后,也就是把符号引用替换为直接引用,解析的过程会去查询全局字符串池,也就是我们上面所说的StringTable,以保证运行时常量池所引用的字符串与全局字符串池中所引用的是一致的。

你可能感兴趣的:(java)