您的位置:首页 > 编程语言 > Java开发

Java字节相关资料

2015-05-10 13:36 363 查看


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

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





下载LOFTER客户端

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



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




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

字节码具有以下结构特征

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

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

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

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

学过数据结构的都知道,任何物质都是有数据构成的,字节码主要有无符号数字和表构成,无符号数字使用u1、u2、u3、u4表示一个、两个、三个和四个字节,用于描述数字、索引、数值和字符串等,而表主要以无符号数和其他表组合而成的复合数据,通常 以_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等编辑软件打开即可看到,如下图所示:



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



对应的版本关系如下(注意,这个是
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的常量值等。符号引用主要包含类和接口的全限定名、字段的名称和描述符以及方法的名称和描述符等。其中常量池中每一种常量都是使用表来进行描述的。常量中的表如下图所示



其详细的结构如下图所示



常量值最基本的就是
UTF-8类型的字符串,如下图所示



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

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



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



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

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



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



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



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

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



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

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





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



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



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



对应的访问标识符如下:



结合上面的示例如下:



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




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



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



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



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



但是这些字节码被解析之后还需要哪些步骤去执行的呢?请参加下篇博文。字节码执行引擎。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: