JVM中的常量池

目录

JVM中的常量池

一、class常量池

1.1 字面量

1.2 符号引用

二、字符串常量池

三、三种创建字符串的方式(jdk1.7及以上版本)

四、字符串常量池的位置


JVM中的常量池

一、class常量池

Class常量池可以理解为是Class文件中的资源仓库。Class文件中除了包含类的版本、字段、方法、接口等信息外,还有一项信息就是常量池(constant pool table),用于存放编译器生成的各种字面量(Literal)和符号引用(Symbolic Reference)。

public class Test2 {
    public static void main(String[] args) {
       int a = 1;
       int b = 2;
       String c = "abc";
       String d = "efg";
    }
}
/**
使用javap -v Test2.class 查看字节码文件
Classfile /opt/project/myself/interView/out/production/interView/com/liheng/buffer/Test2.class
  Last modified 2021-12-21; size 802 bytes
  MD5 checksum 6b4615b4259196c2745e402728b14cf3
  Compiled from "Test2.java"
public class com.liheng.buffer.Test2
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #13.#29        // java/lang/Object."<init>":()V
   #2 = Class              #30            // java/lang/String
   #3 = String             #31            // adc
   #4 = Methodref          #2.#32         // java/lang/String."<init>":(Ljava/lang/String;)V
   #5 = Class              #33            // java/lang/StringBuilder
   #6 = Methodref          #5.#29         // java/lang/StringBuilder."<init>":()V
   #7 = Methodref          #5.#34         // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   #8 = String             #35            // d
   #9 = Methodref          #5.#36         // java/lang/StringBuilder.toString:()Ljava/lang/String;
  #10 = Fieldref           #37.#38        // java/lang/System.out:Ljava/io/PrintStream;
  #11 = Methodref          #39.#40        // java/io/PrintStream.println:(Ljava/lang/String;)V
  #12 = Class              #41            // com/liheng/buffer/Test2
  #13 = Class              #42            // java/lang/Object
  #14 = Utf8               <init>
  #15 = Utf8               ()V
  #16 = Utf8               Code
  #17 = Utf8               LineNumberTable
  #18 = Utf8               LocalVariableTable
  #19 = Utf8               this
  #20 = Utf8               Lcom/liheng/buffer/Test2;
  #21 = Utf8               main
  #22 = Utf8               ([Ljava/lang/String;)V
  #23 = Utf8               args
  #24 = Utf8               [Ljava/lang/String;
  #25 = Utf8               s
  #26 = Utf8               Ljava/lang/String;
  #27 = Utf8               SourceFile
  #28 = Utf8               Test2.java
  #29 = NameAndType        #14:#15        // "<init>":()V
  #30 = Utf8               java/lang/String
  #31 = Utf8               adc
  #32 = NameAndType        #14:#43        // "<init>":(Ljava/lang/String;)V
  #33 = Utf8               java/lang/StringBuilder
  #34 = NameAndType        #44:#45        // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  #35 = Utf8               d
  #36 = NameAndType        #46:#47        // toString:()Ljava/lang/String;
  #37 = Class              #48            // java/lang/System
  #38 = NameAndType        #49:#50        // out:Ljava/io/PrintStream;
  #39 = Class              #51            // java/io/PrintStream
  #40 = NameAndType        #52:#43        // println:(Ljava/lang/String;)V
  #41 = Utf8               com/liheng/buffer/Test2
  #42 = Utf8               java/lang/Object
  #43 = Utf8               (Ljava/lang/String;)V
  #44 = Utf8               append
  #45 = Utf8               (Ljava/lang/String;)Ljava/lang/StringBuilder;
  #46 = Utf8               toString
  #47 = Utf8               ()Ljava/lang/String;
  #48 = Utf8               java/lang/System
  #49 = Utf8               out
  #50 = Utf8               Ljava/io/PrintStream;
  #51 = Utf8               java/io/PrintStream
  #52 = Utf8               println
{
  public com.liheng.buffer.Test2();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 8: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/liheng/buffer/Test2;

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=3, locals=2, args_size=1
         0: new           #2                  // class java/lang/String
         3: dup
         4: ldc           #3                  // String adc
         6: invokespecial #4                  // Method java/lang/String."<init>":(Ljava/lang/String;)V
         9: astore_1
        10: new           #5                  // class java/lang/StringBuilder
        13: dup
        14: invokespecial #6                  // Method java/lang/StringBuilder."<init>":()V
        17: aload_1
        18: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        21: ldc           #8                  // String d
        23: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        26: invokevirtual #9                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        29: astore_1
        30: getstatic     #10                 // Field java/lang/System.out:Ljava/io/PrintStream;
        33: aload_1
        34: invokevirtual #11                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        37: return
      LineNumberTable:
        line 10: 0
        line 11: 10
        line 12: 30
        line 13: 37
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      38     0  args   [Ljava/lang/String;
           10      28     1     s   Ljava/lang/String;
}

*/

