java的常量池


前言

java中有字节码中的常量池、运行时常量池、字符串常量池三张常量池,语雀地址:https://www.yuque.com/yangxiaofei-vquku/wmp1zm/dmz2gd

为甚要有常量池

public class Demo1 {
    public static void main(String[] args) {
        System.out.println("123");
        System.out.println(UUID.randomUUID().toString());
    }

}

一个.java文件只有几行代码,但其加载时却需要依赖很多东西,像Objec类、System、UUID、String、toString()方法、println()方法等,这些信息不可能全部编译到对应的.class文件中,也不能全部方到Demo1的元数据中,所以就引入了常量池这个概念,常量池可以看做是一张表,虚拟机指令可以根据这张表找到要执行的类、方法、属性、参数类型、返回值类、字面量型等引用信息。

字节码中的常量池

将一个.class文件用javap命令反编译后,发现生成的文件中最大的结构叫Constant pool这就是我们所说的字节码中的常量池,其主要内容是字面量(文本字符串、声明为final的常量值)、符号引用(类和接口的全限定名、字段的名称和描述符、方法的名称和描述符)。
列:

Constant pool:
   #1 = Methodref          #13.#47        // java/lang/Object."<init>":()V
   #2 = String             #48            // String
   #3 = Fieldref           #6.#49         // jvm/memory/loading/Person.a:Ljava/lang/String;
   #4 = Fieldref           #6.#50         // jvm/memory/loading/Person.b:I
   #5 = Fieldref           #6.#51         // jvm/memory/loading/Person.finalStr1:Ljava/lang/String;
   #6 = Class              #52            // jvm/memory/loading/Person
   #7 = String             #53            // final String
   #8 = Fieldref           #6.#54         // jvm/memory/loading/Person.staticStr:Ljava/lang/String;
   #9 = Fieldref           #6.#55         // jvm/memory/loading/Person.staticInt:I
  #10 = Methodref          #56.#57        // java/util/UUID.randomUUID:()Ljava/util/UUID;
  #11 = Methodref          #56.#58        // java/util/UUID.toString:()Ljava/lang/String;
  #12 = String             #59            // static String
  #13 = Class              #60            // java/lang/Object
  #14 = Utf8               finalStr1
  #15 = Utf8               Ljava/lang/String;
  #16 = Utf8               finalStr2
  #17 = Utf8               ConstantValue
  #18 = Utf8               staticStr
  #19 = Utf8               staticInt
  #20 = Utf8               I
  #21 = Utf8               a
  #22 = Utf8               b
  #23 = Utf8               <init>
  #24 = Utf8               ()V
  #25 = Utf8               Code
  #26 = Utf8               LineNumberTable
  #27 = Utf8               LocalVariableTable
  #28 = Utf8               this
  #29 = Utf8               Ljvm/memory/loading/Person;
  #30 = Utf8               getFinalStr1
  #31 = Utf8               ()Ljava/lang/String;
  #32 = Utf8               getFinalStr2
  #33 = Utf8               getStaticStr
  #34 = Utf8               setStaticStr
  #35 = Utf8               (Ljava/lang/String;)V
  #36 = Utf8               getStaticInt
  #37 = Utf8               ()I
  #38 = Utf8               setStaticInt
  #39 = Utf8               (I)V
  #40 = Utf8               getA
  #41 = Utf8               setA
  #42 = Utf8               getB
  #43 = Utf8               setB
  #44 = Utf8               <clinit>
  #45 = Utf8               SourceFile
  #46 = Utf8               Person.java
  #47 = NameAndType        #23:#24        // "<init>":()V
  #48 = Utf8               String
  #49 = NameAndType        #21:#15        // a:Ljava/lang/String;
  #50 = NameAndType        #22:#20        // b:I
  #51 = NameAndType        #14:#15        // finalStr1:Ljava/lang/String;
  #52 = Utf8               jvm/memory/loading/Person
  #53 = Utf8               final String
  #54 = NameAndType        #18:#15        // staticStr:Ljava/lang/String;
  #55 = NameAndType        #19:#20        // staticInt:I
  #56 = Class              #61            // java/util/UUID
  #57 = NameAndType        #62:#63        // randomUUID:()Ljava/util/UUID;
  #58 = NameAndType        #64:#31        // toString:()Ljava/lang/String;
  #59 = Utf8               static String
  #60 = Utf8               java/lang/Object
  #61 = Utf8               java/util/UUID
  #62 = Utf8               randomUUID
  #63 = Utf8               ()Ljava/util/UUID;
  #64 = Utf8               toString

什么是符号引用

以一组符号来表示被引用的目标,符号可以是任何新式的字面量,只要能无歧义的表示被引用目标即可,符号引用于jvm的内存布局无关,被引用的目标并不一定被加载到内存。

运行时常量池

当.class文件被加载后,字节码的各部分信息都会被解析转换池类的元数据信息保存在方法区,当然也包括字节码中的常量池(在jvm中一个类对应一个运行时常量池)。虚拟机会将字节码常量池中的符号引用转换为直接引用,并翻译到具体的内存地址中。

什么是直接引用

直接引用即直接执行引用目标的指针、相对偏移量或者一个能间接定位到目标的句柄,直接引用是与jvm的内存内存布局相关的,如果有了直接引用说明引用的目标已经被加载到内存中了,如果尚未开辟内存空间则无法获取直接引用。

字符串常量池

字符串常量池在jdk1.6是位于方法区(永久带),在jdk1.7以后移动到了堆中,字符串常量池整个jvm进程只有一份,保存了全部字符串的直接引用。运行时常量池每个类都有一份,其中在解析运行时常量池中字符串字面量的直接引用时会和全局的字符串常量池做比较保证一致。

字符串常量池为什么要移动到堆中

原先字符串常量池在方法区(永久带)中,由于方法区只有在full GC时才会被回收,full GC只有在方法区或者老年代满了时才会触发,一个系统中会用到大量字符串,继而导致字符串常量池中很多无用字符串得不到回收,浪费了方法区的空间。所以才会将字符串常量池转移到堆中,降低GC成本,提供空间利用率。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

躺平程序猿

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

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

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

打赏作者

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

抵扣说明:

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

余额充值