Class文件常量池

class文件是以字节为单位的二进制数据流,java编译器将java源码文件编译成.class字节码文件寄存在磁盘上,.class中就蕴含文件常量池(非运行时常量池),在编译期间就确定了,.class文件遵循jvm虚拟机标准.

Java源码:

package com.rumenz;public class rumenz{      public Integer id=10;      public String  name="入门";      public final int  age=100;      public void setId(Integer id){          this.id=id;      }}

查看字节码

> javac rumenz.java> javap -v rumenz
正告: 二进制文件rumenz蕴含com.rumenz.rumenzClassfile /code/rumenz.class  Last modified 2020-10-17; size 542 bytes  MD5 checksum 6a8a73fb6327c1a64e9ad54e53e94afd  Compiled from "rumenz.java"public class com.rumenz.rumenz  minor version: 0  major version: 52  flags: ACC_PUBLIC, ACC_SUPERConstant pool:   #1 = Methodref          #9.#24         // java/lang/Object."<init>":()V   #2 = Fieldref           #8.#25         // com/rumenz/rumenz.id:I   #3 = String             #26            // 入门   #4 = Fieldref           #8.#27         // com/rumenz/rumenz.name:Ljava/lang/String;   #5 = Methodref          #28.#29        // java/lang/Integer.valueOf:(I)Ljava/lang/Integer;   #6 = Fieldref           #8.#30         // com/rumenz/rumenz.age:Ljava/lang/Integer;   #7 = Methodref          #28.#31        // java/lang/Integer.intValue:()I   #8 = Class              #32            // com/rumenz/rumenz   #9 = Class              #33            // java/lang/Object  #10 = Utf8               id  #11 = Utf8               I  #12 = Utf8               name  #13 = Utf8               Ljava/lang/String;  #14 = Utf8               age  #15 = Utf8               Ljava/lang/Integer;  #16 = Utf8               <init>  #17 = Utf8               ()V  #18 = Utf8               Code  #19 = Utf8               LineNumberTable  #20 = Utf8               setId  #21 = Utf8               (Ljava/lang/Integer;)V  #22 = Utf8               SourceFile  #23 = Utf8               rumenz.java  #24 = NameAndType        #16:#17        // "<init>":()V  #25 = NameAndType        #10:#11        // id:I  #26 = Utf8               入门  #27 = NameAndType        #12:#13        // name:Ljava/lang/String;  #28 = Class              #34            // java/lang/Integer  #29 = NameAndType        #35:#36        // valueOf:(I)Ljava/lang/Integer;  #30 = NameAndType        #14:#15        // age:Ljava/lang/Integer;  #31 = NameAndType        #37:#38        // intValue:()I  #32 = Utf8               com/rumenz/rumenz  #33 = Utf8               java/lang/Object  #34 = Utf8               java/lang/Integer  #35 = Utf8               valueOf  #36 = Utf8               (I)Ljava/lang/Integer;  #37 = Utf8               intValue  #38 = Utf8               ()I{  public int id;    descriptor: I    flags: ACC_PUBLIC  public java.lang.String name;    descriptor: Ljava/lang/String;    flags: ACC_PUBLIC  public final java.lang.Integer age;    descriptor: Ljava/lang/Integer;    flags: ACC_PUBLIC, ACC_FINAL  public com.rumenz.rumenz();    descriptor: ()V    flags: 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: bipush        10         7: putfield      #2                  // Field id:I        10: aload_0        11: ldc           #3                  // String 入门        13: putfield      #4                  // Field name:Ljava/lang/String;        16: aload_0        17: bipush        100        19: invokestatic  #5                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;        22: putfield      #6                  // Field age:Ljava/lang/Integer;        25: return      LineNumberTable:        line 11: 0        line 12: 4        line 13: 10        line 14: 16  public void setId(java.lang.Integer);    descriptor: (Ljava/lang/Integer;)V    flags: ACC_PUBLIC    Code:      stack=3, locals=3, args_size=2         0: aload_0         1: iconst_3         2: aload_1         3: invokevirtual #7                  // Method java/lang/Integer.intValue:()I         6: iadd         7: putfield      #2                  // Field id:I        10: return      LineNumberTable:        line 17: 0        line 18: 10}SourceFile: "rumenz.java"

什么是常量

文本字面量

#29 = Utf8 入门

final润饰成员变量(动态变量,实例变量,局部变量)

对于根本类型public Integer id=10;常量池中只保留了他的字段描述符I和字段名称value,他们的字面量不会存在于常量池。

符号援用

符号援用次要设波及编译原理方面的概念,包含上面三类常量:
  • 类和接口的全限定名,也就是java/lang/String,将原来的.替换成/,次要用于在运行时解析失去类的间接援用。
#8 = Class              #32            // com/rumenz/rumenz
  • 字段的名称和描述符:就是类或者接口中申明的变量,包含类级别的变量和实例级的变量
#14 = Utf8               age
  • 办法中的名称和描述符:参数类型+返回值
 #20 = Utf8              setId

运行时常量池

运行时常量池是办法区的一部分,所以也是全局共享的,JVM在执行某个类的时候会通过加载,链接(验证,筹备,解析),初始化,在加载的时候须要做:
  • 通过一个类的全类限定名获取此类的二进制字节流。
  • 在堆内存生成一个java.lang.Class对象,代表加载这个类,做为这个类的入口。
一般对象类对象的区别:一般对象是通过new创立进去的。类对象是JVM创立的单例对象。
  • 将字节流的动态存储构造转化成办法区的运行时数据结构
class文件常量池进入运行时常量池,所有类独特应用一个运行时常量池,在进入运行时常量的过程中,多个class常量池中雷同的字符串,只会在运行时常量池存在一份,这是一种优化。

运行时常量池的作用是存储class文件常量池中的符号援用,同时运行时常量池保留着class文件中形容的符号援用,在类的解析阶段会把这些符号援用转换成间接援用(实例对象的内存地址),翻译进去的间接援用也是存储在运行时常量池中。class文件常量池的大部分数据会被加载到运行时常量池。

运行时常量池相比于class文件常量池具备动态性,运行时常量池的内容不全副来自于class文件常量池,能够通过代码生成的形式退出到外面。如String.intern。

String.intern()的用法

拿String的内容去Stringtable查找,则返回援用。如果不存在就把该对象的援用存在Stringtable中。

字符串常量池

1.字符串创立的两种形式

  • String rumenz1="入门";
  • String rumenz2=new String("小站");

入门在编译期间就曾经确定,会进入字符串常量池,然而字符串常量池只会保留一个援用,最终还是会在堆上创立一个入门对象。new String这种形式调用了String类的构造函数,new是创立一个对象实例并初始化该实例,因而这个字符串对象是在运行时能力确定的,创立的实例在堆上。

2.字符串常量的实质

字符串常量池是JVM保护的一个字符串实例援用表,在HotSpot VM中它是叫做一个StringTable的全局表。在字符串常量池中保护的是字符串实例的援用,底层C++保护的就是一个Hashtable。这些被保护援用的字符串实例,被称作被驻留字符串interned string进入字符串常量池的字符串