JVM概述
目录
当我们开始讨论 JVM 时表示我们已经开始研究 Java 程序运行的底层原理,因此让我们先从程序的运行开始,了解一个程序是如何运行起来的,由此引出 Java 一直倡导的口号:一次编译,到处运行,最终介绍 Java 为了能够实现它的口号,必须做出其他语言不同的东西——JVM
¶程序的运行
对于电脑而言,它只认识一种语言,也就是 0101 序列所组合而成的指令,当使用的是 C/C++等之类的高阶语言编写代码时,由于计算机并不能理解高级语言本身,所以需要编译器将高级语言编译成机器语言,提供给计算机执行
¶高级语言的运行过程
通过编译器可以将高级语言编译为在特定系统上运行的机器码指令, 提供给机器运行,如果同一份高级语言程序想要在不同的操作系统上运行,需要不同的编译器编译相同的高级语言
由于每个平台的特性不同,可引用的函数程序库(Library)不同,也许代码还得作修改,才可以在另一个平台上编译执行。这很不方便,恰逢此时 Java 提出了自己惊人的口号:write once,run anywhere
¶Java 语言的运行过程
❓同样作为高级语言的Java
是如何做到一次编译,处处执行?
Java 程序能够在不同平台中运行的主要原因如下图:
Java 也是高级语言,要让电脑执行编写的应用程序,仍然需要编译器的编译功能。不过,Java 编译时,并不直接翻译为相依于某平台的 0101 指令,而是先翻译为一个中间格式的字节码(byte code),Java 的原始代码文件格式名为*.java
,经过编译器翻译过后,会变成*.class
的字节码文件。
✨如果想要执行这个字节码文件,目标平台上必须安装有 JVM,JVM 会将字节码翻译为相应平台支持的语言,这样借助于 JVM 中间层的力量,Java 编写的代码就可以做到一次编译,运行在各处
🎶JVM 就是一个处于程序与操作系统中间的另一个程序,它可以将.class
文件中的指令,转换为对应底层操作系统的机器指令并发起系统调用,让操作系统执行这些执行,这样避免了 Java 语言和操作系统的强耦合
🎶设计 JVM 非常重要的几个理念
- 对于 Java 程序而言,它仅和 JVM 耦合,与真正的物理操作系统没有任何耦合关系
- 对于 JVM 而言,字节码是它的可执行文件,不认识其他任何文件
- Java 程序的运行并不会关注自己在那个物理机器上运行,只关注自己在那个 JVM 上运行
- JVM 完成与底层操作系统的交互
¶JVM 和操作系统的关系
✨JVM 的特点
- JVM 全称 Java Virtual Machine,常称之为 Java 虚拟机,用来模拟通用的计算机,有着一套虚拟的完善的硬件架构,如处理器、堆栈、寄存器等,同时还具有相应的指令系统
- 它能识别
.class
后缀的文件,并且能够解析它的指令,最终调用操作系统上的函数,执行程序需要进行的功能 - JVM 上承开发语言,下接操作系统,它的中间接口就是字节码
JVM 与操作系统的类比
JVM | 操作系统 |
---|---|
Java 字节码 | 操作系统中的汇编语言 |
¶JVM——跨语言的平台
Java 是目前应用最广泛的软件开发平台之一,随着 Java 以及 Java 社区的不断壮大 Java 也早已不是简简单单的一门计算机语言,它是一个平台、一种文化、一个社区
- 作为一个平台,Java 虚拟机扮演着举足轻重的作用
- 作为灯种文化,Java 几乎成为了“开源”的代名词。
- 作为一个社区,Java 拥有全世界最多的技术拥护者和开源社区支持,有数不清的论坛和资料。从桌面应用软件、嵌入式开发到企业级应用、后台服务器、中间件,都可以看到 Java 的身影。其应用形式之复杂、参与人数之众多也令人咋舌。
如今的 Java 虚拟机,将每个语言都转换成字节码文件,最后转换的字节码文件都能通过 Java 虚拟机进行运行和处理,实现 Java 虚拟机平台上运行非 Java 语言编写的程序
Java 虚拟机根本不关心运行在其内部的程序到底是使用何种编程语言编写的,它只关心字节码文件。也就是说 Java 虚拟机拥有语言无关性,并不会单纯地与 Java 语言进行终身绑定,只要其他编程语言的编译结果满足并包含 Java 虚拟机的内部指令集、符号表以及其他的辅助信息,它就是一个有效的字节码文件,就能被虚拟机所识别并装载运行
¶字节码
✨字节码特点:
- Java 字节码指的是用 Java 语言编译成的字节码。准确的说任何能在 JVM 平台上执行的字节码格式都是一样的,所以应该称为:JVM 字节码
- 不同的编译器,可以编译出相同的字节码文件,字节码文件也可以在不同的 JVM 上运行
- Java 虚拟机与 Java 语言并没有必然的联系,它只与特定的二进制文件格式
.class
文件格式所关联,Class 文件中包含了 JVM 指令集和符号表,还有一些其他辅助信息
¶虚拟机与 Java 虚拟机
¶虚拟机
虚拟机(Virtual Machine
),是一台虚拟的计算机,它是一种软件,用来执行一系列虚拟计算机指令,整体上,虚拟机可以分为系统虚拟机和程序虚拟机
Visual Box、Vmware 就属于系统虚拟机,是对物理计算机的仿真,提供了一个可运行完整操作系统的软件平台
程序虚拟机的典型代表就是 JVM,它专门为执行单个计算机程序而设计,在 Java 虚拟机中执行的指令称之为 Java 字节码指令
无论是系统虚拟机还是程序虚拟机,在上面运行的软件都被限制于虚拟机提供的资源中
¶Java 虚拟机
Java 虚拟机是一台执行 Java 字节码的虚拟计算机,它拥有独立的运行机制,其运行的 Java 字节码也未必由 Java 语言编译而成
JVM 平台的各种语言可以共享 Java 虚拟机带来的跨平台性、优秀的垃圾回收器,以及可靠的即时编译器
Java 虚拟机就是二进制字节码的运行环境,负责装载字节码到其内部,解释/编译为对应平台上的机器指令执行。每一条 Java 指令,JVM 规范中都有详细定义,
✨JVM 特点
- 一次编译,到处运行
- 自动内存管理
- 自动垃圾回收功能
¶Java 整体架构
JVM 是运行在操作系统之上的,它与硬件没有直接的交互
¶JVM 的整体结构
🎶HotSpot VM 是目前市面上高性能虚拟机的代表作之一,它采用解释器与即时编译器并存的架构,早期的 Java 会被诟病各种性能问题,如今,Java 程序的运行性能早已脱胎换骨,已经达到了可以和 C/C++程序一较高下的地步
目前存在两种常见的指令集架构模式:基于栈的指令集架构和基于寄存器的指令集架构
✨java 编译器输入的指令流基本上是一种基于栈的指令集结构,两种不同指令集架构具有的优缺点将在下文分析
¶基于栈架构的特点
✨基于栈的指令集架构特点
- 设计和实现更简单,适用于资源受限的系统
- 避开了寄存器的分配难题,使用零地址指令方式分配
- 指令流中的指令大部分式零地址指令,其执行过程依赖于操作栈,指令集更小,编译器容易实现
- 不需要硬件支持,可移植性更好,更好实现跨平台
¶基于寄存器架构的特点
✨基于寄存去的指令集架构特点
- 典型的应用是 X86 的二进制指令集,指令集架构完全依赖硬件,可移植性差
- 性能优秀和执行更高效
- 花费更少的指令去完成一项操作
- 在大部分情况下,基于寄存器架构的指令集往往都以一地址指令、二地址指令和三地址指令为主,而基于栈式架构的指令集却是以零地址指令为主
¶案例
同时执行 2+3 算数运算,指令分别如下
1 | //基于栈的计算流程 |
1 | // 基于寄存器的计算流程 |
🤔为什么 Java 使用基于栈的指令架构?
- 由于跨平台性的设计,Java 的指令都是根据栈来设计的。不同平台 CPU 架构不同,所以不能设计为基于寄存器的。优点是跨平台,指令集小,编译器容易实现,缺点是性能下降,实现同样的功能需要更多的指令
¶JVM 的生命周期
了解了 JVM 的功能以及整体架构,本小节将介绍 JVM 是什么启动什么时候结束运行的,也称为 JVM 的生命周期管理
¶虚拟机的启动
Java 虚拟机的启动是通过引导类加载器(bootstrap class loader)创建一个初始类(Initial class)来完成的,这个类是由虚拟机的具体实现来指定的,具体这个类叫什么名字没有规定
¶虚拟机的执行
✨Java 虚拟机启动之后,就是要在虚拟机中执行响应的任务,Java 虚拟机执行的任务有以下特点:
- 一个运行中的 Java 虚拟机有一个清晰的任务:执行 Java 程序
- 程序开始执行时它才执行,程序结束时它就停止
- 执行一个所谓的 Java 程序的时候,真真正正在执行的是 Java 虚拟机的进程
¶虚拟机的退出
🤔当 Java 虚拟机中的程序执行完称之后,相应的虚拟机就会退出,那么到底哪些情况会导致一个虚拟机的退出?
- 程序正常执行结束
- 程序在执行过程中遇到了异常或错误而异常终止
- 由于操作系统出现错误而导致 Java 虚拟机进程终止
- 某线程调用
Runtime
类或System
类的exit()
方法,或者Runtime
类的halt()
方法,并且 Java 安全管理器也允许这次exit()
或halt()
操作 - JNI(
Java Native Interface
)规范描述了用 JNI Invocation API 来加载或卸载 Java 虚拟机时,Java 虚拟机的退出情况
¶Java 代码执行流程
对于 JVM 的架构有了初步的了解之后,下图详细描述了一个 Java 程序是如何能够在系统中运行的流程,具体流程细节在之后都会有介绍