Class文件格式采用一张类似于C语言结构体的伪结构来存储数据,这种伪数据结构中只有两种数据类型:无符号数和表:
无符号数属于基本的数据类型,以u1,u2,u4,u8来分别代表1个字节,2个字节,4个字节和8个字节的无符号数。
表是由多个无符号数或者其他表作为数据项构成的复合数据类型。整个Class文件本质上就是一张表。如下图所示
每个Class问价的头4个字节称为魔数,它的唯一作用是确定这个文件是否为一个能被虚拟机接受的Class文件
紧接着魔数的四个字节1存储的是Class文件的版本号:第5和第6字节是次版本号,第7和第8是主版本号。
在魔数和版本后的是常量池的入口,常量池可以理解为Class文件之中的资源仓库。
常量池的入口中有一个u2类型的数据,代表常量池的容量计算值(constant_pool_count)。这个容量的计算值是从1开始计算的。
常量池中主要存放两大类常量(字面量和符号引用)字面量主要指的是如文本字符串,申明为final的常量值等。而符号引用主要包括以下三个部分:
常量池中每一项常量都是一个表,到jdk1.7时有14项类型,如下图所示:
这14中类的常量其第一位是u1类型的标志位(tag),表示是哪种类型的常量。继而是各个类型其对应的结构及其表示,具体的每种类型结构可从下图了解:
在常量池之后随后的两个字节表示的是访问标志(access_flags),这个标志用于识别一些类或者接口层次的访问信息:如这个class是类还是接口;是否定义为public类型...具体见下图:
类索引和父类索引都是一个u2类型的数据,分别表示当前类和其父类的而接口是一组u2类型的数据的集合,接口索引集合是一组u2类型的数据的集合,class文件由这三项数据来确定这个类的继承关系。
字段表用于描述接口或者类中声明的变量。但不包括方法内部声明的局部变量,其主要结构如下图所示。
其中access_flags表示的是字段的相关修饰字段,如是否为public,private,protected,static,final,volatile,transient,enum或编译器自动产生的。
其后是name_index和descriptor_index两个索引值,它们都是对常量池的引用,分别代表着字段的简单名称以及字段和方法的描述符。
方法和字段的描述符的规则为:基本数据类型(byte,char,double,float,int,long,void,boolean)等都用大写字符来表示,而对象类型则用字符L加对象的全限定名来表示。举例来说:int indexOf(char[]source,int sourceOffset,int sourceCount,char[]target,int targetOffset,int targetCount,int fromIndex)的描述符为"([CII[CIII)I"。
Class文件格式对于方法的描述和对于字段的描述方式基本一致,方法表的结构和一致,其结构图可以见方法表。对于方法表中access_flags的表述由于方法中没有(transient,volatile等描述,相应的添加了如synchonized,native,strictfp和abstract等属性)。
在方法集中,可能存在编译器自动添加的方法,最典型的就是类构造器方法"
在java语言中,要重载(overload)一个方法,除了要与原方法具有相同的简单名称之外,还要求必须拥有一个与原方法不同的特征签名。特征签名就是一个方法中各个参数在常量池中的字段符号引用的集合。
方法表,字段表中都携带自己的属性表集合,以用于某些场景专有的信息。虚拟机中的属性有下图所示21中:
java程序代码块中的代码经过javac编译器编译处理后,最终变为字节码存储在code属性内。
code属性表结构中主要有一下几项:attribute_name_index指向一个恒定为"Code"的CONSTANT_Utf8_info型常量的索引。表示属性名称
max_stack代表操作数栈深度的最大值。max_locals代表局部变量表所需的存储空间.在这里,max_locals的单位是slot。code_length最大值为65535,一般不会超过这个值,但是如果是编译复杂的jsp文件时,可能超过这个值。
在javac对实例方法的实现时将this作为普通参数调用,在虚拟机调用实例方法时自动传入了this这个参数。
在code属性中还有的是异常表。异常表中有四个部分:(start_pc , end_pc , catch_type , handler_pc)在start_pc到end_pc之间出现了类型为catch_type或者其子集的异常时则转到handler_pc中进行处理。当catch_type=0,表明任何异常都要转到hander_pc中执行。
Exceptions属性列出可能抛出的受检查的异常
LineNumber属性用于描述java源码行号与字节码行号之间的对应关系(可以在javac中用-g:none或-g:lines取消生成)
LocalVariableTable属性用于描述栈帧中局部变量表中的变量与Java源码中定义的变量之间的关系(可以在javac中用-g:none或-g:vars取消生成)
SourceFile属性可以用于记录生成这个Class文件的源码文件名称(可以在javac中用-g:none或-g:source取消生成)
ConstantValue属性的作用是通知虚拟机自动为静态变量赋值。只有被static关键字修饰的变量(类变量)才可以使用这项属性。
对于类变量和实例变量赋值的方式不同,实例变量的赋值是在"
InnerClass属性用于记录内部类与宿主类之间的关联。如果一个类中定义了内部类,那么编译器将会为它和它所包含的内部类生产InnerClasses属性
InnerClass属性结构和inner_class_info属性结构如下图:
Deprecated属性表示作者已不推荐使用,Syntheic表示此字段或者方法是由编译器自动生成
这个属性会在虚拟机类加载的字节码验证阶段被新类型检查验证器使用,目的在于代替以前比较消耗性能的基于数据流分析的类型推导验证器。
任何类,接口,初始化方法或成员的泛型类型签名如果包含了类型变量或参数化类型,则Signature属性会为它记录泛型签名信息。它的作用是为了处理java对于泛型的使用时使用伪泛型中如运行期做反射时无法获得反射信息等问题而存在的。
BootstrapMethods属性用于保存invokedynamic指令引用的引导方法限定符。
其中如iload_等以n结尾的n为iload_0 , iload_1,iload_2等。
java直接支持基本类型的向宽化类型转型,对于java基本类型的范围排序为(char
对与char,boolean,byte,short等类型的比较会转换为int类型,而对于long,float和double类型则先调用比较运算指令(dcmpg,dcmpl,fcmpg)比较后返回int类型的值再用int类型判断
java虚拟机中,处理异常不是由字节码来实现,而是用异常表来实现(code属性中的exception table)
同步指令集monitorenter和monitorexit,当方法调用时,调用指令会检查方法的ACC_SYNCHRONIZED访问标志是否被设置,如果设置了执行线程要求成功持有管程,在方法执行期间,执行线程持有了管程,其他任何线程都无法再获取到同一个管程。如果同一个同步方法执行期间抛出了异常,并且在方法内部无法处理此异常,那么这个同步方法所持有的管程将在异常抛出时自动释放。