问题:

  1. 你知道有那些虚拟机
  2. Java代码执行流程
  3. JVM 的架构模型有哪些?
  4. JVM 的生命周期

目录

  • 概述
  • 简介
  • 字节码
  • 多语言混合编程
  • Java 及 JVM 发展的重大事件
  • 虚拟机和 Java 虚拟机
  • Java 虚拟机的作用

JVM 的整体结构

Java 代码的执行流程

JVM 的架构模型

JVM 的生命周期

JVM 的发展历程

概述

简介

JVM 不属于上层框架技术,而是基础技术。如果我们将核心类库 API 比作数学公式的话,那么 Java 虚拟机就好比是公式的推导过程。

我们编写的程序与框架、JavaAPI、JVM之间的关系如下图:

java 技术 底座支撑 java底层架构_java 技术 底座支撑

计算机本身并不识别高级语言的,高级语言是如何执行的,先解释成机器指令,最后执行。

java 技术 底座支撑 java底层架构_Java_02

Java最著名的一句话“ Write once , run anywhere ”,实打实的说出了 Java 的跨平台性。

Java 是跨平台的语言。那么 JVM 就是跨语言的平台。如何来理解 JVM 是跨语言的平台?就是说 JVM 能执行满足要求的字节码文件,只需要不同的语言提供各自的编译器,编译出来的字节码文件遵循 JVM 规范即可。

随着 Java7 的正式发布,Java 虚拟机的设计者们通过 JSR-292 规范基本实现在 Java 虚拟机平台上运行非 Java 语言编写的程序

Java 虚拟机根本不关心运行在器内部的程序到底是是何种编程语言编写的,它只关心 “ 字节码 ” 文件。也就是说 Java 虚拟机拥有语言无关性,并不会单纯地与 Java 语言 “ 终身绑定 ”,只要其它编程语言的编译结果满足并包含 Java 虚拟机内部指令集、符号表以及其它辅助信息,它就是一个有效的字节码文件,就能够被虚拟机所识别并装载运行。

java 技术 底座支撑 java底层架构_java 技术 底座支撑_03

总结一句话:Java不是最强大的语言,但是JVM是最强大的虚拟机。

从上图我们看到各自语言提供的编译器,这个编译器又叫前端编译器;在我们的 JVM 内部也有解释器和 JIT 即时编译器。

字节码

我们平时说的字节码是 Java 字节码,指的是用 Java 语言编译成的字节码。准确的说,任何能在 JVM 平台上执行的字节码格式都是一样的。所以应该统称为:JVM 字节码。

不同的编译器可以编译出相同的字节码文件,字节码文件也可以在不同的 JVM 上运行。

Java 虚拟机与 Java 语言并没有必然的联系,它只与特定难过的二进制文件格式 class 文件格式所关联, class 文件中包含了 Java 虚拟机指令集( 或者称为字节码、Bytecodes )和符号表,还有一些其它的辅助信息。

多语言混合编程

Java 平台上的多语言混合编程正成为主流,通过特定领域的语言去解决特定领域的问题是当前软件开发应对日趋复杂的项目需求的一个方向。

试想一下,在一个项目之中,并行处理用 Clojure 语言编写,展示层用 JRuby/Rails ,中间层则是 Java ,每个应用层都将使用不同的编程语言来完成,而且接口对每一层的开发者都是透明的, 各种语言之间的交互不存在任何困难,就像使用自己语言的原生 API 一样方便,因为他们最终都运行在一个虚拟机上。

对这些运行于 Java 虚拟机之上、Java 之外的语言,来自系统级的、底层的支持正在迅速增强,以 JSR-292 为核心的一系列项目和功能改进(如 DaVinci Machine 项目 、Nashorn 引擎、InvokeDynamic 指令、java.lang.invoke包等), 推动 Java 虚拟机从 “Java 语言的虚拟机” 向 “多语言虚拟机 ” 的方向发展。

