文章目录

  • 为什么需要 JVM?它处在什么位置?
  • JVM 和操作系统的关系
  • JVM、JRE、JDK的关系
  • Java 虚拟机规范和 Java 语言规范的关系
  • 我们写的 Java 代码到底是如何运行起来的
  • 小结



为什么需要 JVM?它处在什么位置?

面试题:

  • 为什么 Java 研发系统需要 JVM?
  • 你对 JVM 的运行原理了解多少?
  • 我们写的 Java 代码到底是如何运行起来的?

想要弄清楚这些问题,我们首先需要从这三个维度去思考:

  • JVM 和操作系统的关系?
  • JVM、JRE、JDK 的关系?
  • Java 虚拟机规范和 Java 语言规范的关系?

JVM 和操作系统的关系

在武侠小说中,想要炼制一把睥睨天下的宝剑,是需要下一番功夫的。除了要有上等的铸剑技术,还需要一鼎经百炼的剑炉,而工程师就相当于铸剑的剑师,JVM 便是剑炉。

java中为什么要创建抽象类 java为什么需要jvm_字节码

JVM 全称 Java Virtual Machine,也就是我们耳熟能详的 Java 虚拟机。它能识别 .class后缀的文件,并且能够解析它的指令,最终调用操作系统上的函数,完成我们想要的操作。

java中为什么要创建抽象类 java为什么需要jvm_java中为什么要创建抽象类_02

一般情况下,使用 C++ 开发的程序,编译成二进制文件后,就可以直接执行了,操作系统能够识别它;但是 Java 程序不一样,使用 javac 编译成 .class 文件之后,还需要使用 Java 命令去主动执行它,操作系统并不认识这些 .class 文件。

Java 是一门抽象程度特别高的语言,提供了自动内存管理等一系列的特性。这些特性直接在操作系统上实现是不太可能的,所以就需要 JVM 进行一番转换。

你可以把 JVM 认为是一个翻译器,会持续不断的翻译执行 Java 字节码,然后调用真正的操作系统函数,这些操作系统函数是与平台息息相关的。

java中为什么要创建抽象类 java为什么需要jvm_java中为什么要创建抽象类_03

从图中可以看到,有了 JVM 这个抽象层之后,Java 就可以实现跨平台了。JVM 只需要保证能够正确执行 .class 文件,就可以运行在诸如 Linux、Windows、MacOS 等平台上了。

现在的一些 JVM 的扩展语言,比如 Clojure、JRuby、Groovy 等,编译到最后都是 .class 文件,Java 语言的维护者,只需要控制好 JVM 这个解析器,就可以将这些扩展语言无缝的运行在 JVM 之上了。

用一句话概括 JVM 与操作系统之间的关系:JVM 上承开发语言,下接操作系统,它的中间接口就是字节码。

java中为什么要创建抽象类 java为什么需要jvm_JAVA_04

JVM、JRE、JDK的关系

JVM 是 Java 程序能够运行的核心。但是需要注意,JVM 自己什么也干不了,你需要给它提供生产原料(.class 文件)。

java中为什么要创建抽象类 java为什么需要jvm_JAVA_05

仅仅是 JVM,是无法完成一次编译,处处运行的。它需要一个基本的类库,比如怎么操作文件、怎么连接网络等。而 Java 体系很慷慨,会一次性将 JVM 运行所需的类库都传递给它。JVM 标准加上实现的一大堆基础类库,就组成了 Java 的运行时环境,也就是我们常说的 JRE(Java Runtime Environment)。

对于 JDK 来说,就更庞大了一些。除了 JRE,JDK 还提供了一些非常好用的小工具,比如 javac、java、jar 等。它是 Java 开发的核心,让外行也可以炼剑!

JDK>JRE>JVM

java中为什么要创建抽象类 java为什么需要jvm_java中为什么要创建抽象类_06

Java 虚拟机规范和 Java 语言规范的关系

我们通常谈到 JVM,首先会想到它的垃圾回收器,其实它还有很多部分,比如对字节码进行解析的执行引擎等。广义上来讲,JVM 是一种规范,它是最为官方、最为准确的文档;狭义上来讲,由于我们使用 Hotspot 更多一些,我们一般在谈到这个概念时,会将它们等同起来。

java中为什么要创建抽象类 java为什么需要jvm_Java_07

左半部分是 Java 虚拟机规范,其实就是为输入和执行字节码提供一个运行环境。右半部分是我们常说的 Java 语法规范,比如 switch、for、泛型、lambda 等相关的程序,最终都会编译成字节码。而连接左右两部分的桥梁依然是 Java 的字节码。

我们写的 Java 代码到底是如何运行起来的

public class HelloWorld {

    public static void main(String[] args) {

        System.out.println("Hello World");

    }

}

使用 JDK 的工具 javac 进行编译后,会产生 HelloWorld 的字节码。

0 getstatic #2 <java/lang/System.out>

3 ldc #3 <Hello World>

5 invokevirtual #4 <java/io/PrintStream.println>

8 return

Java 虚拟机采用基于栈的架构,其指令由操作码(opcode)和操作数组成。 这些字节码指令,就叫作 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 会翻译这些字节码,它有两种执行方式。常见的就是解释执行;另外一种执行方式就是即时编译(JIT)

所谓解释执行就是边翻译为机器码边执行,而即时编译就是先将一个方法中的所有字节码全部编译成机器码之后再执行。前者不需要等待编译,翻译一部分就可以执行一部分,而后者在编译完成后,实际的运行速度更快,在HotSpot中默认采用混合模式,其先解释执行字节码,然后将其中的热点代码(多次执行,循环等)直接编译成机器码,下次就不用再编译了,让其更快速地运行。

java中为什么要创建抽象类 java为什么需要jvm_字节码_08

小结

  • 为什么 Java 研发系统需要 JVM?
    JVM 解释的是类似于汇编语言的字节码,需要一个抽象的运行时环境。同时,这个虚拟环境也需要解决字节码加载、自动垃圾回收、并发等一系列问题。JVM 其实是一个规范,定义了 .class 文件的结构、加载机制、数据存储、运行时栈等诸多内容,最常用的 JVM 实现就是 Hotspot。
  • 你对JVM 的运行原理了解多少?
    JVM 的生命周期是和 Java 程序的运行一样的,当程序运行结束,JVM 实例也跟着消失了。JVM 处于整个体系中的核心位置,关于其具体运行原理,在下一章节中详细介绍。
  • 我们写的 Java 代码到底是如何运行起来的?
    一个 Java 程序,首先经过 javac 编译成 .class 文件,然后 JVM 将其加载到元数据区,执行引擎将会通过混合模式执行这些字节码。执行时,会翻译成操作系统相关的函数。JVM 作为 .class 文件的黑盒存在,输入字节码,调用操作系统函数。
    过程如下:Java 文件->编译器>字节码->JVM->机器码。