JVM-解析常量池

Posted HarryHook

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JVM-解析常量池相关的知识,希望对你有一定的参考价值。

  Java最显著的特点就是"Write Once, Run Anywhere", 这全是因为虚拟机JVM的存在,使得Java代码的运行可以不受操作系统的限制。不论是Java语言的代码还是其他语言的代码,最终都可以编译成字节码.Class文件,虚拟机并不关心文件来自什么语言,只要符合Class文件的格式,可以在虚拟机中运行就行。

  Class文件中只要两种数据机构:无符号数无符号数u1,u2,u4,u8分别代表1个字节,2个字节,4个字节和8个字节;是由多个无符号数或其他表构成的复合数据类型,和C语言中的结构体类似。整个Class文件就是由以下的数据项构成的:

类型 名称 数量
u4 magic 1
u2 minor_version 1
u2 major_version 1
u2 constant_pool_count 1
cp_info constant_pool constant_pool_count - 1
u2 access_flags 1
u2 this_class 1
u2 super_class 1
u2 interfaces_count 1
u2 interfaces interfaces_count
u2 fields_count 1
field_info fields fields_count
u2 methods_count 1
method_info methods methods_count
u2 attribute_count 1
attribute_info attributes attributes_count

下面是十六进制编辑器wxmedit打开的一个Class文件

  前四个字节CA FE BA BE 称为魔数(Magic Number)对应u4类型的magic,魔数是用来判定当前的文件是否为Class文件

  第五和第六个字节 00 00 代表次版本号(Minor Version),第七和第八个字节00 34代表主版本号(Major Version)52,对应JDK52.0

  主版本号之后就是常量池(Constant Pool)的入口,是占用Class文件最大的项目,常量池的大小是不固定的,所以在常量池入口处首先看到的是常量池的计数值(constant_pool_count)00 36 ,但常量池的容量不是从0而是从1开始计数的,所以对应常量池有54-1=53个常量项

  常量池中主要存放两大类常量:字面量和符号引用;字面量好比Java中的文本字符串和被声明的final常量,符号引用主要包括了三类常量:类和接口的全限定名,字段的名称和描述符,方法的名称和描述符

  之所以存在常量池是因为Java代码在编译的时候没有"连接" 

  C 语言: 编译->链接->.exe->执行
    函数A调用函数B,在链接时会直接在函数A中记录函数B的地址。
  Java : 编译->.class ->装载执行
     类 Employee 中使用了另外一个类 Department,在Employee.class 中只保存类Department的名称, 而不会保留类Department的“地址”。虚拟机运行的时候需要从常量池获得对应的符号引用,在类创建的时候翻译并解析到具体的内存之中。

    下面是常量池中包含的项目类型

 

常量池中数据项类型 类型标志 类型描述
CONSTANT_Utf8 1 UTF-8编码的Unicode字符串
CONSTANT_Integer 3 int类型字面值
CONSTANT_Float 4 float类型字面值
CONSTANT_Long 5 long类型字面值
CONSTANT_Double 6 double类型字面值
CONSTANT_Class 7 对一个类或接口的符号引用
CONSTANT_String 8 String类型字面值
CONSTANT_Fieldref 9 对一个字段的符号引用
CONSTANT_Methodref 10 对一个类中声明的方法的符号引用
CONSTANT_InterfaceMethodref 11 对一个接口中声明的方法的符号引用
CONSTANT_NameAndType 12 对一个字段或方法的部分符号引用

 

 

 

   

 

 

 

 

 

 

 

 

 

 

 

常量池的第一项常量,标志位tag =  07,对应 CONSTANT_Class 类型

 

类型 名称 数量
u1 tag 1
u2 name_index 1

 

 

   

 

 

 

 标志位之后是name_index = 00 02,指向常量池中的第二项, tag = 01,表示CONSTANT_Utf8

 

类型 名称 数量
u1 tag 1
u2 length 1
u1 bytes length

  

 

 

 

 

第二项常量中 00 33 十进制表示51,即length=51,即此UTF8编码的字符串长度是51个字节

 

 

  第三项常量tag = 07, 可知是CONSTANT_Class 类型 ,name_index = 00 04, 指向第四项常量

  第四项常量tag = 01, 可知是CONSTANT_Utf8类型,length = 16 (00 10)转换而来

  其他项的解析和之前的类似,对照具体的常量的结构来分析

 

    常量

 项目     

  类型  

描述

 

CONSTANT_Utf8

tag

u1

值为1

length

u2

UF-8编码的字符串占用的字节数

bytes

u1

长度为length的UTF-8编码的字符串

 

CONSTANT_Integer

tag

u1

值为3

bytes

u4

按照高位在前存储的int值

 

CONSTANT_Float

tag

u1

值为4

bytes

u4

按照高位在前存储的float值

 

CONSTANT_Long

tag

u1

值为5

bytes

u8

按照高位在前存储的long值

 

CONSTANT_Double

tag

u1

值为6

bytes

u8

按照高位在前存储的double值

 

CONSTANT_Class

tag

u1

值为7

index

u2

指向全限定名常量项的索引

 

CONSTANT_String

tag

u1

值为8

index

u2

指向字符串字面量的索引

 

CONSTANT_Fieldref

tag

u1

值为9

index

u2

指向声明字段的类或接口描述符CONSTANT_Class_info的索引项

index

u2

指向字段名称及类型描述符CONSTANT_NameAndType_info的索引项

 

CONSTANT_Methodref

tag

u1

值为10

index

u2

指向声明方法的类描述符CONSTANT_Class_info的索引项

index

u2

指向方法名称及类型描述符CONSTANT_NameAndType_info的索引项

 

CONSTANT_InrerfaceMethodref

tag

u1

值为11

index

u2

指向声明方法的接口描述符CONSTANT_Class_info的索引项

index

u2

指向方法名称及类型描述符CONSTANT_NameAndType_info的索引项

 

CONSTANT_NameAndType

tag

u1

值为12

index

u2

指向字段或方法名称常量项目的索引

index

u2

指向该字段或方法描述符常量项的索引

以上是关于JVM-解析常量池的主要内容,如果未能解决你的问题,请参考以下文章

[jvm解析系列][六]class里的常量池,访问标志,类的继承关系,如何把一个类在字节码中描述清楚?

20道BAT面试官最喜欢问的JVM+MySQL面试题(含答案解析)

JVM-class文件完全解析-属性表集合

JVM-class文件完全解析-方法表集合

JVM-class文件完全解析-字段表集合

常量池