什么是JVM
jvm即Java虚拟机,我们常说的Java虚拟机通常指的是sun公司的hotspot虚拟机。它主要负责解释执行字节码文件,是可以加载、运行Java字节码文件的虚拟计算机。
jvm是Java语言实现跨平台性的关键,它屏蔽了不同操作系统之间的差异,Java源程序经过编译之后变成字节码文件,jvm可以将Java的字节码文件转译为系统指令,使得Java程序可以在不同的操作系统上运行,实现‘一次编译,到处运行’。
注:编译生成的字节码是不能直接运行的,必须通过jvm翻译成机器码才能运行。不同平台下编译生成的字节码是一样的,但是不同平台的jvm翻译成的机器码是不同的。
jvm,jdk,jre的区别
jdk指的是Java开发工具包,它包括jre和一些开发工具,通常是给开发人员使用的;
jre是Java运行环境,它包括jvm和Java类库。
Java语言跨平台的优点和缺点
优点:
一次编译,到处运行。像C、C++等语言开发的程序在不同操作系统下是不兼容的,如果要在不同的平台上运行,至少要编译成不同的目标代码。而Java语言在不同平台上运行时是不需要重新编译的。Java语言使用Java虚拟机屏蔽了与具体平台相关的信息使得Java语言编译程序只需生成在Java虚拟机上运行的目标代码就可以在多种平台上不加修改地运行。Java虚拟机在执行字节码时把字节码解释成具体平台上的机器指令执行。
缺点:
Java跨平台要依赖jvm实现,所以要运行Java程序就必须安装jvm,增加了程序运行的成本。字节码文件要通过jvm翻译才能运行也会影响程序运行速度。
Java代码编译流程
将源代码翻译为目标代码的过程就叫做编译过程。
Java编译是指将.java源文件转换为.class文件的过程,这个过程称为编译前端;除此之外,编译还可以指运行期即使编译(JIT)或提前编译(AOT),这两种编译称为编译后端。
编译前端(.class文件的生成)
我们在代码层面上所看到的:javac编译工具进行编译,然后执行
编译:javac 源文件名.java
执行:java 类名
那么在javac这个命令的执行过程中发生了什么?
如图所示,javac编译处理主要包括三个阶段,分别是解析与填充符号表、注解处理、以及生成字节码。
-
解析与填充符号表
- 词法分析、语法分析
词法分析是将源代码的字符流转变为Token(标记)集合。单个字符是程序编写过程的而最小元素,而标记是编译过程的最小元素,关键字、变量名、字面量和运算符都可以称为标记,如'int a= b+2;'这句代码中就包含了6个标记,分别是int、a、=、b、+、2。
语法分析是根据Token流来构建抽象语法树的过程。抽象语法树(AST)是一种用来描述程序代码语法结构的树形表示方式,语法树的每一个节点都代表者程序代码中的一个语法结构,比如包、类型、修饰符、运算符、接口、返回值甚至连代码注释都可以是一个语法结构。
经过语法分析后,编译器基本不会再对源代码进行操作了,后续的操作都是建立在抽象语法树之上。
2.填充符号表
符号表指的是有一组符号地址和符号信息构成的表格,类似于哈希表中k-v键值对形式。
符号表用来存储标识所对应的类型、作用域。
-
注解处理
jdk 1.6开始提供了了一组插入式的注解处理器API,可以实现在编译期间对注解进行处理,通过实现插入式注解处理器可以访问和修改抽象语法树中任意元素。如果这些插件在处理注解期间对语法树进行了修改,那么编译器就会重新回到解析及填充符号表的过程重新处理。
以lombok举例:
处理之前:
处理之后:
-
语义分析与字节码生成
- 语义分析
语义分析就是从结构和规则上对源代码进行检查,包括声明检查和类型检查等等。
标注检查: 变量使用前是否声明、变量类型是否匹配、变量运算是否合理
常量折叠: 如 int a = 1+2 ==> int a = 3;常量折叠是javac编译器对源代码做的极少量的优化措施之一,也是为数不多的编译期对代码进行优化的操作.数据流分析:检验局部变量在使用前是否有确定性赋值、声明有返回值的方法是否有确定性返回值等。 ---final变量不可重复赋值就是在这一步进行检查。
解语法糖:java中的自动拆箱装箱功能、foreach循环功能等,都是为了程序员能够更写出更简洁流程的代码而封装的语法糖。这些语法糖在编译期都会被还原成简单的基础语法。
2.字节码生成
字节码生成即将前面几个步骤生成的信息转换成字节码生成Class文件。
在这个过程中,编译器会自动生成<init>方法和<clinit>方法插入语法树,完成对语法树的遍历和调整。
编译后端(字节码-->机器码)
JIT编译器(just in time compiler 即时编译器)
为了提高热点代码的执行效率,JIT编译器通过在运行时将字节码编译为机器码来提高Java应用程序的性能。
编译对象:
-
被多次调用的方法
-
被多次执行的循环体。
当虚拟机发现某个方法或代码块的运行特别频繁时,就会把这些代码认定为 热点代码。为了提高热点代码的执行效率,在运行时虚拟机将会把这些代码编译成与本地平台相关的机器码,并进行各种层次的优化
AOT编译器(ahead of time compiler 静态提前编译器)
AOT编译器的基本思想是:在程序执行前生成Java方法的本地机器码,以便程序在运行时可以直接使用本地代码。