深入理解JVM之字节码结构

通常情况下我们都知道编写的.java文件编译成.class之后,由类加载器ClassLoader加载、链接、初始化等一系列操作。JVM加载类的过程中会产生一个Class对象来表示类的信息。通过Class可以知道类的段名字、类型、访问权限、方法等信息。那么java文件编译成class字节码是怎样一种格式呢?接下来进行分析,class 字节码包含的信息其实就是JVM定义了一系列的格式来表示字节码或者类的信息。
字节码类型
java字节码中类型分为无符号数、表两种类型
无符号数:可以表示数字、常量值、UTF-8编码的字符串,无符号数分为u1一个字节、u2两个字节、u4四个字节、u8个字节
表:是由无符号数和其他表构成的复合数据结构,都是以为_info结尾,表没有固定的长度,所以在前面加上一个容量计数器。
如下图

类型

说明

长度

u1

1个字节

1

u2

2个字节

2

u4

4个字节

4

u8

8个字节

8

cp_info

常量表

n

field_info

字段表

n

method_info

方法表

n

attribute_info

属性表

n

java字节码整体结构

名称

类型

长度

说明

magic

u4

4个字节

魔数识别class文件

minor version

u2

2个字节

副版本号

major version

u2

2个字节

主板本号

constant_pool_count

u2

2个字节

常量池计数器

constant_pool

cp_info

n个字节

常量池

access_flag

u2

2个字节

访问标识

this class

u2

2个字节

类名字索引

super class

u2

2个字节

父类名字索引

Interfaces count

u2

2个字节

接口数量

interfaces

u2

2个字节

接口信息

fields count

u2

2个字节

字段数量

fields

field_info

n个字节

字段信息

methods_count

u2

2个字节

方法数量

methods

method_info

n个字节

方法信息

attributes_count

u2

2个字节

属性数量

attributes

attribute_info

n个字节

属性信息

格式一一说明
因为字节码每一个结构中都对应很多理论概念或者字节码对照特殊含义所以一边分析字节码结构一边解释每个结构以及结构中对应的概念
案例1编写一个简单的java类源码

public class ByteCode {
    private int a = 1;

    public int getA() {
        return a;
    }
    public void setA(int a) {
        this.a = a;
    }
}

编译成字节码以后的格式

魔数:案例对应字节码CAFEBABE
所有class字节码的开头标识,所有的字节码开头4个自己都是CAFEBABE,如上图字节码前4位
副版本号、次版本号:案例中对应的字节码 00 00
java虚拟机版本后面的小版本号例如下面可以看到本机安装的版本是1.8.0 所以class中次版本号就是 00 00 转换成十进制 就是0
主版本号:案例中对应的字节码 00 34
标识java虚拟机版本的前面大版本号,所以class对应的主版本号就是 0034 转换成十进制就是 52
主板本号的对应关系是
Java SE 9 = 53,
Java SE 8 = 52,
Java SE 7 = 51,
Java SE 6.0 = 50,
Java SE 5.0 = 49,
JDK 1.4 = 48,
JDK 1.3 = 47,
JDK 1.2 = 46,
JDK 1.1 = 45

常量池:常量池里面存放的并不是常量,常量池可以看做class的资源仓库,比如类的名字索引、类的描述符、方法的名字索引、方法的描述符、字段的等信息都在常量池中
常量池有两部分构成,因为class表对应的没有固定的长度,所以在_info表前面都加上一个容量计数器。所以常量池有constant_pool_count常量池长度、constant_pool常量池表构成
常量池分为:字面量、符号引用
字面量:字符串、final修饰的常量值
符号引用:如类的名字、描述符、方法的名字、描述符、类的全限定名都是,符号引用的信息最终都是指向常量池中的字面量对应的索引
常量池类型在class中的对应关系

标识符

含义

 

B

基本数据类型byte

 

C

基本数据类型char

 

D

基本数据类型double

 

F

基本数据类型float

 

I

基本数据类型int

 

J

基本数据类型long

 

S

基本数据类型short

 

Z

基本数据类型boolean

 

V

无返回值void

 

L

对象类型Ljava/lang/String

