jvm06

Class文件

javac Class文件是一组以8进制为基础的二进制流,各个数据项目严格按照顺序紧凑的排列在Class文件中,中间没有添加任何分隔符,整个class文件存储的内容几乎全是程序运行的必要数据,没有空隙存在。

当遇到8位字节以上的空间的数据项,则会按照高位在前的方式分割成若干个8位字节进行存储。 Class文件有两种数据类型,分别是无符号位和表

运行在Jvm之上的语言:Clojure ,groovy,Jruby,Jython,Scala

class文件结构

魔数
Class文件版本
常量池
访问标志
类索引,父类索引,接口索引集合
字段表集合
方法表集合
属性表集合

魔数

通过魔数区分文件类型,而不是通过扩展名,魔数后四个字节表示的是字节码的版本,前三位表示次版本,后一位表示主
版本,jdk1.8 = 52 jdk1.7 = 51


常量池

存放的内容,字面量:文本字符串,声明为final的常量池。符号引用:类和接口的全限定名,字段的名称和描述符,方法的名称和描述符。

 


cp_info
type descriptor
  u1 tag
  u1 info[]

常量池项,cp_info都会对应记录着class文件中的某种类型的字面量,根据tag值确定某个常量池项(cp_info)表示什么类型的字面量。

 

 

常量池有19个长度,常量项有1-18,cp_info之前有constant_pool_count 表示常量的项数。

常量池中的每一项常量都是一个表

CONSTANT_Methodref_info
用于记录方法信息,包括类中定义的方法和代码中使用的方法
type descriptor
  u1 tag
  u2 class_index
  u2 name_and_type_indexang

 

访问标识(access_flags)紧接着常量池后,占有两个字节,总共16位
  public 0x0001
  final 0x0010
  super 0x0020
  interface 0x0200
  abstaract 0x0400

类索引紧接着访问标志的后面,占有两个字节,在这两个字节中存储的值是一个指向常量池的一个索引,该索引指向的是CONSTANT_Class_info常量池项,即类索引是告诉我们这个class文件所表示的是哪一个类。

跟类索引一样,父类索引这两个字节中的值指向了常量池中的某个常量池项CONSTANT_Class_info,表示该class表示的类是继承自哪一个类。

 

接口索引集合:

由于类实现的接口数目不确定,所以接口索引集合的描述的前部分叫做接口计数器(interfaces_count),接口计数器占用两个字节,其中的值表示着这个类实现了多少个接口,紧跟着接口计数器的部分就是接口索引部分了,每一个接口索引占有两个字节,接口计数器的值代表着后面跟着的接口索引的个数。接口索引和类索引和父类索引一样,其内的值存储的是指向了常量池中的常量池项的索引,表示着这个接口的完全限定名

字段表集合:

字段表集合是指若干个字段表(field_info)组成的集合。

FIeld_info的组成元素:访问标志(access_flags)、名称索引(name_index)、描述索引(descriptor_index)、属性表集合
字段表用于描述接口或者类中声明的变量

access flags占两个字节

如果某个类有定义field域:public static String str;则第15位的acc_private和acc_static标志位为1,0000 0000 0000 1010 访问标志为0*000A

所以该字段的标志符只有有ACC_PRIVATE和ACC_STATIC。

 

 

javac编译器在编译此field字段构建field_info结构体时,除了访问标志、名称索引、描述符索引外,会增加一个ConstantValue类型的属性表。

 

 

方法表集合

方法表集合是指由若干个方法表(method_info)组成的集合。对于在类中定义的若干个,经过JVM编译成class文件后,会将相应的method方法信息组织到一个叫做方法表集合的结构中,字段表集合是一个类数组结构

 

方法表集合紧跟在字段表集合后面,包含方法计数器和方法信息数据区(多个method_info结构)。

method_info包括访问标志,名称索引,描述索引,属性表组成。

访问标志(access_flags):

method_info结构体最前面的两个字节表示的访问标志(access_flags),记录这这个方法的作用域、静态or非静态、可变性、是否可同步、是否本地方法、是否抽象等信息等。

名称索引(name_index):

紧跟在访问标志(access_flags)后面的两个字节称为名称索引,这两个字节中的值指向了常量池中的某一个常量池项,这个方法的名称以UTF-8格式的字符串存储在这个常量池项中。该常量池项表示这这个方法名称的字符串。

描述索引(descriptor_index):

描述索引表示的是这个方法的特征或者说是签名,一个方法会有若干个参数和返回值,而若干个参数的数据类型和返回值的数据类型构成了这个方法的描述,其基本格式为:(参数数据类型描述列表)返回值数据类型 )  。表示了方法描述符的字符串。

