Jvm - 的几种常量池

一.Class 文件常量池

1.class 文件中存在常量池,其在编译阶段就已经确定了。

2.用javap -v 命令查看编译后的文件:javap -v ClassConstantPool.class

3.class文件常量池主要存放两大常量:字面量和符号引用

1.查看class字节码的具体方法

先创建一个这样的类

public class ClassConstantPool {
private int value = 1;
public String s = "abc";
public final static int f = 0x101;
public void setValue(int v) {
final int temp = 3;
this.value = temp + v;
}
public int getValue() {
return value;
}
}

点击这个锤子编译

完成后,在javac 下找到编译后的这个类的上一级目录,并点击Terminal

 随后,输入命令 javap -v ClassConstantPool.class 就可以了。

public class ClassConstantPool
  minor version: 0
  major version: 52
  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
  this_class: #5                          // ClassConstantPool
  super_class: #6                         // java/lang/Object
  interfaces: 0, fields: 3, methods: 3, attributes: 1
Constant pool:
   #1 = Methodref          #6.#29         // java/lang/Object."<init>":()V
   #2 = Fieldref           #5.#30         // ClassConstantPool.value:I
   #3 = String             #31            // abc
   #4 = Fieldref           #5.#32         // ClassConstantPool.s:Ljava/lang/String;
   #5 = Class              #33            // ClassConstantPool
   #6 = Class              #34            // java/lang/Object
   #7 = Utf8               value
   #8 = Utf8               I
   #9 = Utf8               s
  #10 = Utf8               Ljava/lang/String;
  #11 = Utf8               f
  #12 = Utf8               ConstantValue
  #13 = Integer            257
  #14 = Utf8               <init>
  #15 = Utf8               ()V
  #16 = Utf8               Code
  #17 = Utf8               LineNumberTable
  #18 = Utf8               LocalVariableTable
  #19 = Utf8               this
  #20 = Utf8               LClassConstantPool;
  #21 = Utf8               setValue
  #22 = Utf8               (I)V
  #23 = Utf8               v
  #24 = Utf8               temp
  #25 = Utf8               getValue
  #26 = Utf8               ()I
  #27 = Utf8               SourceFile
  #28 = Utf8               ClassConstantPool.java
  #29 = NameAndType        #14:#15        // "<init>":()V
  #30 = NameAndType        #7:#8          // value:I
  #31 = Utf8               abc
  #32 = NameAndType        #9:#10         // s:Ljava/lang/String;
  #33 = Utf8               ClassConstantPool
  #34 = Utf8               java/lang/Object
{
  public java.lang.String s;
    descriptor: Ljava/lang/String;
    flags: (0x0001) ACC_PUBLIC

  public static final int f;
    descriptor: I
    flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
    ConstantValue: int 257

  public ClassConstantPool();
    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: iconst_1
         6: putfield      #2                  // Field value:I
         9: aload_0
        10: ldc           #3                  // String abc
        12: putfield      #4                  // Field s:Ljava/lang/String;
        15: return
      LineNumberTable:
        line 4: 0
        line 5: 4
        line 6: 9
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      16     0  this   LClassConstantPool;

  public void setValue(int);
    descriptor: (I)V
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=3, locals=3, args_size=2
         0: iconst_3
         1: istore_2
         2: aload_0
         3: iconst_3
         4: iload_1
         5: iadd
         6: putfield      #2                  // Field value:I
         9: return
      LineNumberTable:
        line 9: 0
        line 10: 2
        line 11: 9
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      10     0  this   LClassConstantPool;
            0      10     1     v   I
            2       8     2  temp   I

  public int getValue();
    descriptor: ()I
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: getfield      #2                  // Field value:I
         4: ireturn
      LineNumberTable:
        line 13: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   LClassConstantPool;
}
SourceFile: "ClassConstantPool.java"

这就是反编后的字节码。

字节码解释地址https://cloud.tencent.com/developer/article/1333540

2.字面量

字面量主要包括:

