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

本文知识点

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

  • 静态常量池

  • 运行时常量池及字符串的引用

  • 各种String语句变量的地址

参考文档:

https://docs.oracle.com/javase/specs/jvms/se11/html/jvms-5.html#jvms-5.1

静态常量池

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

 

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

 

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

JVM | 运行时常量池和字符串常量池及intern()_第1张图片

如上图画线部分的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可看到如下信息:

JVM | 运行时常量池和字符串常量池及intern()_第2张图片

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

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()这里

JVM | 运行时常量池和字符串常量池及intern()_第3张图片

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

0x0000000110a197f8 // Dirft North

0x0000000110a197d8 //凑心

0x0000000110a19798 //凑心

0x0000000110a19748 //java技术大本营

0x0000000110a19748 //java技术大本营

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

JVM | 运行时常量池和字符串常量池及intern()_第4张图片

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

你可能感兴趣的:(jvm,java,编程语言)