类文件结构
本文大体讲一下结构,着重从理解层面看,就是当时我最难理解的地方讲,并不会深入的讲解某一块的具体结构信息,如果要看结构信息还请另行搜索
前提:实现语言无关性的基础仍然是虚拟机和字节码存储格式
对不同的平台进行不同的虚拟机实现(主要还是解释指令码)以达到语言与平台无关,最核心的还是虚拟机进行的转换。
Java语言的定义时就拆分了Java语言规范和Java虚拟机规范,这个虚拟机规范就是定义Class文件中字节码格式的,它和Java语言毫无关系,因此其它语言如果通过编译器能生成符合Java虚拟机规范的字节码,那么也可以运行在Java虚拟机上,并且Java虚拟机规范的字节码命令比Java语言本身的语法更强,这也提供了其它基于Java虚拟机运行的语言不同于Java的特性
Class文件结构
Class文件结构类似于C的结构体,只有两种数据类型,无符号数和表
表的意思有点像类型,可以定义表结构然后引用持有,下面的常量池内和属性表内就定义了很多预设的表结构供其他结构持有。理解了表的定义后面看结构信息就简单多了
java -v a.class可以查看统计之后的class文件,Class文件结构必须严格按照这个顺序定义并且排列JVM就直接安全这个顺序进行解析的。
1魔数,头4个字节都是魔数,java的为0xCAFEBABE
2class版本号:主次版本号依次排列
3常量池:常量池容量计数器和常量池本身,常量池包涵两大类常量,字面量和符号引用
常量池很重要,必须要理解了它后面才可以理解。 常量池可以理解为Class文件之中的资源仓库,它是Class文件结构中与其他项目关联最多的数据类型(后面的很多数据类型都会指向此处),也是占用Class文件空间最大的数据项目之一。这里定义了一系列的固定结构信息,下面的信息很多都指向这里
4访问标志:2个字节,用于识别类或者接口层次的访问信息
5类索引,父类索引,接口索引集合:通过这三项数据确定类的继承关系
6字段表集合:描述接口或者类中声明的变量
7方法表集合:描述方法
8属性表集合:在class文件,字段表,方法表都可以携带自己的属性表,属性表包含很多拓展信息,包括代码字节码部分,签名,注解信息,废弃标志等,反正这里有很多信息可以用来拓展类信息的,如果有其它部分需要就持有即可.
属性表也很重要,代码什么玩意都是存在这里的,对于字段表啊,方法表啊的都是通过持有属性表数组达到描述自身的。
如图展示完整信息,每个class文件格式都必须是严格按照这个顺序排的
Class文件结构 常量池结构定义
从这里图中可以看到常量池的结构中几乎包含了所有涉及的类结构定义。你的类中涉及到多少那么就载入多少到常量池中进行使用即可,属性表也是一样的。
字节码指令
Java虚拟机指令由一个字节长度的,代表着某种特定操作含义的数字(操作码)以及跟随其后的零个或多个参数(操作数)而构成。
Java虚拟机采用面向操作数栈而不是寄存器架构,所以大多数指令都不包含操作数
有兴趣的可以自行搜索一下这俩的区别,后面讲虚拟机执行引擎的时候会详细解释基于操作数栈的执行流程。想要理解字节码指令需要先了解操作数栈架构的执行流程,这里不太理解也不重要,先知道一下。
字节码按照用途大致分为9类:
1加载和存储指令:将数据在帧栈中的局部变量表盒操作数栈之间来回传输
2运算指令:加减乘除,求余,取反,位移。。等等
3类型转换:主要是显示类型转换或者将无法操作的数据类型转换为可以操作的数据类型
4对象创建与访问:实例的创建和数组的创建
5操作数栈管理指令:可以直接出栈,复制,交换等
6控制转移指令:有条件或者无条件的修改PC寄存器的值
7方法调用和返回指令:名字说明一切
8异常处理指令:athrow指令处理抛出的异常,catch的异常通过异常表完成
9同步指令:monitorenter和monitorexit,进入和退出
第一次看的时候都不太相信就靠这些指令就完成了我代码的逻辑转换,但是仔细一看基本都包含的代码的运行逻辑功能。如果是简单的解释器实现那JVM也太简单了吧。很明显JVM并不是简单的解释器,它还有JIT的特性以提高性能。
总结
看了一圈就忘了,还是理解理解就好了吧。有一点还是有很多收获的,了解了class文件结构知道了原来代码都是在属性表存着,实际的实例对象还是指向原来的class对象来进行方法的执行,实例对象本身只存储数据。
对于Java有ASM的一些字节码操纵包可以修改class结构安全且高效,很多框架的动态代理就是基于这个实现的,典型就是cglib。