文本字符串,也就是我们经常声明的:public String s = "abc";中的"abc"

#9 = Utf8           s
#3 = String         #31 // abc
#31 = Utf8          abc

用final修饰的成员变量,包括静态变量、实例变量和局部变量

#11 = Utf8           f
#12 = Utf8           ConstantValue
#13 = Integer        257

存在于常量池的字面量,指的是数据的值,也就是abc和0x101(257),通过对常量池的观察可知这两个字 面量是确实存在于常量池中。

而对于基本类型数据(甚至是方法中的局部变量),也就是上面的private int value = 1;常量池中只保留了 他的的字段描述符I和字段的名称value,他们的字面量不会存在于常量池。

3.符号引用

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

类和接口的全限定名,也就是Ljava/lang/String;这样,将类名中原来的"."替换为"/"得到的,主要用于在 运行时解析得到类的直接引用,像上面:

#5 = Class            #33 // com/dongnaoedu/jvmstudylib/JavaBean
#33 = Utf8            JavaBasicKnowledge/JavaBean

字段的名称和描述符,字段也就是类或者接口中声明的变量,包括类级别变量(static)和实例级的变量

#4 = Fieldref #5.#32 // com/dongnaoedu/jvmstudylib/JavaBean.value:I
#5 = Class #33 // com/dongnaoedu/jvmstudylib/JavaBean
#32 = NameAndType #7:#8 // value:I
#7 = Utf8 value
#8 = Utf8 I
//这两个是局部变量,只保留字段名称
#23 = Utf8 v
#24 = Utf8 temp

方法的名称和描述符,方法的描述类似于JNI动态注册时的“方法签名”,也就是参数类型+返回值类型

#21 = Utf8 setValue
#22 = Utf8 (I)V
#25 = Utf8 getValue
#26 = Utf8 ()I

二.运行时常量池

所谓的运行时常量池其实就是将编译后的类信息放入运行时的一个区域中,用来动态获取类信息。 运行时 常量池是在类加载完成之后,将每个class常量池中的符号引用值转存到运行时常量池中,也就是说,每个 class都有一个运行时常量池,类在解析之后,将符号引用替换成直接引用,与全局常量池中的引用值保持 一致。

三.字符串常量池

字符串池里的内容是在类加载完成,经过验证,准备阶段之后在堆中生成字符串对象实例,然后将该字符 串对象实例的引用值存到string pool中(string pool中存的是引用值而不是具体的实例对象,具体的实例 对象是在堆中开辟的一块空间存放的)。 在HotSpot VM里实现的string pool功能的是一个StringTable 类,它是一个哈希表,里面存的是驻留字符串(也就是我们常说的用双引号括起来的)的引用(而不是驻留 字符串实例本身),也就是说在堆中的某些字符串实例被这个StringTable引用之后就等同被赋予了”驻 留字符串”的身份。这个StringTable在每个HotSpot VM的实例只有一份,被所有的类共享。

String s1 = "Hello";
String s2 = "Hello";
String s3 = "Hel" + "lo";
String s4 = "Hel" + new String("lo");
String s5 = new String("Hello");
String s7 = "H";
String s8 = "ello";
String s9 = s7 + s8;


System.out.println(s1 == s2); // true
System.out.println(s1 == s3); // true
System.out.println(s1 == s4); // false
System.out.println(s1 == s9); // false

intern方法

如果字符串常量池里存在一个和当前字符串对象等价的字符串对象(equals==true认为相同),那么返回字符串常量池里那个对象.如果不存在,把当前字符串对象存进常量池, 返回当前字符串对象。

String test1 = "abc";
String test2 = new String("abc");
String str = test2.intern();

字符串引用str,如果直接用str=test2,那么就会如图中蓝线所示,指向不在常量池中的字符串对象

如果使用上面的str=test2.intern(),则会如同红线所示(因为常量池里的字符串对象和test2是等价的, intern会返回常量池里的那个

不在常量池中的字符串对象"str"在没有其他引用的情况下,可以进行回收,这是intern()真正的好处

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值