Java常量池

常量池/Class文件常量池

每一个Java类都会编译成一个Class文件。Class文件中会包含一个常量池。如下。常量池中包含:

  1. 字面量:类的全路径字面值、字段名、字段字面值、方法名、方法入参
  2. 符号引用:Methodref、Fieldref、Class等。
public class StudyClassFile {
	  //出现在常量池中
    static final int staticInt = 10;
		//出现在常量池中
    static final String  staticString = "abd";
		//出现在常量池中
    static final  String staticFinalString = "staticFinalStringDEF";
		//出现在常量池中
    final  String finalString = "finalStringHHH";

    public static void main(String[] args) {

    }
		//class中的常量池:Methodref、Fieldref、Class、utf-8(类的全路径名、类名、方法名、实际字面量的值、字段名、方法入参路径)

}
> javap -c -verbose StudyClassFile
Classfile 
.../com/lim/study/vm/metaspace/StudyClassFile.class
  Last modified 2021年3月14日; size 546 bytes
  MD5 checksum 66cdef11d40e4bd269566c4ce2d4f559
  Compiled from "StudyClassFile.java"
public class com.lim.study.vm.metaspace.StudyClassFile
  minor version: 0
  major version: 55
  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
  this_class: #4                          // com/lim/study/vm/metaspace/StudyClassFile
  super_class: #5                         // java/lang/Object
  interfaces: 0, fields: 4, methods: 2, attributes: 1
Constant pool:
   #1 = Methodref          #5.#24         // java/lang/Object."<init>":()V
   #2 = String             #25            // finalStringHHH
   #3 = Fieldref           #4.#26         // com/lim/study/vm/metaspace/StudyClassFile.finalString:Ljava/lang/String;
   #4 = Class              #27            // com/lim/study/vm/metaspace/StudyClassFile
   #5 = Class              #28            // java/lang/Object
   #6 = Utf8               staticInt
   #7 = Utf8               I
   #8 = Utf8               ConstantValue
   #9 = Integer            10
  #10 = Utf8               staticString
  #11 = Utf8               Ljava/lang/String;
  #12 = String             #29            // abd
  #13 = Utf8               staticFinalString
  #14 = String             #30            // staticFinalStringDEF
  #15 = Utf8               finalString
  #16 = Utf8               <init>
  #17 = Utf8               ()V
  #18 = Utf8               Code
  #19 = Utf8               LineNumberTable
  #20 = Utf8               main
  #21 = Utf8               ([Ljava/lang/String;)V
  #22 = Utf8               SourceFile
  #23 = Utf8               StudyClassFile.java
  #24 = NameAndType        #16:#17        // "<init>":()V
  #25 = Utf8               finalStringHHH
  #26 = NameAndType        #15:#11        // finalString:Ljava/lang/String;
  #27 = Utf8               com/lim/study/vm/metaspace/StudyClassFile
  #28 = Utf8               java/lang/Object
  #29 = Utf8               abd
  #30 = Utf8               staticFinalStringDEF
{
  static final int staticInt;
    descriptor: I
    flags: (0x0018) ACC_STATIC, ACC_FINAL
    ConstantValue: int 10

  static final java.lang.String staticString;
    descriptor: Ljava/lang/String;
    flags: (0x0018) ACC_STATIC, ACC_FINAL
    ConstantValue: String abd

  static final java.lang.String staticFinalString;
    descriptor: Ljava/lang/String;
    flags: (0x0018) ACC_STATIC, ACC_FINAL
    ConstantValue: String staticFinalStringDEF

  final java.lang.String finalString;
    descriptor: Ljava/lang/String;
    flags: (0x0010) ACC_FINAL
    ConstantValue: String finalStringHHH

  public com.lim.study.vm.metaspace.StudyClassFile();
    descriptor: ()V
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=2, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: aload_0
         5: ldc           #2                  // String finalStringHHH
         7: putfield      #3                  // Field finalString:Ljava/lang/String;
        10: return
      LineNumberTable:
        line 8: 0
        line 12: 4

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
    Code:
      stack=0, locals=1, args_size=1
         0: return
      LineNumberTable:
        line 18: 0
}
SourceFile: "StudyClassFile.java"

运行时常量池

JVM在读取Class文件的时候,会为每个类创建instanceKlass来记录类的元数据。每个instanceKlass上引用着一个constantPoolOopDesc对象,然后间接引用着一个constantPoolCacheOopDesc对象。前者跟Class文件里记录的常量池的结构类似,而后者是为了让解释器运行得更高效的一个缓存。

参考:请问,jvm实现读取class文件常量池信息是怎样呢? - 讨论 - 高级语言虚拟机 - ITeye群组

instanceKlass 中有一个_contants字段,引用constantPoolOopDesc对象。在这个对象中,各个常量的值是混在一起的,基本上和Class文件一样。只不过,在instanceKlass 之间,符号是共享的,但是Class文件不是。

uft8常量在运行时常量池constantPoolOopDesc中已symbolOopDesc来体现。常量池中只包含了对symbol对象的引用,并没有实际存储。而是通过全局的常量表来体现【全局字符串池】。

另外,运行时常量池中可能会存在UnresolvedClass。正是动态类加载/链接的一个表现。 当这个引用的类没有被该类使用过时,就没有被链接【符号引用】。当被使用过了之后,运行时常量池中的这个外部类的应用会变成一个【直接引用】。

符号引用和直接引用

符号引用通常是用来设计在字符串上的,用文本形式来表示引用关系。而直接引用是JVM所能直接使用的形式。既可以表现为直接指针,也可以是其他形式。

符号引用在进行方法调用的时候,还需要解析符号引用所执行的代码,之后,才能执行。

JVM里的符号引用如何存储?

全局字符串值

字符串的内容是在类的加载完成,准备阶段之后,在堆内生成的字符串的实例对象。然后将对象的引用存储到string pool中。

在HotSpot VM 中string pool 是由StringTable实现的,里面存的是引用。存在里面的对象,表示拥有“驻留字符串”的身份。StringTable在VM中的实例只有一份,被所有的类共享。

举例说明

String str1 = "abc";
String str2 = new String("def");
String str3 = "abc";
String str4 = str2.intern();
String str5 = "def";
System.out.println(str1 == str3);//true
System.out.println(str2 == str4);//false
System.out.println(str4 == str5);//true

其中: string1和string3的"abc"值应该在class文件常量池中就是一个String类型。

 

 

加载到运行时常量池后,在未解析之前,str1,str3指向了两个符号引用。解析时,会去查询全局字符串。会查到同一个对象,所以str1==str3

str2会因为new操作符,导致创建一个新的对象实例,和之前加载到全局字符池中的不是一个。因此str2≠str5≠str4

intern()函数的作用是:如果全局字符池中没有这个字符串的引用,则将其加入进去。如果有,则返回全局字符池中的引用。因此str4==str5

参考:Java中几种常量池的区分

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值