Java 及 JVM 发展的重大事件

  • 1990年,在 sun 公司中,由Patrick Naughton 、MikeSheridan、James Gosling 领导的小组 Green Team ,开发出新的程序语言,命名为Oak,后期命名为 Java 。
  • 1995年,Sun 公司正式发布 Java 和 HotJava 产品,Java 首次公开亮相。
  • 1996年1月23日,Sun Microsystem 发布了 JDK 1.0。
  • 1998年,JDK 1.2 版本发布。同时,Sun 发布了JSP、Servlet、EJB规范,以及将 Java 分成了 J2EE、J2SE、J2ME。这表明了 Java 开始向企业、桌面应用和移动设备应用三大领域挺进。
  • 2000年,JDK 1.3 发布,Java HotSpot Virtual Machine 正式发布,成为 Java 的默认虚拟机。
  • 2002年,JDK 1.4 发布,古老的 Classic VM 退出历史舞台。
  • 2003年年底,Java 平台的 Scala 正式发布,同年 Groovy 也加入了 Java 的阵营。
  • 2004年,JDK 1.5 发布,更名为 JavaSE 5.0 。
  • 2006年,JDK 1.6 发布,同年,Java 开源并建立了 OpenJDK 。顺理成章,HotSpot 虚拟机也成为了 OpenJDK 中的默认虚拟机。
  • 2007年,Java 平台迎来了新伙伴 Clojure 。
  • 2008年,Oracle 收购了 BEA 公司,得到了 JRockit 虚拟机。
  • 2009年,Twitter 宣布把后台大部分程序从 Ruby 迁移到 Scala ,这是 Java 平台的又一次大规模的应用。
  • 2010年,Oracle 收购了 Sun ,获得了 Java 的商标和最具有价值的 HotSpot 虚拟机。此时,Oracle 拥有市场占有率最高的两款虚拟机 HotSpot 和 JRockit ,并计划在未来对它们进行整合:HotRockit 。
  • 2011年,JDK7 发布,在 JDK 1.7u4 中,正式启用了新的垃圾回收器 G1 。
  • 2017年,JDK9 发布,将 G1 设置为默认的 GC ,替代了 CMS。
  • 同年,IBM 的 J9 开源,形成了 Open J9 社区。
  • 2018年,Android 的 Java 侵权判决, Google赔偿 Oracle 计88亿美元,当初收购才花了70多亿美元。
  • 同年,Oracle 宣布 JavaEE 成为历史名词,JDBC、JMS、Servlet 赠予 Eclipse 基金会。
  • 同年 JDK11 发布,LTS 版本的 JDK,发布革命性的 ZGC,调整了 JDK 的授权。
  • 2019年,JDK12 发布,加入 RedHat 领导开发的 Shenandoah GC。
  • 目前,JDK14 已经发布。

虚拟机和 Java 虚拟机

所谓虚拟机(Virtual Machine ),就是一台虚拟的计算机,它是一款软件。分为系统虚拟机和程序虚拟机。

无论是系统虚拟机还是程序虚拟机,在上面运行的软件被限制于虚拟机提供的资源。

Java 虚拟机是一台执行 Java 字节码的虚拟计算机,它拥有独立的运算机制,其运行的 Java 自己吗页未必由 Java 语言编译而成。

JVM 平台的各种语言可以共享 Java 虚拟机带来的跨平台性、优秀的垃圾回收器,以及可靠的即时编译器。

Java 技术的核心就是 Java虚拟机( JVM,Java Virtual Machine)

Java 虚拟机的作用


**Java 虚拟机的作用**:

Java 虚拟机就是二进制字节码的运行环境,负责装载字节码到其内部,解释/编译为对应平台上的机器指令执行。

特点:

  • 一次编译,到处运行
  • 自动内存管理
  • 自动垃圾回收功能。

说明:简单来说就是不需要开发人员手动操作内存和垃圾回收;好处就是降低了内存溢出和内存泄漏的风险;对开发人员只需要将关注点集中到业务。

JVM 所处的位置:

java 技术 底座支撑 java底层架构_JVM_04

JVM 的整体结构

以HotSpot VM 是目前市面上高性能虚拟机的代表之一。它采用解释器与即时编译器并存的架构。

java 技术 底座支撑 java底层架构_java 技术 底座支撑_05

在内存中,多个线程共享方法区和堆。程序计数器和本地方法栈以及虚拟机栈(又叫 Java 栈)这三个是线程私有的。

其中执行引擎充当了将高级语言向机器指令的翻译者,因为机器本身只会执行机器指令。执行引擎中包含解释器和即时编译器以及GC。

Java 代码的执行流程

我们编写完了的 Java 程序,经过编译生成 “ .class 字节码 ” 文件,由虚拟机装载解释运行。首先,“ .class 字节码 ” 文件本身也是与系统无关的,只需要由相应系统的 JVM 即可装载执行。

详细的执行流程,如下图:

java 技术 底座支撑 java底层架构_JVM_06

JVM 的架构模型

Java编译器输入的指令流基本上是一种基于栈的指令集架构,另外一种指令集架构则是基于寄存器的指令集架构

具体来说,这两种架构之间的区别:

