Java字节相关资料

来源:互联网 发布:彩票关注自动软件 编辑:程序博客网 时间:2024/05/16 19:26

深入理解JVM—字节码文件子系统  

2012-03-20 15:48:42|  分类: JVM |  标签:jvm  字节码  子系统  深入理解  class  |举报|字号 订阅

下载LOFTER客户端

    我们在前面的几篇文章已经提到过很多次,Java是一种平台无关性的语言,而平台无关性主要是因为有了中间代码字节码。其运行流程如下所示:

深入理解JVM—字节码文件子系统 - 一线天色 天宇星辰 - 一线天色 天宇星辰

 同样的,我们的

JVM有着更强大的功能,平台无关性和语言无关性,如下图所示,无论什么语言,只要编译后是符合Java虚拟机规范的字节码文件,JVM便可以正常运行。

深入理解JVM—字节码文件子系统 - 一线天色 天宇星辰 - 一线天色 天宇星辰

 那

Class文件究竟是什么样子的一个格式,什么一个结构的呢?下面让我们一起来解开它的面纱。

字节码具有以下结构特征

1、    Class文件是以8bit为基础单位的二进制数据流,也就是我们的一个Byte

2、    字节码数据严密紧凑,无分隔符,这一点是效仿当初的C语言。

3、    字节码只存在无符号数字和表两周数据类型

4、    所有的字节码数据采用UTF-8字符编码

学过数据结构的都知道,任何物质都是有数据构成的,字节码主要有无符号数字和表构成,无符号数字使用u1u2u3u4表示一个、两个、三个和四个字节,用于描述数字、索引、数值和字符串等,而表主要以无符号数和其他表组合而成的复合数据,通常 _info结尾。

那字节码为减是如何生成的呢?这一点我在上一篇博文中已经写的很详细了,大家可以参考上篇博文。

类在加载前必须先解析字节码,字节码主要由以下文件格式构成

名称

类型

数量

magic

u4

1

minor_version

u2

1

major_version

u2

1

constant_pool_count

u2

1

constant_pool

cp_info

constant_pool_count-1

access_flags

u2

1

this_class

u2

1

super_class

u2

1

interfaces_count

u2

1

interfaces

u2

interfaces_count

fields_count

u2

1

fields

field_info

fields_count

methods_count

u2

1

methods

method_info

methods_count

attributes_count

u2

1

attributes

attribute_info

attributes_count

所有的字节码文件都以0xCAFEBABE打头,这个被成为是魔数。当年Java刚出来的时候因为咖啡比较火,开发JVM的人员就希望Java有一天和咖啡一样流行,就在字节码的的开头打上了cafe的关键字,并把Java的logo都设计成了咖啡的样子。其实很多文件都是有一个固定的开头的,如jpeg的文件的打头字节码为0xFFD8FFE0。我们使用UE等编辑软件打开即可看到,如下图所示:

深入理解JVM—字节码文件子系统 - 一线天色 天宇星辰 - 一线天色 天宇星辰

 接下分别是编译这段字节码的编译器的小版本和大版本,如下图所示:

深入理解JVM—字节码文件子系统 - 一线天色 天宇星辰 - 一线天色 天宇星辰

 对应的版本关系如下(注意,这个是

16进制的哈)

JDK 编译器版本

target 参数

十六进制minor.major

十进制minor.major

jdk1.1.8

不能带 target 参数

00 03 00 2D

45.3

jdk1.2.2

不带(默认为 -target 1.1)

00 03 00 2D

45.3

jdk1.2.2

-target 1.2

00 00 00 2E

46.0

jdk1.3.1_19

不带(默认为 -target 1.1)

00 03 00 2D

45.3

jdk1.3.1_19

-target 1.3

00 00 00 2F

47.0

j2sdk1.4.2_10

不带(默认为 -target 1.2)

00 00 00 2E

46.0

j2sdk1.4.2_10

-target 1.4

00 00 00 30

48.0

jdk1.5.0_11

不带(默认为 -target 1.5)

00 00 00 31

49.0

jdk1.5.0_11

-target 1.4 -source 1.4

00 00 00 30

48.0

jdk1.6.0_01

不带(默认为 -target 1.6)

00 00 00 32

50.0

jdk1.6.0_01

-target 1.5

00 00 00 31

49.0

jdk1.6.0_01

-target 1.4 -source 1.4

00 00 00 30

48.0

jdk1.7.0

不带(默认为 -target 1.6)

00 00 00 32

50.0

jdk1.7.0

-target 1.7

00 00 00 33

51.0

jdk1.7.0

-target 1.4 -source 1.4

00 00 00 30

48.0

这里我们可以看到有一个target参数,如我们看最后一行JDK1.7,但是他的target只有1.4,大版本只有48,这个表示虽然是由JDK1.7编译的,但是需要编译成兼容JDK1.4的版本,生成的字节码是可以在JRE1.4的环境上运行的。

跟着大小编译器版本后面的就是我们的常量池。常量池主要有字面常量和符号引用两种组成。字面常量主要包括文本常量,被声明为final的常量值等。符号引用主要包含类和接口的全限定名、字段的名称和描述符以及方法的名称和描述符等。其中常量池中每一种常量都是使用表来进行描述的。常量中的表如下图所示

