什么是 Java 虚拟机?
JVM 是 Java Virtual Machine(Java 虚拟机)的缩写,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。由一套字节码指令集、一组寄存器、一个栈、一个垃圾回收堆和一个存储方法域等组成。JVM 屏蔽了与操作系统平台相关的信息,使得 Java 程序只需要生成在 Java 虚拟机上运行的目标代码(字节码),就可在多种平台上不加修改的运行,这也是 Java 能够“一次编译,到处运行的”原因。
Java 虚拟机的意义
java 代码是无法直接被机器运行,需要经过
- 通过 javac 命令将 java 代码编译为 Java 虚拟机可以认识的.class 文件(Java 字节码)
- 然后在运行时再由 Java 虚拟机内嵌的解释器将 Java 字节码编译为机器可以运行的机器码。但是常见的 jdk 都提供了 JIT 编译器,JIT 编译器能够在运行时将热点代码编译为机器码,等下次执行到改代码时就可以直接运行。
# 机器码和Java字节码
# 最左列是偏移;中间列是给虚拟机读的机器码;最右列是给人读的代码
0x00: b2 00 02 getstatic java.lang.System.out
0x03: 12 03 ldc "Hello, World!"
0x05: b6 00 04 invokevirtual java.io.PrintStream.println
0x08: b1 return
Java 虚拟机是由 c 语言编写,在现在常见的平台(如 windows、linux、mac)上都提供了软件实现,也就是:“一次编写,到处运行”。 Java 虚拟机还提供了一个托管环境,比如内存管理、垃圾回收等。 Java 虚拟机还提供了动态监测,如数组越界、动态类型、安全权限等。 Java 虚拟机的好处:1.跨平台 2.托管管理 3.简化开发者工作
Java 虚拟机运行 class 文件过程
-
虚拟机会通过类加载器(Class-Loader)加载字节码到 Java 虚拟机中
-
加载后的 Java 类会存放于方法区(Method Area)。实际运行时,虚拟机会执行方法区内的代码。
- 执行时需要将 Java 字节码翻译为机器码。有解释执行和即时编译两种
- 解释执行是在执行时一行一行的翻译给机器,所以无须等待编译。
- 即时编译(JIT)是将一个方法中包含的所有字节码编译成字节码后再执行,但是等下次再执行就无须编译了。
- 现在流行 jdk 默认采用混合模式,先会解释执行字节码,然后将其中反复执行的热点代码,以方法为单位进行即时编译。
-
在运行过程中,每进入一个 Java 方法,虚拟机会在当前现成的 Java 方法栈中生成一个栈针,用来存放局部变量以及字节码的操作数。
-
当推出当前执行的方法时,不管正常还是异常返回 Java 虚拟机都会弹出当前线程的当前栈针,将之舍弃。
Java 虚拟机在运行效率上的优化
即时编译建立在程序符合二八定律的假设上,也就是说百分之二十的代码占据了百分之八十的资源。要优化的部分就是那部分占据小的热点代码,我们将其编译成机器码,提高运行效率。即时编译后的代码理论上可能超过 c++。 HotSpot 内置了 C1、C2 和 Graal 编译器。C1 优化手段相对简单,编译时间也较短,一般用在 GUI 客户端上;C2 优化手段复杂,编译时间较长,一般用在 server 端。 在 Java7 开始,HotSpot 默认采用即时编译的方式,热点方法首先被 C1 编译,而后热点中的热点会进一步被 C2 编译。且为了不干扰程序运行,即时编译是放在额外的编译线程中进行的。 热点代码一般有两种探测方式:1.基于采样的热点探测 2.基于计数器的热点探测。一般采用的都是基于计数器的热点探测。基于计数器的热点探测又分为调用计数器,回边计数器。