基于栈的指令架构的特点:

  • 设计和实现更简单,适用于资源受限的系统
  • 避开了寄存器的分配难题:使用的是零地址指令方式分配
  • 指令流中的指令大部分是零地址指令,其执行过程依赖于操作栈。指令集更小,编译器容易实现
  • 不需要硬件支持(与硬件的耦合度更低),可移植性更好,更好实现跨平台

基于寄存器的指令集架构的特点:

  • 典型的应用就是 x86 的二进制指令集:比如传统的 pc 以及 Android 的 Davlik 虚拟机
  • 指令集架构完全依赖硬件,可移植性差
  • 性能优秀,执行更高效
  • 相比零地址指令集,花费更少的指令去完成一项操作
  • 在大部分情况下,基于寄存器架构的指令集往往都以一地址指令、二地址指令和三地址指令为主,而基于栈的架构的指令集却是以零地址指令为主。

举例说明一下,以 2+3 这样的逻辑操作为例,这两个数字都是5以内,操作指令集更简单:

public static void main(String[] args) {
     int i=2;
     int j=3;
     int k=i+j;
 }
//以上是代码,使用 javap 命令反编译之后可以看到:
public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=4, args_size=1
         0: iconst_2//将常量2推送到栈顶
         1: istore_1//将栈顶int型数值存入第2个本地变量
         2: iconst_3//将常量3推送到栈顶
         3: istore_2//给栈顶int型数值3存入第3个本地变量
         4: iload_1//将第2个本地变量推送到栈顶
         5: iload_2//将第3个本地变量推送到栈顶
         6: iadd//加法
         7: istore_3//将结果int型数值推送到第4个本地变量
         8: return//返回
//大概是这么个意思吧。

而基于寄存器的指令计算流程:

mov eax,2 //将eax寄存器的值设为2
add eax,3 //add 加,寄存器,加的值是3,简单说就是给寄存器的值加上3

可以看出,基于栈的架构的指令更小,零地址指令嘛,8位,更小。但是逻辑操作使用的指令更多。反观基于寄存器机构的指令少很多。

总结:

由于跨平台性的设计,Java 的指令都是根据栈来设计的。不同平台的 CPU 架构不同,所以不能设计为基于寄存器的。优点是跨平台,指令集更小,编译器容易实现;缺点是性能下降,实现同样的功能需要更多的指令。

抛出一个问题:

时至今日,尽管嵌入式平台已经不是 Java 程序的主战场了(准确来说,HotSpot VM 的宿主环境已经不局限于前入职平台了),那么为什么不讲架构方式更改为基于寄存器的架构呢?

我个人的答案是:基于栈的架构的特点,设计和实现更简单,同时可移植性好。那么就牺牲了性能。

JVM 的生命周期

JVM 的生命周期这里暂时介绍虚拟机的启动、执行、退出三个部分。

虚拟机的启动

Java 虚拟机的启动是通过引导类加载器( Bootstrap ClassLoader ) 创建一个初始类( initial class ) 来完成的,这个类是由虚拟机的具体实现指定的。

虚拟机的执行

  • 一个运行中的 Java 虚拟机有着一个清晰的任务:执行 Java 程序。
  • 程序开始执行它才开始运行,程序结束时它就停止。
  • 执行一个所谓的 Java 程序的时候,真正执行的是一个叫做 Java 虚拟机的进程

举例简单说一下:

package com.jvm.study;

