JVM | 运行时常量池和字符串常量池及 intern()

本贴最后更新于 1509 天前,其中的信息可能已经时移世异

本文知识点

​ 这一块的知识点,一直都是最为混乱的,网上很多博客有的都自相矛盾,因此,这一块需要深入学习,本文以 JDK11 为基础,尽量参考官方文档

  • 静态常量池
  • 运行时常量池及字符串的引用
  • 各种 String 语句变量的地址

参考文档:

Chapter 5. Loading, Linking, and Initializing

静态常量池

静态常量池也常常称做为 class 常量池,是每个.class 文件都有的,如测试代码如下:

public class Main {
    public static void main(String[] args) {
        String s = "java技术大本营";
        System.out.println(s);
    }
}

我们在.class 文件目录执行 javap -v Main.class 可以看到如下图所示常量池信息:

XsXNOq

如上图画线部分的 Constant pool,就是静态常量池/class 常量池, 在下面的内容中, 我们可以看到, 和我们需要找的 java技术大本营 来对比,有两行是相关的

#2=String  #23  //java技术大本营
#23=Utf8 java技术大本营

#23 = Utf8 java技术大本营 代表 #23 这个位置存的是 Utf-8 编码的字符串

#2=String #23 //java技术大本营 代码#2 这个位置是字符串常量,字符串的值指向#23 这个位置

运行时常量池

运行时常量池是方法区的一部分,每一个类(注意不是实例)都拥有自己的运行时常量池.这里我们先看字符串常量,测试代码如下:

public class TestObject {
    private String s  = "java技术大本营";

}
public class Main {

    public static void main(String[] args) {
        TestObject t1 = new TestObject();
        TestObject t2 = new TestObject();
        System.out.println(t1);
    }
}

如上代码所示,在运行到 System.out.println(t1)时,堆里应该有两个 TestObject 实例.然后我们需要看这两个实例中的 s 是不是都指向同一个位置, 打开 HSDB 可看到如下信息:

WzhL3e

如上图所示,可以看到两个不同的实例引用的是同一个字符串.

testObject 实例存着 s 这个字符串的地址引用. s 里面存着'java 技术大本营'的 byte 的引用.s 在字符串常量池中.

各种 String 语句变量的地址

以前遇到这种问题只能各种猜,现在可以顺着静态常量池,运行时常量池一步步的看下去,运行代码如下:

public static void main(String[] args) {
    String s1 = "java技术大本营";
    String s2 = "java技术大本营";
    String s3 = "凑"+"心";
    String s4 = new String("凑心");
    String s5 = new String("Drift north");
    //true
    System.out.println(s1==s2);
    // false
    System.out.println(s3==s4);
    String s6 = s4.intern();
    // true
    System.out.println(s3==s6);
}

运行结果我已经放上面了,这里只要注意以下知识点,就不难判断出来

  • 只有 new 才往堆里面放
  • == 比较的是地址
  • intern()的作用
  • s3 = "凑"+"心"; 这个在编译的时候会直接变成"凑心"

这里我们直接通过 HSDB 来看各个字段的地址,断点先打在 String s6 = s4.intern() 这里

tS4xWH

如上图所示,我们可以得到如下 5 个地址.通过 inspect,我们可以做如下对应:

0x0000000110a197f8 // Dirft North

0x0000000110a197d8 //凑心

0x0000000110a19798 //凑心

0x0000000110a19748 //java 技术大本营

0x0000000110a19748 //java 技术大本营

这里要注意第二条和第三条,都是凑心,但是地址却不一样.常量池是肯定只有一条的,然后我们先 detach , 然后断点往下走一行,执行一下 intern()

YMtzJo

可以看到在原来的基础上,新增了一个 s6,地址是 798. 就和我们上面的第三条的地址是一样的了. 也就是执行了 intern()之后, 会看常量池有没有, 有就返回常量池的地址引用,没有就创建再返回地址引用.

总结

这一块知识点的延伸还有很多,比如创建了几个 String 对象, 比如 hsdb 里面还可以用 js 去调试等等. 比起死记硬背,还是更推荐大家这样调试着去学习!

  • JVM

    JVM(Java Virtual Machine)Java 虚拟机是一个微型操作系统,有自己的硬件构架体系,还有相应的指令系统。能够识别 Java 独特的 .class 文件(字节码),能够将这些文件中的信息读取出来,使得 Java 程序只需要生成 Java 虚拟机上的字节码后就能在不同操作系统平台上进行运行。

    180 引用 • 120 回帖 • 2 关注
  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3165 引用 • 8206 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...