java常量池

java的常量池有三个概念,静态常量池,运行时常量池,字符串常量池
JVM在执行某个类的时候,必须经过加载、连接、初始化,而连接又包括验证、准备、解析三个阶段。
静态常量池用于存放编译期生成的各种字面量和符号引用,而当类加载到内存中后,jvm就会将静态常量池中的内容存放到运行时常量池中。而字符串常量池存的是引用值,其存在于运行时常量池之中。

静态常量池

静态常量池也就是Class文件中的常量池。

静态常量池用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后存放到方法区的运行时常量池中。其中符号引用其实引用的就是常量池里面的字符串,但符号引用不是直接存储字符串,而是存储字符串在常量池里的索引。

当Class文件被加载完成后,java虚拟机会将静态常量池里的内容转移到运行时常量池里,在静态常量池的符号引用有一部分是会被转变为直接引用的,比如说类的静态方法或私有方法,实例构造方法,父类方法,这是因为这些方法不能被重写其他版本,所以能在加载的时候就可以将符号引用转变为直接引用,而其他的一些方法是在这个方法被第一次调用的时候才会将符号引用转变为直接引用的。

运行时常量池

运行时常量池(Runtime Constant Pool)是方法区的一部分,在jdk8之后,运行时常量池在堆中。对于运行时常量池,Java虚拟机规范没有做任何细节的要求,不同的提供商实现的虚拟机可以按照自己的需要来实现这个内存区域。不过,一般来说,除了保存Class文件中描述的符号引用外,还会把翻译出来的直接引用也存储在运行时常量池中。
运行时常量池还有个更重要的的特征:动态性。Java要求,编译期的常量池的内容可以进入运行时常量池,运行时产生的常量也可以放入池中。常用的是String类的intern()方法。
既然运行时常量池是方法区的一部分自然会受到方法区内存的限制,当常量池无法再申请到内存时会抛出OutOfMemoryError异常。

字符串常量池

字符串常量池存在堆中(在JDK7之前存在运行时常量池之中,在JDK7已经将其转移到堆中)。

使用字符串常量池,每当我们使用字面量(String s=”1”;)创建字符串常量时,JVM会首先检查字符串常量池,如果该字符串已经存在常量池中,那么就将此字符串对象的地址赋值给引用s(引用s在Java栈中)。如果字符串不存在常量池中,就会实例化该字符串并且将其放到常量池中,并将此字符串对象的地址赋值给引用s(引用s在Java栈中)。

使用字符串常量池,每当我们使用关键字new(String s=new String(”1”);)创建字符串常量时,JVM会首先检查字符串常量池,如果该字符串已经存在常量池中,那么不再在字符串常量池创建该字符串对象,而直接堆中创建该对象的副本,然后将堆中对象的地址赋值给引用s,如果字符串不存在常量池中,就会实例化该字符串并且将其放到常量池中,然后在堆中创建该对象的副本,然后将堆中对象的地址赋值给引用s。

在JDK8版本:

静态常量池在Class文件中。

JVM已经将运行时常量池从方法区中移了出来,在Java 堆(Heap)中开辟了一块区域存放运行时常量池。同时永久代被移除,以元空间代替。元空间并不在虚拟机中,而是使用本地内存。因此,默认情况下,元空间的大小仅受本地内存限制。其主要用于存放一些元数据。

字符串常量池存在于Java堆中。