public class Test0006 {
    public static void main(String[] args) {
        int i=2;
        int j=3;
        int k=i+j;
        //为了让程序不要快速的结束
        try {
            Thread.sleep(60000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("hello");
    }
}
//输入 jps -l 命令,结果如下:
12000 org.jetbrains.kotlin.daemon.KotlinCompileDaemon
13784 sun.tools.jps.Jps
11916 org.jetbrains.jps.cmdline.Launcher
17884
5052 com.jvm.study.Test0006
//等到屏幕打印 hello 的时候再次查看 jps -l
12000 org.jetbrains.kotlin.daemon.KotlinCompileDaemon
2260 sun.tools.jps.Jps
11916 org.jetbrains.jps.cmdline.Launcher
17884
//可以看出上面的进程号为 5052 就是我们启动的进程。

虚拟机的退出

有如下几种情况:

  • 程序正常执行结束
  • 程序在执行过程中遇到了异常或错误而异常终止
  • 由于操作系统出现错误而导致 Java 虚拟机进程终止
  • 某线程调用 Runtime 类或者 System 类的 exit() 方法,或者 Runtime 类的 halt() 方法,并且 Java 安全管理器也允许这次 exit 或者 halt 操作。
  • 除此之外,JNI ( Java Native Interface )规范描述了用 JNI Invocation API 来加载或者卸载 Java 虚拟机时,Java 虚拟机退出的情况。

JVM 的发展历程

Sun Classic VM

  • 早在1996年 Java1.0 版本的时候,sun 公司发布了一款名为 sun Classic VM 的 Java 虚拟机,它同时也是世界上第一款商用的 Java 虚拟机。JDK 1.4 时完全被淘汰。
  • 这款虚拟机只提供解释器。
  • 如果使用 JIT 编译器,就需要进行外挂。但是一旦使用了 JIT 编译器,JIT 就会接管虚拟机的执行系统。解释器就不再工作。解释器和编译器不能配合工作。
  • 现在 HotSpot 内置了此虚拟机。

这里简单说明一下:解释器是响应快,但是执行效率比较低,是顺序逐行解释执行的。JIT 是即时编译器,将热点和循环使用的代码缓存起来,后面使用效率就很高。

为什么不全部使用即时编译器缓存起来?

因为缓存也需要时间。简单来说就是会感觉一开始卡住了。

Exact VM

  • 为了解决上一个虚拟机的问题,jdk 1.2 时,sun 提供了这个虚拟机。
  • Exact Memory Management :准确式的内存管理。
  • 也可以叫 Non-Conservative / Accurate Memory Management
  • 虚拟机可以知道内存中某个位置的数据具体是什么类型。
  • 具备现代高性能虚拟机的雏形。
  • 热点探测
  • 编译器与解释器混合工作模式
  • 只再 Solaris 平台短暂使用,其它平台上还是 Classic VM。
  • 英雄气短,终被 HotSpot 虚拟机取代。

HotSpot VM

  • HotSpot历史
  • 最初由一家名为 “ Longview Technologies ” 的小公司设计。
  • 1997年,此公司被 sun 收购;当然 sun 公司后来也被甲骨文收购。
  • JDK 1.3 时,HotSpot VM 成为默认虚拟机。
  • 目前 HotSpot 占有绝对的市场地位,陈霸武林。
  • 从服务器、桌面到移动端、嵌入式都有它的应用。
  • 名称中的 HotSpot 指的就是它的热点代码探测技术
  • 通过计数器找到最具编译价值的代码,触发即时编译或栈上替换。
  • 通过编译器与解释器协同工作,再最优化的程序响应时间与最佳执行性能中取得平衡。

市场主流的三大虚拟机包括 HotSpot ,剩下两个分别是 JRockit 和 IBM J9

JRockit

  • 专注于服务端应用
  • 它可以不太关注程序的启动速度,因此 JRockit 内部不包含解释器实现,全部代码都是靠即时编译器编译后执行。
  • 大量的行业基准测试显示,JRockit 是世界上最快的 JVM。
  • 优势:全面的 Java 运行时解决方案组合
  • JRockit 面向延迟敏感性应用解决方案 JRockit Real Time 提供以毫秒或者未秒级别的 JVM响应时间,适合财务、军事指挥、电信网络的需要。
  • MissionControl 服务套件,他是一组以极低的开销来监控、管理和分析生产环境中的应用程序的工具。
  • 2008年,BEA 被 Oracle 收购,所以 JRockit 现在属于 Oracle
  • Oracle 表达了整个两大优秀虚拟机的工作,大致再 JDK 8 中完成。整合的方式是在 HotSpot 的基础上,移植 JRockit 的优秀特性。
  • 高斯林:目前就职于谷歌,研究人工智能和水下机器人。

IBM J9

  • 市场定位与 HotSpot 接近,服务器端、桌面级、嵌入式等多用途的 VM
  • 广泛使用再 IBM 的各种 Java 产品。
  • 也号称世界上最快的 Java 虚拟机。2017年左右,IBM 将其开源了,命名为 Open J9,交给了 Eclipse 基金会管理,也成为 Eclipse Open J9。

这三大虚拟机,方法区就是针对 HotSpot 才有,JRocket 和 J9 就没有

其它虚拟机

KVM 、 CDC / CLDC HotSpot( Oracle 在 Java ME 产品显示的两款虚拟机)、Azul VM、Liquid VM、Apache Harmony、Microsoft JVM、TaobaoJVM

2018年4月,Oracle Labs 公布了 Graal VM ,号称 “ Run Programs Faster Anywhere ” 与1995年 Java 的 “ Write once , run anywhere ” 遥相呼应,如果 HotSpot要被淘汰,应该就是它了。