字节码文件是跨平台的吗
是的,Java虚拟机不和包括Java在内的任何语言绑定,它只与Class文件这种特定的二进制文件格式所关联。无论使用何种语言进行软件开发,只要将源文件编译为正确的Class文件,那么这种语言就可以在Java虚拟机上执行。可以说,统一而强大的Class文件结构,就是Java虚拟机的基石、桥梁。
Class文件里面是什么
Class文件是一种二进制类文件,它的内容是JVM的指令,而不像C、C++经由编译器直接生成机器码
随着Java平台的不断发展,在将来,Class文件的内容也一定会做进一步的扩充,但是其基本的格式和结构不会做重大调整。
生成Class文件的编译器
典型的就是javac编译器。除了javac之外就是内置在Eclipse中的ECJ编译器,与javac全量式编译不同,ECJ是增量式编译器。
主要任务就是将符合Java语法规范的Java代码转换为符合JVM规范的字节码文件。
javac编译器的编译步骤
程序源码 -> 词法分析 -> 单词流 -> 语法分析 -> 抽象语法树 -> JVM字节码
目前前端编译器的局限性
前端编译器并不会直接涉及到编译优化方面的计数,而是将这些具体优化细节交给HotSpot的JIT编译器负责(后端编译器)
哪些类型对应有Class的对象
- class
- interface
- []数组
- enum
- annotation
- primitive type
- void
什么是字节码指令
字节码指令是由一个字节长度的,代表着某种特定操作含义的操作码以及跟随其后的零至多个代表此操作所需参数的操作数所构成(虚拟机中许多指令并不包含操作数,只有一个操作码)
如何解读Class文件
Binary Viewer、javap、jclasslib
Class文件结构
-
魔数
MagicNumber,Class文件的开头标志,文件开始的4个字节。
-
Class文件版本
紧接着魔数的4个字节存储的是Class文件的版本号,同样4个字节。前两个字节构成编译的副版本号(m),后面两个字节构成编译的主版本号(M),Class文件的版本号为M.m。高版本兼容低版本,反之则不行。
-
常量池
Class文件的基石。包含常量池计数器和常量池表。由于常量池的数量不固定,时长时短,所以需要放置两个字节来表示常量池容量计数值。常量池主要存放两大类常量:字面量和符号引用,符号引用后续在类的加载过程中需要转换为直接引用。
字面量:文本字符串、声明为final的常量值
符号引用:类和接口的全限定名、字段的名称和描述符、方法的名称和描述符
符号引用:符号引用以一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能无歧义地定位到目标即可。符号引用与虚拟机实现的内存布局无关,引用的目标并不一定已经加载到了内存中 直接引用:直接引用可以时直接指向目标的指针、相对偏移量或是一个能间接定位到目标的句柄。直接引用是与虚拟机实现的内存布局相关的,同一个符号引用在不同虚拟机实例上翻译出来的直接引用一般不会相同。如果有了直接引用,那说明引用的目标必定已经存在于内存之中了。 -
访问标识
在常量池之后,紧跟着访问标记。该标记使用两个字节表示,用于识别一些类或者接口层次的访问信息,包括:这个Class是类还是接口、是否定义为public类型、是否定义为abstract类型、如果是类的话,是否被声明为final等
-
类索引、父类引用、接口索引集合
在访问标识之后,会指定该类的类别、父类类别以及实现的接口。
-
字段表集合
包括字段计数器和字段表。用于描述接口或类中声明的变量。字段包括类级变量以及实例变量,但是不包括方法内部、代码块内部声明的局部变量。
-
方法表集合
包括方法计数器和方法表。指向常量池索引集合,它完整描述了每个方法的签名。
-
属性表集合
包括属性计数器和属性表。指的是Class文件锁携带的辅助信息,比如该Class文件的源文件名称以及任何带有RetentionPolicy.CLASS或者RetentionPolicy.RUNTIME的注解。这类信息通常被用于Java虚拟机的验证和运行,以及Java程序的调试,一般无需深入了解。
字节码指令
- 加载与存储指令
- 算术指令
- 类型转换指令
- 对象的创建与访问指令
- 方法调用与返回指令
- 操作数栈管理指令
- 控制转移指令
- 异常处理指令
- 同步控制指令