数组[Ljava/lang/String

常量表每一项对应的都是表的概念如下图

常量池分析
constant_pool_count 对应的是 0018 =24 常量池count为24,实际上常量池元素格式为count-1即为23,常量池是从1开始,0为保留常量,用来标识符号引用中没有指向任何索引。
第1个元素: 0A 0004 0014  tag(0A)=10指向方法的详细信息,index(0004) =4表示指向声明方法的类的描述符信息对应的索引项(CONSTANT_Class_info), index(0014)=20表示方法的名称、描述符信息对应的索引项(CONSTANT_NameAndType_info)
0004索引项对应的常量池信息=java/lang/Object
0014索引项对应的常量池信息=<init> ()V

第2个元素: 09 0003  0015 tag(09)=9指向字段的详细信息,index(0003)=3表示指向声明字段的类的描述信息对应的索引项(CONSTANT_Class_info),index(0015)=21表示字段的名称、描述符信息对应的索引项(CONSTANT_NameAndType_info)
0003索引项对应的常量池信息=com/jvm/code/ByteCode
0015对应的常量项信息=a I

第3个元素:07 0016 tag(07) =7 指向类的全限定名信息,index(0016)=22表示指向类的全限定名的常量池索引(CONSTANT_Class_info)
0016对应的常量项信息为com/jvm/code/ByteCode

第4个元素:07 0017 tag(07) =7 指向类的全限定名信息,index(0017)=23表示指向类的全限定名的常量池索引(CONSTANT_Class_info)
0017对应的常量池为java/lang/Object

第5个元素:01 0001 61  tag(01) =1 UTF-8编码的字符串,length(0001)=1表示UTF-8编码的字符串的长度1,bytes(61)表示UTF-8编码长度对应的字符串,在这里常量池对应的字符串就是a(ascii)

第6个元素:01 0001 49  tag(01) =1 UTF-8编码的字符串,length(0001)=1表示UTF-8编码的字符串的长度为1,bytes(49)表示UTF-8编码长度对应的字符串 ,在这里常量池对应的字符串就是 I(ascii)

第7个元素:01 0006  3C696E69743E tag(01) =1 UTF-8编码的字符串,length(0006)=1表示UTF-8编码的字符串的长度为6,bytes(3C696E69743E)=<init>(ascii)表示UTF-8编码长度对应的字符串 ,在这里常量池对应的字符串就是 <init>

第8个元素:01 0003  282956 tag(01) =1 UTF-8编码的字符串,length(0003)表示UTF-8编码的字符串的长度为3,bytes(282956) 表示UTF-8编码长度对应的字符串 ,在这里常量池对应的字符串就是 ()V(ascii)

第9个元素:01 0004 436F6465 tag(01) =1 UTF-8编码的字符串,length(0003)表示UTF-8编码的字符串的长度为4,bytes(436F6465) 表示UTF-8编码长度对应的字符串 ,在这里常量池对应的字符串就是 code(ascii)

第10个元素:01 000F 4C696E654E756D6265725461626C65 tag(01) =1 UTF-8编码的字符串,length(000F)表示UTF-8编码的字符串的长度为15,bytes(……) 表示UTF-8编码长度对应的字符串 ,在这里常量池对应的字符串就是 LineNumberTable(ascii)

第11个元素:01 0012 4C6F63616C5661726961626C655461626C65 tag(01) =1 UTF-8编码的字符串,length(0012)表示UTF-8编码的字符串的长度为18,bytes(……) 表示UTF-8编码长度对应的字符串 ,在这里常量池对应的字符串就是 LocalVariableTable(ascii)

第12个元素:01 0004  74686973 tag(01) =1 UTF-8编码的字符串,length(0004)表示UTF-8编码的字符串的长度为4,bytes(74686973) 表示UTF-8编码长度对应的字符串 ,在这里常量池对应的字符串就是 this(ascii)

第13个元素:01 0017  4C636F6D2F6A766D2F636F64652F42797465436F64653B tag(01) =1 UTF-8编码的字符串,length(0017)表示UTF-8编码的字符串的长度为23,bytes(……) 表示UTF-8编码长度对应的字符串 ,在这里常量池对应的字符串就是 Lcom/jvm/code/ByteCode; (ascii)

第14个元素:01 0004  67657441 tag(01) =1 UTF-8编码的字符串,length(0004  )表示UTF-8编码的字符串的长度为4,bytes(67657441) 表示UTF-8编码长度对应的字符串 ,在这里常量池对应的字符串就是 getA (ascii)

第15个元素:01 0003  282949 tag(01) =1 UTF-8编码的字符串,length(0003)表示UTF-8编码的字符串的长度为3,bytes(282949) 表示UTF-8编码长度对应的字符串 ,在这里常量池对应的字符串就是 ()I (ascii)

第16个元素:01 0004  73657441tag(01) =1 UTF-8编码的字符串,length(0004)表示UTF-8编码的字符串的长度为4,bytes(73657441) 表示UTF-8编码长度对应的字符串 ,在这里常量池对应的字符串就是 setA (ascii)

第17个元素:01 0004  28492956 tag(01) =1 UTF-8编码的字符串,length(0004)表示UTF-8编码的字符串的长度为4,bytes(28492956) 表示UTF-8编码长度对应的字符串 ,在这里常量池对应的字符串就是 (I)V(ascii)

第18个元素:01  000A 536F7572636546696C65 tag(01) =1 UTF-8编码的字符串,length(000A)表示UTF-8编码的字符串的长度为10,bytes(……) 表示UTF-8编码长度对应的字符串 ,在这里常量池对应的字符串就是 SourceFile(ascii)

第19个元素:01  000D 42797465436F64652E6A617661 tag(01) =1 UTF-8编码的字符串,length(000D)表示UTF-8编码的字符串的长度为13,bytes(……) 表示UTF-8编码长度对应的字符串 ,在这里常量池对应的字符串就是 ByteCode.java(ascii)

第20个元素:)0C 0007 0008 tag(0C)=12 指向字段或者方法的索引信息,index(0007)指向方法或者字段名称的常量项索引07,index(0008)指向方法或字段的描述符常量项索引08. 07常量项目=<init> 08常量项=()V

