java虚拟机面试干货(一)— 字节码生成
1. 总体流程简单来讲,一款编程语言想兼容底层(跨操作系统)的方式大概有两种:一是通过编译器,如C或C++。但是要针对不同硬件平台和操作系统开发不同的编译器,这样实现十分麻烦。二是通过中间语言,如Java、C#。代码被编译后生成中间语言,后由虚拟机负责解释和运行,虚拟机在运行期间将中间语言实时翻译成与特定底层平台匹配的机器指令并运行。java语言声称可以“一次编译,到处运行(write o...
1. 总体流程
简单来讲,一款编程语言想兼容底层(跨操作系统)的方式大概有两种:一是通过编译器,如C或C++。但是要针对不同硬件平台和操作系统开发不同的编译器,这样实现十分麻烦。二是通过中间语言,如Java、C#。代码被编译后生成中间语言,后由虚拟机负责解释和运行,虚拟机在运行期间将中间语言实时翻译成与特定底层平台匹配的机器指令并运行。
java语言声称可以“一次编译,到处运行(write once , run everywhere)”,那么具体是如何实现的呢?
主体流程大约如下:一个java代码文件,首先被编译器(javac)编译成一个特定格式的二进制字节码文件(class文件),然后被虚拟机(jvm)的加载器加载到内存中,再由执行引擎将将二进制字节码转换为可直接运行的本地机器码并运行。流程图如下:
JVM也会根据使用场景划分内存区域,当内存不足时会自动触发垃圾回收(GC)。为了将主机的性能最大化发挥,有时我们还需要进行JVM调优。
下面依次对上面的内容进行展开说明。
2. 字节码生成
通过上面的简述我们已经知道,java语言中是用过javac程序将java文件编译成class文件的,编译的过程大约分为如下四步:
(1)词法分析器:将代码(字符流)中关键字和标识符等内容解析转换成Token流,token流就是一组对应源码字符集和的单词序列;
(2)语法分析器:解析Token流中package关键字声明、import关键字声明和class主体信息,组建成更加结构话的语法树;
(3)语义分析器:对语法树进一步处理,包括添加默认构造函数、检查变量初始化、常量合并处理、检查操作变量类型是否匹配、检查操作语句是否可达、异常是否捕获或抛出、接触java语法糖等;
(4)代码生成器:调用com.sun.tools.javac.jvm.Gen类遍历语法树,生成最终字节码;
过程图如下:
3. Class文件结构
Class文件是一组以8位字节为基础范围的二进制刘,各个数据项目严格按照顺序紧凑地排列在Class文件之中,中间没有添加任何分隔符。一个Class文件主要包含如下内容:
魔数和Class文件版本
头4个字节为魔数,0xCAFEBABE。
紧跟着魔数的4个字节存储的是Class文件的版本号,其中第7、8位保存的是主版本号。
常量池
可理解为Class文件之中的资源仓库,由于常量池中常量的数量是不固定的,所以在常量池入口放置一个u2类型的数据,代表常量池计数值。常量池中主要存放两类常量:字面量和符号引用。字面量即常量,符号引用包含类和接口的全限定名、字段的名称和描述符和方法的名称和描述符。
访问标识
紧跟在常量池后的2个字节,标识一些类或接口层次的访问信息,如:是类还是接口、是否为public类型、是否为abstract类型,是否为final等。
类索引、父索引与接口索引集合
类索引和父索引都是u2类型,而接口索引是一组u2类型数据集合。class文件用这三个数据来确定这个类的继承关系。
字段表集合
字段表用于描述接口或者声明的变量,字段包括类变量和实例变量,但不包括局部变量。
方法表集合
结构和字段表一致,保存方法的属性。注意,不包含方法的代码。
属性表集合
包含若干个属性。其中,
code属性储存方法的代码,
Exceptions属性存储throws关键字后的列举的异常,
lineNumberTablee属性存储java源码和字节码行号的对应关系,
localVariableTable属性记录栈帧中局部变量表中的变量和java源码中定义的变量之间的关系,
sourceFile属性记录生成这个class文件的源码文件名称,
ConstantValue属性通知JVM自动为静态变量赋值,
InnerClasses属性记录内部类信息。
更多推荐
所有评论(0)