JVMAndroid知识Java 杂谈

(六)-class文件结构

2017-12-19  本文已影响18人  紫霞等了至尊宝五百年

1 什么是JVM的“无关性”?

Java具有平台无关性,也就是任何操作系统都能运行Java代码.之所以能实现这一点,是因为Java运行在虚拟机之上,不同的操作系统都拥有各自的Java虚拟机,因此Java能实现"一次编写,处处运行".

而JVM不仅具有平台无关性,还具有语言无关性.

这听起来非常惊人,但JVM对能运行的语言是有严格要求的.首先来了解下Java代码的运行过程.

Java源代码首先需要使用Javac编译器编译成class文件,然后启动JVM执行class文件,从而程序开始运行.
也就是JVM只认识class文件,它并不管何种语言生成了class文件,只要class文件符合JVM的规范就能运行.
因此目前已经有Scala、JRuby、Jython等语言能够在JVM上运行.它们有各自的语法规则,不过它们的编译器都能将各自的源码编译成符合JVM规范的class文件,从而能够借助JVM运行它们.


这里写图片描述

2 纵观Class文件结构

class文件是一组以8位字节为基础单位的二进制流,它的内容具有严格的规范,文件中没有任何分隔符,全是连续的0/1.
class文件中的所有内容被分为两种类型:无符号数 和 表。

2.1 魔数(Magic Number)

class文件的头4个字节称为魔数,唯一作用是确定这个文件是否为一个能被JVM接受的Class文件.
作用就相当于文件后缀名,只不过后缀名容易被修改,不安全.
是用16进制表示的"CAFEBABE".

2.2 版本信息

紧接着魔数的4个字节是版本号.它表示本class中使用的是哪个版本的JDK.
在高版本的JVM上能够运行低版本的class文件,但在低版本的JVM上无法运行高版本的class文件.

2.3 常量池

2.3.1 什么是常量池?

紧接着版本号之后的就是常量池.常量池中存放两种类型的常量:

2.3.2 常量池的特点

2.3.3 常量池中常量的类型

刚才介绍了,常量池中的常量大体上分为:字面量 和 符号引用.在此基础上,根据常量的数据类型不同,又可以被细分为14种常量类型.这14种常量类型都有各自的二维表示结构.每种常量类型的头1个字节都是tag,表示当前常量属于14种类型中的哪一个.

这里写图片描述
以CONSTANT_Class_info常量为例,它的二维表示结构如下:
CONSTANT_Class_info表:
[图片上传失败...(image-847fbb-1513661176215)]
tag表示当前常量的类型(当前常量为CONSTANT_Class_info,因此tag的值应为7,表示一个类或接口的全限定名);
name_index表示这个类或接口全限定名的位置.它的值表示指向常量池的第几个常量.它会指向一个CONSTANT_Utf8_info类型的常量,它的二维表结构如下:
CONSTANT_Utf8_info表:
[图片上传失败...(image-c16889-1513661176215)]
CONSTANT_Utf8_info表示字符串常量;
tag表示当前常量的类型,这里应该是1;
length表示这个字符串的长度;
bytes为这个字符串的内容(采用缩略的UTF8编码)

问:为什么Java中定义的类、变量名字必须小于64K?
类、接口、变量等名字都属于符号引用,它们都存储在常量池中。而不管哪种符号引用,它们的名字都由CONSTANT_Utf8_info类型的常量表示,这种类型的常量使用u2存储字符串的长度。由于2字节最多能表示65535个数,因此这些名字的最大长度最多只能是64K。

问:什么是UTF-8编码?什么是缩略UTF-8编码?
前者每个字符使用3个字节表示,而后者把128个ASKII码用1字节表示,某些字符用2字节表示,某些字符用3字节表示。

2.4 访问标志

在常量池结束之后是2字节的访问标志.表示这个class文件是类还是接口、是否被public修饰、是否被abstract修饰、是否被final修饰等.
由于这些标志都由是/否表示,因此可以用0/1表示.
访问标志为2字节,可以表示16位标志,但JVM目前只定义了8种,未定义的直接写0.


这里写图片描述

2.5 类索引、父类索引、接口索引集合

表示当前class文件所表示类的名字、父类名字、接口们的名字.
它们按照顺序依次排列,类索引和父类索引各自使用一个u2类型的无符号常量,这个常量指向CONSTANT_Class_info类型的常量,该常量的bytes字段记录了本类、父类的全限定名.
由于一个类的接口可能有好多个,因此需要用一个集合来表示接口索引,它在类索引和父类索引之后.这个集合头两个字节表示接口索引集合的长度,接下来就是接口的名字索引.

2.6 字段表的集合

2.6.1 什么是字段表集合?

用于存储本类所涉及到的成员变量,包括实例变量和类变量,但不包括方法中的局部变量.
每一个字段表只表示一个成员变量,本类中所有的成员变量构成了字段表集合.

2.6.2 字段表结构的定义

这里写图片描述

2.6.3 什么是描述符?

成员变量(包括静态成员变量和实例变量) 和 方法都有各自的描述符。
对于字段而言,描述符用于描述字段的数据类型;
对于方法而言,描述符用于描述字段的数据类型、参数列表、返回值。

在描述符中,基本数据类型用大写字母表示,对象类型用“L对象类型的全限定名”表示,数组用“[数组类型的全限定名”表示。
描述方法时,将参数根据上述规则放在()中,()右侧按照上述方法放置返回值。而且,参数之间无需任何符号。

2.6.4 字段表集合的注意点

2.7 方法表的集合

在class文件中,所有的方法以二维表的形式存储,每张表来表示一个函数,一个类中的所有方法构成方法表的集合。
方法表的结构和字段表的结构一致,只不过访问标志和属性表集合的可选项有所不同。


这里写图片描述

方法表的属性表集合中有一张Code属性表,用于存储当前方法经编译器编译过后的字节码指令。

方法表集合的注意点

2.8 属性表的集合

上一篇下一篇

猜你喜欢

热点阅读