上面Constant pool中就是常量池信息,主要存放两大类:字面量和符号引用

这些常量池现在是静态信息,只有到运行时被加载到内存后,这些符号才有对应的内存地址信息,这些常量池一旦被装入内存就变成运行时常量,对应的符号引用在程序加载或者运行时会被转变为被加载内存区域的代码的直接引用,也就是动态链接了。

1.1 字面量

字面量就是指由字母、数字等构成的字符串或者数值常量。字面量只可以右值出现,所谓右值是指等号右边的值。

int a = 1 //a为左值 1为右值

1.2 符号引用

符号引用主要包括以下三类常量:

  • 类和接口的权限定名

  • 字段的名称和描述符

  • 方法的名称和描述符

int a = 1;
int b = 2;
//a和b就是一种符号引用。
 

二、字符串常量池

2.1字符串常量池的设计思想

1、字符串的分配和其他的对象一样,耗费高昂的时间与空间代价,最为最基础的数据类型,大量频繁的创建字符串,会极大的影响程序的性能。

2、JVM为了提高性能和内存开销,在实例化字符串常量的时候做了一些优化。

  • 为字符串开辟一个字符串常量池,类似于缓存区

  • 创建字符串常量时,首先查询字符串常量池是否存在该字符串

  • 存在该字符串,返回引用实例,不存在,实例化该字符串被放入池中

三、三种创建字符串的方式(jdk1.7及以上版本)

  1. 直接赋值字符串

    String s = "liheng";//s指向常量池中的引用

    这种方式创建的字符串对象,只会在常量池中。因为会有"liheng"这个字面量,所以JVM会先去常量池中通过equals(key)方法判断是否有相同的对象。

    如果有则返回字符串常量池中的引用。反之,则在常量池中创建一个新对象,并返回其引用。

  2. new String()

    这种方式创建会保证在常量池中和堆中都会有这个对象,没有就创建,最后返回堆中的引用。

    因为有"liheng"这个字面量,所以会先去检查字符串常量池中是否有这个字符串。不存在现在字符串常量池中创建一个字符串对象;再去堆内存中创建一个字符串对象"liheng"。反之,存在的话,就去对内存中创建一个字符串对象"liheng"。

    最后返回的是对内存的中的对象。

  3. intern方法

    String s = new String("liheng");

    String s1 = s.intern();

    String中的intern()方法是一个native方法。当调用intern时,如果池中已经包含了一个等于此String的字符串(equals(key)判断),则返回池中的字符串,否则,将intern返回引用指向当前字符串s。(jdk1.6需要将s复制到常量池中)。

四、字符串常量池的位置

  • jdk1.6及之前:有永久代,运行时常量池在永久代,运行时常量池包含字符串常量池。

  • jdk1.7有永久代,但是已经在去永久代,字符串常量池从永久代里的运行时常量池分离到堆里。

  • jdk1.8及以后,无永久代,运行时常量池在元空间,字符串常量池依然在堆中

 

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

半路笙歌

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值