深入理解JVM—字节码文件子系统 - 一线天色 天宇星辰 - 一线天色 天宇星辰

 其详细的结构如下图所示

深入理解JVM—字节码文件子系统 - 一线天色 天宇星辰 - 一线天色 天宇星辰

 常量值最基本的就是

UTF-8类型的字符串,如下图所示

深入理解JVM—字节码文件子系统 - 一线天色 天宇星辰 - 一线天色 天宇星辰

 01

表示UTF-8后面跟的是他的内容。我们可以通过javap –verbose ${ClassName}来查看。

紧接着的两个字节是访问标识符,如下表所示

深入理解JVM—字节码文件子系统 - 一线天色 天宇星辰 - 一线天色 天宇星辰

 我们来看一个实例,刚才那段代码对应的访问标识符如下:

深入理解JVM—字节码文件子系统 - 一线天色 天宇星辰 - 一线天色 天宇星辰

 对照表格我们可以清楚的看到对应的访问标识符是

public的。

下面紧接着的是对应的类索引、父类索引和接口索引集。如图所示:

深入理解JVM—字节码文件子系统 - 一线天色 天宇星辰 - 一线天色 天宇星辰

 这里

0x07属于类结构,对应的索引为0x11,为第17个常量,对应的数据如下图所示

深入理解JVM—字节码文件子系统 - 一线天色 天宇星辰 - 一线天色 天宇星辰

 这里我们就可以看待对应的类名了,是一个

UTF-8类型的字符串。同样的,我们可以查找对应的父类和接口索引。我们也可以同javap –verbose ${ClassName}来查看,命令运行的结构如下图所示:

深入理解JVM—字节码文件子系统 - 一线天色 天宇星辰 - 一线天色 天宇星辰

 我们可以清晰的看到对应的常量和关系。

紧接着下面的数据是字段表,他的结构如下所示

深入理解JVM—字节码文件子系统 - 一线天色 天宇星辰 - 一线天色 天宇星辰

 他有一个访问标识符,字段名称索引,描述索引和属性表。属性表我们稍后在讨论,现在我们来了解以下敌营的描述符。

描述符是通过一个简单的符号来代替一段符号的描述,如Int就是用一个I表示,数组就使用[表示,下面是一些基本的例子。

深入理解JVM—字节码文件子系统 - 一线天色 天宇星辰 - 一线天色 天宇星辰
 深入理解JVM—字节码文件子系统 - 一线天色 天宇星辰 - 一线天色 天宇星辰

 我们前面已经提到了一个简单名称、全限定名和描述符,那这几个有啥区别的呢?下面用一个例子来展示以下

深入理解JVM—字节码文件子系统 - 一线天色 天宇星辰 - 一线天色 天宇星辰

 不用多说,大家一看就明白了吧!具体到我们之前的那段代码示例,如下图所示

深入理解JVM—字节码文件子系统 - 一线天色 天宇星辰 - 一线天色 天宇星辰

 接下来的是方法表,方法表和字段表很像,结构如下图所示

深入理解JVM—字节码文件子系统 - 一线天色 天宇星辰 - 一线天色 天宇星辰

 对应的访问标识符如下:

深入理解JVM—字节码文件子系统 - 一线天色 天宇星辰 - 一线天色 天宇星辰

 结合上面的示例如下:

深入理解JVM—字节码文件子系统 - 一线天色 天宇星辰 - 一线天色 天宇星辰

 适应

javap可以很清楚的看到方法表的内容

深入理解JVM—字节码文件子系统 - 一线天色 天宇星辰 - 一线天色 天宇星辰

 在

Java里面,我们没定义一个方法,JVM在运行时知道我们要调用哪些方法,那这些是通过什么来区分的呢?有人会说,肯定通过方法名了,那方法名相同的怎么办?也许你继续会说,方法名相同,还有参数不同哇?那返回值 不同行不行?我们很早已经知道答案,方法名相同参数不同的函数写法被称为是重载,在Java语言中,重载一个方法除了需要和原方法相同的方法名之外,还需要和原方法有不同的特征签名。而这个特征签名包括顺序的签名类型的集合,并不包含返回值类型,因此不同的返回值类型不能重载,但是我们可以定义返回类型不同、名称相同、签名不同的方法,如下图代码所示。

深入理解JVM—字节码文件子系统 - 一线天色 天宇星辰 - 一线天色 天宇星辰

 紧接着方法表,接下来的就是属性表,属性表主要包含以下信息

深入理解JVM—字节码文件子系统 - 一线天色 天宇星辰 - 一线天色 天宇星辰

 我们使用

javap命令可以很详细的看到对应的结果。

深入理解JVM—字节码文件子系统 - 一线天色 天宇星辰 - 一线天色 天宇星辰

 JDK

1.5以后加入了 不少新的属性表,下表展示了JDK1.5 1.6新加入的属性表。

深入理解JVM—字节码文件子系统 - 一线天色 天宇星辰 - 一线天色 天宇星辰

 但是这些字节码被解析之后还需要哪些步骤去执行的呢?请参加下篇博文。字节码执行引擎。

0 0
原创粉丝点击