JVM 全称 Java Virtual Machine,也就是咱们耳熟能详的 Java 虚拟机。它能识别 .class后缀的文件,而且可以解析它的指令,最终调用操做系统上的函数,完成咱们想要的操做。html
通常状况下,使用 C++ 开发的程序,编译成二进制文件后,就能够直接执行了,操做系统可以识别它;可是 Java 程序不同,使用 javac 编译成 .class 文件以后,还须要使用 Java 命令去主动执行它,操做系统并不认识这些 .class 文件。java
你可能会想,咱们为何不能像 C++ 同样,直接在操做系统上运行编译后的二进制文件呢?而非要搞一个处于程序与操做系统中间层的虚拟机呢?数组
这就是 JVM 的过人之处了。你们都知道,Java 是一门抽象程度特别高的语言,提供了自动内存管理等一系列的特性。这些特性直接在操做系统上实现是不太可能的,因此就须要 JVM 进行一番转换。浏览器
有了上面的介绍,咱们就能够作以下的类比。
JVM:等同于操做系统;
Java 字节码:等同于汇编语言。网络
若是你仍是对上面的介绍有点模糊,能够参考下图:
从图中能够看到,有了 JVM 这个抽象层以后,Java 就能够实现跨平台了。JVM 只须要保证可以正确执行 .class 文件,就能够运行在诸如 Linux、Windows、MacOS 等平台上了。架构
而 Java 跨平台的意义在于一次编译,到处运行,可以作到这一点 JVM 功不可没。好比咱们在 Maven 仓库下载同一版本的 jar 包就能够处处运行,不须要在每一个平台上再编译一次。并发
如今的一些 JVM 的扩展语言,好比 Clojure、JRuby、Groovy 等,编译到最后都是 .class 文件,Java 语言的维护者,只须要控制好 JVM 这个解析器,就能够将这些扩展语言无缝的运行在 JVM 之上了。oracle
咱们用一句话归纳 JVM 与操做系统之间的关系:JVM 上承开发语言,下接操做系统,它的中间接口就是字节码。函数
而 Java 程序和咱们一般使用的 C++ 程序有什么不一样呢?这里用两张图进行说明。
对比这两张图能够看到 C++ 程序是编译成操做系统可以识别的 .exe 文件,而 Java 程序是编译成 JVM 可以识别的 .class 文件,而后由 JVM 负责调用系统函数执行程序。工具
JVM 是 Java 程序可以运行的核心。可是须要注意,JVM 本身什么也干不了,你须要给它提供生产原料(.class 文件)。俗语说的好,巧妇难为无米之炊。它虽然功能强大,但仍须要为它提供 .class 文件。
仅仅是 JVM,是没法完成一次编译,到处运行的。它须要一个基本的类库,好比怎么操做文件、怎么链接网络等。而 Java 体系很慷慨,会一次性将 JVM 运行所需的类库都传递给它。JVM 标准加上实现的一大堆基础类库,就组成了 Java 的运行时环境,也就是咱们常说的 JRE(Java Runtime Environment)。
有了 JRE 以后,咱们的 Java 程序即可以在浏览器中运行了。你们能够看一下本身安装的 Java 目录,若是是只须要执行一些 Java 程序,只须要一个 JRE 就足够了。
对于 JDK(Java Development Kit) 来讲,就更庞大了一些。除了 JRE,JDK 还提供了一些很是好用的小工具,好比 javac、java、jar 等。它是 Java 开发的核心。
JVM、JRE、JDK 它们三者之间的关系,能够用一个包含关系表示。
JDK>JRE>JVM
这里的 Java 程序是文本格式的。好比下面这段 HelloWorld.java,它遵循的就是 Java 语言规范。其中,咱们调用了 System.out 等模块,也就是 JRE 里提供的类库。
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello World");
}
}
使用 JDK 的工具 javac 进行编译后,会产生 HelloWorld 的字节码。
咱们一直在说 Java 字节码是沟通 JVM 与 Java 程序的桥梁,下面使用 javap 来稍微看一下字节码到底长什么样子。
0 getstatic #2 <java/lang/System.out>
3 ldc #3 <Hello World>
5 invokevirtual #4 <java/io/PrintStream.println>
8 return
Java 虚拟机采用基于栈的架构,其指令由操做码和操做数组成。这些字节码指令,就叫做 opcode。其中,getstatic、ldc、invokevirtual、return 等,就是 opcode,能够看到是比较容易理解的。
咱们继续使用 hexdump 看一下字节码的二进制内容。与以上字节码对应的二进制,就是下面这几个数字(能够搜索一下)。
b2 00 02 12 03 b6 00 04 b1
咱们能够看一下它们的对应关系。
0xb2 getstatic 获取静态字段的值
0x12 ldc 常量池中的常量值入栈
0xb6 invokevirtual 运行时方法绑定调用方法
0xb1 return void 函数返回
opcode 有一个字节的长度(0~255),意味着指令集的操做码个数不能操做 256 条。而紧跟在 opcode 后面的是被操做数。好比 b2 00 02,就表明了 getstatic #2 <java/lang/System.out>。
JVM 就是靠解析这些 opcode 和操做数来完成程序的执行的。当咱们使用 Java 命令运行 .class 文件的时候,实际上就至关于启动了一个 JVM 进程。
而后 JVM 会翻译这些字节码,它有两种执行方式。常见的就是解释执行,将 opcode + 操做数翻译成机器代码;另一种执行方式就是 JIT,也就是咱们常说的即时编译,它会在必定条件下将字节码编译成机器码以后再执行。
这些 .class 文件会被加载、存放到 metaspace 中,等待被调用,这里会有一个类加载器的概念。
而 JVM 的程序运行,都是在栈上完成的,这和其余普通程序的执行是相似的,一样分为堆和栈。好比咱们如今运行到了 main 方法,就会给它分配一个栈帧。当退出方法体时,会弹出相应的栈帧。你会发现,大多数字节码指令,就是不断的对栈帧进行操做。
而其余大块数据,是存放在堆上的。Java 在内存划分上会更为细致。
最后你们看下面的图,其中 JVM 部分,就是咱们的要点。
既然 JVM 只是一个虚拟机规范,那确定有很是多的实现。其中,最流行的要数 Oracle 的 HotSpot。
目前,最新的版本是 Java13(注意最新的LTS版本是11)。
为了完成这个过程,你能够打开浏览器,输入下载网址(https://www.oracle.com/techne... javase/downloads/jdk13-downloads-5672538.html)并安装软件。固然你也能够用稍低点的版本,可是有些知识点会有些许差别。
为何 Java 研发系统须要 JVM?
JVM 解释的是相似于汇编语言的字节码,须要一个抽象的运行时环境。同时,这个虚拟环境也须要解决字节码加载、自动垃圾回收、并发等一系列问题。JVM 实际上是一个规范,定义了 .class 文件的结构、加载机制、数据存储、运行时栈等诸多内容,最经常使用的 JVM 实现就是 Hotspot。
对你 JVM 的运行原理了解多少?
JVM 的生命周期是和 Java 程序的运行同样的,当程序运行结束,JVM 实例也跟着消失了。JVM 处于整个体系中的核心位置。
咱们写的 Java 代码究竟是如何运行起来的?
一个 Java 程序,首先通过 javac 编译成 .class 文件,而后 JVM 将其加载到元数据
区,执行引擎将会经过混合模式
执行这些字节码。执行时,会翻译成操做系统相关的函数。JVM 做为 .class 文件的黑盒存在,输入字节码,调用操做系统函数。
过程以下:Java 文件->编译器>字节码->JVM->机器码。
咱们所说的 JVM,狭义上指的就 HotSpot。如非特殊说明,咱们都以 HotSpot 为准。咱们了解到,Java 之因此成为跨平台,就是因为 JVM 的存在。Java 的字节码,是沟通 Java 语言与 JVM 的桥梁,同时也是沟通 JVM 与操做系统的桥梁。
JVM 是一个很是小的集合,咱们常说的 Java 运行时环境,就包含 JVM 和一部分基础类库。若是加上咱们经常使用的一些开发工具,就构成了整个 JDK。咱们讲解 JVM 就聚焦在字节码的执行上面。
Java 虚拟机采用基于栈的架构,有比较丰富的 opcode。这些字节码能够解释执行,也能够编译成机器码,运行在底层硬件上,能够说 JVM 是一种混合执行的策略。