我们Java程序员都知道,Java源代码经过编译后生成.class
字节码文件,而字节码最终会由JVM(Java虚拟机)加载运行。JVM的存在屏蔽了底层硬件和操作系统的细节,能够让我们的程序面向统一的执行架构编程,达到Java所宣称的“Write once, run anywhere”的目的,因此Java程序具有极好的跨平台性。
JVM是一种软件抽象的“计算机”,其内部包含程序模拟的处理器、堆栈、寄存器、指令集等,因此JVM属于一种应用层面的程序虚拟机。实际上,除了Java语言,JVM生态上还产生了许多其他语言,如我们经常听说的Clojure、Scala、Groovy、Kotlin等,这些语言也都能编译为JVM可识别的字节码,并由JVM执行。JVM只是一种规范,其实现有很多种,包括HotSpot、JRockit(已与HotSpot合并)、OpenJ9等,不过我们最常用的还是Oracle的HotSpot虚拟机,目前OracleJDK和OpenJDK均集成了HotSpot。经过二十多年的迭代,JVM上诞生了庞大的生态系统,HotSpot本身也是当下最先进的虚拟执行架构之一。
随着时代的发展,JVM也在进行不断的迭代和优化,本系列笔记和后面介绍的内容也都是以Java8前后的HotSpot虚拟机为例,对JVM内部的架构进行简单介绍,了解这些知识能够让我们在使用Java等语言编程时更加得心应手。
虚拟机(Virtual Machine)就是一台软件实现的虚拟计算机,可以大致分为系统虚拟机和程序虚拟机:
Java虚拟机就属于程序虚拟机,我们的Java源代码经过编译后生成JVM字节码,其中包含类似于汇编指令的JVM指令,Java虚拟机负责加载字节码,并解释执行或是即时编译(JIT)为对应平台上的机器指令执行。
Java虚拟机有3个特点:
HotSpot是目前应用最广泛的Java虚拟机,其结构主要分为类装载子系统、运行时数据区和执行引擎,HotSpot的执行引擎采用了解释执行和即时编译(JIT)并存的架构,具有较好的执行性能。如下图所示,为Java8的HotSpot虚拟机架构。
从上面图可以看出,JVM的内存布局分为如下几部分:
我们知道JVM是支持多线程的,JVM会将每一个线程和操作系统的线程一对一映射。上面我们把内存区分为了两种,前者是线程独有的,后者是线程共享的。
有关JVM架构的每一部分,我们会在后文详细介绍。
除此之外我们还应该知道,JVM规范的指令采用了一种基于操作数栈的指令集架构,此外另一种是基于寄存器的指令集架构(如Android的Dalvik虚拟机)。这两种架构的区别如下:
基于栈的指令集架构:1)设计和实现更简单,能够避开寄存器的分配难题,但执行效率相对低一些 2)采用更短的零地址指令,编译器容易实现,但完成一个操作需要的指令数更多 3)不需要特定硬件支持,可移植性好。
基于寄存器的指令集架构:1)充分利用硬件特性,执行效率更高,但可移植性差 2)指令更长,但完成一个操作需要的指令更少。
虚拟机的启动:JVM的启动是通过引导类加载器(BootStrap Class Loader)创建一个初始类来完成的,这个类是由虚拟机的具体实现指定的。
虚拟机的执行:一个运行中的JVM有着一个清晰的任务:执行Java程序。程序开始执行时它才运行,程序结束时它就停止。执行一个Java程序时,真真正正在执行的是一个叫做Java虚拟机的进程。
虚拟机的退出:虚拟机退出有如下几种情况:1)程序正常结束 2)程序在执行过程中遇到了异常或错误而终止 3)由于操作系统出现错误导致Java虚拟机进程终止 4)某程序调用了Runtime类或System类的exit()
方法,或Runtime类的halt()
方法,并且Java安全管理器也允许了这次操作。