深入理解java虚拟机——类文件结构

魔数

class文件的前4个字节是魔数,用来表示其类型

class文件的魔数是0xCAFEBABE   

版本号

紧接着魔数的4个字节存储的是class文件的版本号  第五个第六个表示的是次版本号, 第七个第八个表示的是主版本号,java的版本号是从45开始的, jdk1.1之后的每个jdk版本向上加1, 高版本的jdk能向下兼容以前版本的jdk版本

常量池

常量池可以理解为class文件之中的资源仓库, 它是Class文件结构中与其他项目关联最多的数据类型, 也是class文件中占用空间最大的一块区域

常量池中常量的数量是不确定的, 所以在常量池的入口需要放置一项u2类型的数据, 用来代表常量池容量计数值(这个计数是从1开始的, 所以求出来的值需要减去1, 设计者将0空出来表示特殊的含义)

常量池中主要存放两大类常量:字面量和符号引用,字面量比较接近java语言层的常量概念, 如文本字符串, 声明为final的常量值相等, 而符号引用则属于编译原理方面的概念, 包括了下面三类常量

1. 0类和接口的全限定名

2.字段的名称和描述符

3.方法的名称和描述符

java代码不存在连接一说, 而是在加载class文件时进行动态链接

常量池中有14种常量项

java的方法名和变量名都是一个CONSTANT_Utf8_info类型, 所以最大长度只能为65535个字节, 如果超过64k英文的变量名或方法名, 将会无法编译

常量池的访问标志

常量池结束后, 紧接着的两个字节代表访问标志

这个标志用于标识一些类或者接口层次的访问信息, 包括这个class是类还是接口, 是否定义为public类型;是否定义为abstract类型等等

access_flags中一共有16个标志位可以使用

类索引,父类索引与接口索引集合

this_class和父类索引super_class都是一个u2类型的数据, 而接口索引集合是一组u2类型的数据的集合, class文件中由这三项数据来确定这个类的继承关系

类索引和父类索引都是u2类型的索引值, 他们各自只想一个类型为CONSTANT_Class_info  

接口索引集合  入口的第一项u2类型的数据为接口计数器, 表示索引表的容量,后面表示对应的接口类型索引值

描述符的作用用来秒速字段的数据类型,方法的参数列表和返回值

基本类型和void都用一个大写字母来表示

方法集合表

里面有5个成员access_flag   name_index   descriptor_index  attributes_count   attributes

方法的定义可以通过访问标志, 名称索引,描述符索引表述, 但是方法中的java代码啦,

其实在java中, 方法中的java代码经过编译器编译成字节码指令后, 存放在方法属性表集合中一个名为Code的属性里面 在方法的属性表集合中  0x0009代表常量Code

code属性

code_length虽然是一个u4类型的数, 理论上可以达到2^32-1,但是虚拟机规范中明确限制了一个方法不允许超过65535条字节码

在整个class文件中, Code属性用于描述代码, 所有其他数据项目都用于描述元数据

javac编译器编译的时候把对this关键字的访问转变为对一个普通方法参数的访问, 然后在虚拟机调用实例方法时自动传入此参数

异常表的格式

star_pc   end_pc   handler_pc  catch_type   在star_pc和end_pc之间的代码出现catch_type或者其子类的异常, 则会调用handler_pc进行处理

如果try语句块中出现属于Exception或者其子类的异常, 则转到catch语句块处理

如果try语句块中出现不属于Exception或其子类的异常, 则转到finally语句块中处理

如果catch语句块中出现任何异常, 则转到finally语句块处理

如果代码块中没有出现exception返回值是1, 如果出现了exception异常, 则返回值是2

Exceptions属性

这里的Exceptions属性实在方发表中与Code属性平级的一项属性, 就是方法描述时在throws关键字后面列举的异常

属性表的结构

attribute_name_index   1   attribute_length   1    number_of_exceptions   1    exception_index_table     number_of_exceptions

LineNumberTable属性

java源码行号和字节码行号之间的对应关系   可以在javac中分别使用-g:none   -g:lines选项来取消或要求生成这项信息, 这个东西的最大价值在于, 如果出现错误的话, 会提示出错的行号

LocalVariableTable属性

用于描述栈帧中局部变量表中的变量与java源码中定义的变量之间的关系, 如果没有生成这项属性的话, 则会导致别人使用你的方法时, 会出现所有的参数名称都将丢失, IDE将会使用诸如arg0, arg1之类的占位符来替代原有的参数名

SourceFile属性

该属性用来记录生成这个Class文件的源码文件名称, 这个属性也是可选的, 可以分别使用javac的-g:none或-g:source选项来关闭或要求生成这项信息

ConstantValue属性

ConstantValue属性的作用是通过虚拟机自动为静态变量赋值

对于非static变量的赋值实在实例构造器init方法中进行的, 对于类变量, 在类构造器<clinit>方法中或者使用ConstantValue属性, 如果同时使用final和static来修饰一个变量,并且这个变量的数据类型是基本类型或者java.lang.String的话, 就生成ConstantValue属性来进行初始化

innnerClasses属性

该属性用来记录内部类与宿主类之间的关联

Deprecated及Synthetic属性

这两个属性都属于标志类型的布尔属性

Deprecated属性用于表示某个类,字段或者方法, 已经被程序作者认定为不再推荐使用, 它可以通过在代码中使用@deprecated注释进行设置

Synthetic属性代表此字段或者方法并不是由java源码产生的

字节码指令介绍

java虚拟机的指令由一个字节长度的, 代表着某种特定操作含义的数字以及跟随其后的零至多个代表此操作所需参数   操作码为1个字节,java放弃了对齐, 致使其需要耗费一定的cpu去重构出具体的数据

大部分的指令都没有支持整数类型byte,char和short甚至没有任何指令支持boolean类型, 编译期会将这些数据类型转为相应的int类型数据

公有设计和私有实现

Class文件格式以及字节码指令集,这些都是和硬件操作系统和虚拟机无关的, 虚拟机实现者可能更愿意把它们看做是程序在各种java平台实现之间相互安全地交互的手段

Class文件格式所具备的平台中立, 紧凑, 稳定和可扩展的特点是java技术体系实现平台无关, 语言无关两项特征的重要支柱

    原文作者:java虚拟机
    原文地址: https://blog.csdn.net/chunshucai/article/details/41446779
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