第21个元素:)0C 0005 0006 tag(0C)=12 指向字段或者方法的索引信息,index(0007)指向方法或者字段名称的常量项索引05,index(0006)指向方法或字段的描述符常量项索引06. 07常量项目=a 08常量项=I

第22个元素:01  0015 636F6D2F6A766D2F636F64652F42797465436F6465 tag(01) =1 UTF-8编码的字符串,length(0015)表示UTF-8编码的字符串的长度为21,bytes(……) 表示UTF-8编码长度对应的字符串 ,在这里常量池对应的字符串就是 com/jvm/code/ByteCode (ascii)

第23个元素:01  0010  6A6176612F6C616E672F4F626A656374 tag(01) =1 UTF-8编码的字符串,length(0010)表示UTF-8编码的字符串的长度为16,bytes(……) 表示UTF-8编码长度对应的字符串 ,在这里常量池对应的字符串就是 java/lang/Object(ascii)

通过上面的常量池分析不难发现,最终描述的都是我们源代码中方法、字段、父类、常量等信息

访问标识access_flag 0021

访问标识是有固定的字节标识或者组合而成

通过图发现并没有0021 对应的访问标识,是因为0021有0001和0020并集得到的(0x0001|0x0020=0x0021)字节码不会穷举所有的访问修饰组合,而是通过并集得到。这里标识类是一个acc_public和acc_super。

this class

0003 类名字对应索引这里表示com/jvm/code/ByteCode

super class 

0004 父类名字对应的索引项 这里表示 java/lang/Object

interface counts 接口数 00 00

这里接口数为0,所以后面也就没有接口信息

field_count字段数量

0001 字段数数量为1

field_info字段信息

字段信息分为

访问表示access_flags 0002 对应的访问表示为private

字段名对应常量池索引项name_index 0005 a

字段访问标识符索引对应常量池索引项descriptor_index 0006 I

属性数量attributes_count 00 属性为0

属性值attributes  属性数量为0 那么对应的属性信息也就不存在

 

 

 

 

 

 

 

 

©️2020 CSDN 皮肤主题: 技术工厂 设计师:CSDN官方博客 返回首页