属性表(attribute_info)集合:

方法的实现被JVM编译成JVM的机器码指令机器码指令就存放在一个Code类型的属性表中。

 

 

 

 

Code类型的属性表包含的是JVM可运行的机器码指令

 

例 定义 Demo.java

public class Simple {
	
	public static synchronized final void greeting(){
		
		int a = 10;
	}
	
}

00 21 类访问标志 00 02 类索引 00 03父类索引 00 00 接口索引集合 00 00 字段表集合

方法表的头两个字节为方法表计数器 00 02 表示该类有两个方法,分别表示构造方法 <init> () 和 greeting()方法

图为init()方法

在Method_info中,00 01 访问标志 ,即标志位16位为1,该<init>() 方法修饰符为ACC_PUBLIC

00 04 名称索引,方法名称为 init

00 05 描述索引,()V 表示无参,无返回值

00 01 属性计数器,表示该方法表含有一个属性表,后紧跟属性表

 

Attribute_info中,

00 06 属性名称索引,表示字符串code,表示属性表是code类型的属性表

00 00 00 1D 属性长度,即十进制29,表示后续29个字节表示属性信息

00 01 栈最大深度,最大深度为1,

00 01 局部变量表最大值,JVM在调用该方法时,根据这个值设置栈帧中的局部变量表的大小

00 00 00 05 机器指令数目,后续的5 个字节 0x2A 、0xB7、 0x00、0x01、0xB1表示机器指令;

2A B7 00 01 B1 机器指令集

00 00 异常表,00 00 表示方法中没有需要处理的异常信息;

 

图为greet()方法

00 39 访问标志 ,即 00000000 00111001 即11,12 , 13 ,16 为1 ,可以得到该greeting()方法的修饰符有:ACC_SYNCHRONIZED、                                 ACC_FINAL、ACC_STATIC、ACC_PUBLIC;

00 08 名称索引,字符串常量池第8项,该项表示字符串“greeting”,即该方法的名称是“greeting”;

00 05 描述索引,()V”,即表示该方法不带参数,并且无返回值;

00 01 属性计数器

在Attribute中

00 06 属性名称索引,指向常量池中的第6 项,该项表示字符串“Code”,表示这个属性表是Code类型的属性表;

00 00  00 20 属性长度

00 01 栈最大深度

00  01 局部变量表最大值 

00 00 00 04 机器指令数目

10 0A 3B BA1 机器指令

00 00 异常表

01 00 属性表集合

 

Exceptions类型的属性表

1 public interface Interface {
2  
3     public  void sayHello() throws Exception;
4 }

 

 

1. 方法计数器(methods_count)中的值为0x0001,表明其后的方法表(method_info)就一个,即我们就定义了一个方法,其后会紧跟着一个方法表(method_info)结构体;

2. 方法的访问标志(access_flags)的值是0x0401,二进制是00000100 00000001,第6位和第16位是1,对应上面的标志位信息,可以得出它的访问标志符有:ACC_ABSTRACT、ACC_PUBLIC。但在上面声明的sayHello()方法中并没有声明为abstract类型。确实如此,这是因为编译器对于接口内声明的方法自动加上ACC_ABSTRACT标志

3. 名称索引 指向常量池第三项即表示的方法名称是sayHello

4. 描述符索引(descriptor_index)指向了常量池中的第4项,第4项表示的字符串为“()V” 表示这个方法的无入参,返回值为void类型

5. 属性表计数器

6. 属性表 ,向了常量池中的第5项,第 5项指向字符串“Exceptions”,即表示该属性表表示的异常信息;

7. 属性长度(attribute_length)

8. 异常数量(number_of_exceptions)中的值为0x0001,表示这个方法声明抛出的异常个数是1个;

9.异常名称索引,指向了常量池中的第6项,第6项表示的是CONSTANT_Class_info类型的常量池项,表示“java/lang/Exception”,即表示此方法抛出了java.lang.Exception异常。

 

属性表集合
描述额外的属性

attribute_info {
  u2 attribute_name_index
  u4 attribute_length
  u1 info[attribute_length]
}

code属性在方法表的属性集合中,code属性存储的是方法体中的代码经过javac编译器处理之后的字节码指令.

但是并非所有的方法表都有这个属性.例如接口或类中的方法就不存在Code属性了.如果方法表有Code属性存在.

 

 

 

posted @ 2019-06-26 23:32  曲阳阳  阅读(125)  评论(0编辑  收藏  举报