JVM 各种常量池傻傻分不清?

总体上有如下四种常量池:
class 文件中的常量池、运行时常量池、字符串常量池、符号常量池。

class 文件中的常量池:一个类对应一个 class 文件,每个 class 文件中都有一个常量池。这个常量池是一个静态的常量池,存在于文件中,而不是内存中。记录了类中的字面量和符号引用。

运行时常量池:与 class 文件中静态的常量池相对的是 JVM 进程中的运行时常量池,运行时常量池是 JVM 加载 class 文件(类加载)后,class 文件中的常量池在内存中的表现形式,存储在 JVM 的方法区。显然,运行时常量池和类也是一一对应的。

字符串常量池:管理字符串常量(String)的常量池。是全局共享的,整个 JVM 进程中只有一个。

符号常量池:管理符号常量(Symbol)的常量池。是全局共享的,整个 JVM 进程中只有一个。

总是说常量池常量池,这个池子(pool)究竟是什么样的呢?所有常量池的池子都是一样的吗?
当然不一样,池(pool)只是用来表达存放数据的含义,具体这个数据怎么存,跟 pool 这个词无关。对于 class 文件中的常量池来说,就是外存中文件里的一段二进制数据。对于运行时常量池来说就是内存中的一个数组。而对于字符串常量池和符号常量池来说就是内存中的一个哈希表。

JDK1.7 中各个常量池的内存分布:

java7

绿字表示和类对应,一个 JVM 进程里有多个。红字表示和 JVM 进程对应,一个 JVM 进程里只有一个。

JDK1.8 中各个常量池的内存分布:

java8

class 文件中的常量池

CONSTANT_Utf8 描述类中的所有字符串,真正存储字符串的常量,所有需要字符串的常量都通过引用这个类型的常量存储字符串。使用改进后的 utf-8 编码。

CONSTANT_Integer、CONSTANT_Long、CONSTANT_Float、CONSTANT_Double 描述类中的基本类型常量。(比较小的话不会放到常量池,直接嵌到字节码的操作数)
CONSTANT_String 描述类中的 String 常量,引用了 CONSTANT_Utf8 常量,自身并没有存储字符串。

CONSTANT_Class 描述当前类所依赖类的全限定名,用于加载所依赖的类
CONSTANT_Fieldref、CONSTANT_Methodref 和 CONSTANT_InterfaceMethodref 描述了当前类依赖的其它类的字段、方法和其它接口的方法。这三种常量又引用了 CONSTANT_Class、CONSTANT_NameAndType 共同构成了字段和方法的全限定名和描述符,用于寻找依赖字段和方法

关于 class 文件中常量池的描述,具体的可以参考 Java 虚拟机规范。

字符串常量池、符号常量池

字符串常量池和符号常量池持有的都是引用。String 常量在堆里,Symbol 常量(对应 CONSTANT_Utf8,是 C++ 对象,所有 CONSTANT_Utf8 常量在类加载时构造成 Symbol 对象,并放入全局的 SymbolTable,统一管理)在直接内存里。

字符串常量不会在类加载时就进入字符串常量池。
代码示例:

public class Test {

    static String test(){
        String a = "hehe";
        String b = "haha";
        return a+b;
    }
    
    public static void main(String[]args){
        String s1 = new StringBuilder().append("he").append("he").toString();
        System.out.println(s1 == s1.intern());
        Test.test();
        String s2 = new StringBuilder().append("ha").append("ha").toString();
        System.out.println(s2 == s2.intern());
    }

}

输出:
true
false

你可能感兴趣的:(JVM 各种常量池傻傻分不清?)