java虚拟机 — 字节码生成

1. 总体流程

简单来讲,一款编程语言想兼容底层(跨操作系统)的方式大概有两种:一是通过编译器,如C或C++。但是要针对不同硬件平台和操作系统开发不同的编译器,这样实现十分麻烦。二是通过中间语言,如Java、C#。代码被编译后生成中间语言,后由虚拟机负责解释和运行,虚拟机在运行期间将中间语言实时翻译成与特定底层平台匹配的机器指令并运行。
java语言声称可以“一次编译,到处运行(write once , run everywhere)”,那么具体是如何实现的呢?
主体流程大约如下:一个java代码文件,首先被编译器(javac)编译成一个特定格式的二进制字节码文件(class文件),然后被虚拟机(jvm)的加载器加载到内存中,再由执行引擎将将二进制字节码转换为可直接运行的本地机器码并运行。流程图如下:
java虚拟机 — 字节码生成_第1张图片
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类遍历语法树,生成最终字节码;
过程图如下:
java虚拟机 — 字节码生成_第2张图片

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属性记录内部类信息。

4. 动态编程与静态编程


静态编程:类型检查是在编译时完成的。

动态编程:类型检查是在运行时完成的。

所谓动态编程就是绕过编译过程在运行时进行操作的技术,在Java中有如下几种方式:

反射
这个搞Java的应该比较熟悉,原理也就是通过在运行时获得类型信息然后做相应的操作。

动态编译
动态编译是从Java 6开始支持的,主要是通过一个JavaCompiler接口来完成的。通过这种方式我们可以直接编译一个已经存在的java文件,也可以在内存中动态生成Java代码,动态编译执行。

调用JavaScript引擎
Java 6加入了对Script(JSR223)的支持。这是一个脚本框架,提供了让脚本语言来访问Java内部的方法。你可以在运行的时候找到脚本引擎,然后调用这个引擎去执行脚本。这个脚本API允许你为脚本语言提供Java支持。

动态生成字节码
这种技术通过操作Java字节码的方式在JVM中生成新类或者对已经加载的类动态添加元素。

操作java字节码的工具有两个比较流行,一个是ASM,一个是Javassit 。

ASM :直接操作字节码指令,执行效率高,要是使用者掌握Java类字节码文件格式及指令,对使用者的要求比较高。

Javassit 提供了更高级的API,执行效率相对较差,但无需掌握字节码指令的知识,对使用者要求较低。

应用层面来讲一般使用建议优先选择Javassit,如果后续发现Javassit 成为了整个应用的效率瓶颈的话可以再考虑ASM.当然如果开发的是一个基础类库,或者基础平台,还是直接使用ASM吧,相信从事这方面工作的开发者能力应该比较高。

Java 字节码以二进制的形式存储在 .class 文件中,每一个 .class 文件包含一个 Java 类或接口。Javaassist 就是一个用来 处理 Java 字节码的类库。它可以在一个已经编译好的类中添加新的方法,或者是修改已有的方法,并且不需要对字节码方面有深入的了解。同时也可以去生成一个新的类对象,通过完全手动的方式。

java虚拟机 — 字节码生成_第3张图片

你可能感兴趣的:(Java,java,开发语言,后端)