深入理解JVM—走进Java虚拟机

1、JDK,JRE,JVM之间的关系

仅从传统意义上来看,Sun定义的Java技术体系包括:Java程序设计语言各种平台上的JVMClass文件格式Java API类库来自商业机构和开源社区的第三方Java类库
- JDK全程为Java SE Development Kit(Java开发工具),是用于支持Java程序开发的最小环境,提供了编译和运行Java程序所需的各种资源和工具,包括:Java程序设计语言,Java API类库,JVM。
- JRE全称为Java runtime environment(Java运行环境),是支持Java程序运行的最小环境,包括:Java API类库中的SE API子集,JVM。
- JVM是运行Java程序的核心虚拟机。

他们的关系可以用下图表示:


图 1 Java体系结构

2、Java的发展史

Java之父:詹姆斯·高斯林

1991年,由James Gosling博士领导的绿色计划中产生了Java语言的前生Oak(橡树),用于嵌入式系统,没有成功;
1995年互联网发展,改名为Java,开始火爆,提出Write once ,Run anywhere的原则;
1996年1月 发布JDK1.0,jvm为Sun Classic VM;
1996年5月 首届JavaOne大会;
1997年2月 JDK1.1(内部类、反射、jdbc、javabean、rmi);
1998年 JDK1.2 发布。同时Sun发布了JSP/Servlet、EJB规范,以及将Java分成了J2SE、 J2EE 和J2ME。
2000年5月 JDK1.3 发布,Java Hotspot VM正式发布,成为Java默认的虚拟机
2002年2月 JDK1.4 Struts 、Hibernate、 Spring、 正则表达式 、NIO、 日志 、Xml解析器;
2004年9月 JDK1.5(tiger) 自动装箱拆箱 泛型 注解 枚举 增强for 可变参数 Spring2.X;
2006年 JDK6 JavaSe JavaEE JavaME 提供脚本语言支持 支持http服务器api;
2009年 Oralcel以74亿美元收购Sun,获得了Java商标和最具价值的HotSpot虚拟机。此时,Oracle拥有市场占有率最高的两款虚拟机HotSpot和JRockit,并计划在未来对他们进行整合:HotRockit
2011年 JDK7发布。在JDK 1.7u4中正式启用了新的垃圾回收器G1
2014年 Java8发布, Lambda表达式、 函数式接口 、方法引用 、默认方法 、Stream;
2017年 Java9发布,加入模块化并将G1设置为默认GC,替代CMS。
2018年 Java11发布,LTS版本的JDK,发布了革命性的ZGC。
2019年 JDK12发布,加入RedHat领导开发的Shenandoah GC.

3、Java虚拟机发展史

Sun Classic VM
1996年随着JDK1.0的发布,Java使用了世界上第一款商用虚拟机,只能使用纯解释器(没有JITJust in time编译器)的方法来执行Java代码,在JDK1.4被废弃。

Exact VM
Exact Memory Management 准确式内存管理;编译器和解释器混合工作以及两级即时编译器。

HotSpot VM
HotSpot最初是由一家“Longview Technologies”的小公司设计的,1997年此公司被Sun收购,JDK1.3时,,HotSpot VM成为Java默认的虚拟机。
HotSpot VM的特性正如他的名字一样,它使用了热点代码探测技术
1. 通过计数器找到最具有编译价值的代码,触发即使编译和桟上替换(OSR)
2. 通过编译器和解释器协同工作,在最优化程序响应时间和最佳的性能汇总取得平衡

JRockit
BEA公司(现在已经被Oracle收购)开发,是当前世界上最快的Java虚拟机,专注于服务端应用,全部靠编译器执行(JRockit内部不包含解析器的实现)。

J9
IBM开发 原名:IBM Techn0ology for Java Virtual Machine IT4j。是除HotSpot、JRockit之外目前最有影响的三大商用虚拟机之一。

KVM
kilobyte 简单、轻量、高度可移植,在手机平台运行,运行速度慢。

Azul VM/Liquid VM
高性能的Java虚拟机,在HotSpot基础上改进,专用的虚拟机。

Dalvik VM
* 谷歌开发的,应用于Android系统,并且在Android2.2中提供了JIT
* Dalvik VM只能称作虚拟机,而不能称作“Java虚拟机”,因为他没有遵循Java虚拟机规范,因此不能直接执行Java的class文件
* 采用了寄存器指令集架构而不是栈结构,执行dex(dalvik Executable)文件。

Microsoft JVM
Microsoft 为了在IE3浏览器中支持Java Applets,开发了Microsoft JVM,它只能运行在windows下面。1997年,Sun以商标侵权,不正当竞争罪名指控微软成功后,微软在Windows XP SP3中去掉了Microsoft JVM虚拟机,现在Windows上安装的JDK中都是HotSpot VM。

Taobao VM
* 基于openJDK开发自己的定制版本AlibabJDK,简称AJDK。是整个阿里Java体系的基石。
* 基于OpenJDK 、HotSpot VM深度定制并且开源的高性能服务器版JVM。

4、Java虚拟机的概念

4.1 虚拟机的分类

虚拟机(Virtual Machinel),就是一台虚拟的计算机。它是一款软件,用来执行一系列虚拟计算机指令。大体上,虚拟机可以分为系统虚拟机程序虚拟机
- 系统虚拟机:大名鼎鼎的Visual Box、VMware就属于系统虚拟机,它们完全是对真实计算机的仿真,提供了一个可运行完整操作系统的软件平台。
- 程序虚拟机:典型代表就是Java虚拟机(JVM),它专门为执行单个计算机程序而设计,在Java虚拟机中执行的指令我们称作Java字节码指令。

4.2 Java虚拟机
  • Java虚拟机是一台执行Java字节码的程序虚拟计算机,他拥有独立的运行机制,虽然他叫Java虚拟机,但其运行的字节码指令未必全是有Java语言编译而成,JVM是一个跨语言的平台
  • JVM平台的各种语言可以共享Java虚拟机带来的跨平台性、优秀的垃圾回收,以及可靠的即时编译器。
  • Java技术的核心就是Java虚拟机(JVM),因为所有的Java程序最终运行在JVM内部。
  • JVM的作用:JVM就是二进制字节码的运行环境,负责装载字节码到其内部,解释/编译为对应平台上的机器指令。

  • JVM的特点:一次编译,到处运行;自动内存管理;自动垃圾回收功能……
4.3 JVM的整体结构

上面介绍到JVM是一种用于专门执行单个计算机程序而设计的程序虚拟机,他主要包括类加载子系统、运行时数据区(内存结构)和执行引擎(包括垃圾回收器)三部分组成,关于JVM的组成如下图所示:


图4.3 JVM整体结构示意简图

各部分功能简介
1)类加载子系统负责从文件系统或者网络中加载Class信息,加载的类信息存放于一块称为方法区的内存空间。除了类的信息外,方法区中可能还会存放运行时常量池信息,包括字符串字面量和数字常量(这部分常量信息是Class文件中常量池部分的内存映射)。

2)Java堆在虚拟机启动的时候建立,它是Java程序最主要的内存工作区域。几乎所有的Java对象实例都存放在Java堆中。堆空间是所有线程共享的,这是一块与Java应用密切相关的内存空间。

3)Java的NIO库允许Java程序使用直接内存。直接内存是在Java堆外的、直接向系统申请的内存空间。通常访问直接内存的速度会优于Java堆。因此出于性能的考虑,读写频繁的场合可能会考虑使用直接内存。由于直接内存在Java堆外,因此它的大小不会直接受限于Xmx指定的最大堆大小,但是系统内存是有限的,Java堆和直接内存的总和依然受限于操作系统能给出的最大内存。

4)垃圾回收系统是Java虚拟机的重要组成部分,垃圾回收器可以对方法区、Java堆和直接内存进行回收。其中,Java堆是垃圾收集器的工作重要场所。和C/C++不同,Java中所有的对象空间释放都是隐式的,也就是说,Java中没有类似free()或者delete()这样的函数释放指定的内存区域。对于不再使用的垃圾对象,垃圾回收系统会在后台默默工作,默默查找、标识并释放垃圾对象,完成包括Java堆、方法区和直接内存中的全自动化管理。

5)每一个Java虚拟机线程都有一个私有的Java栈,一个线程的Java栈在线程创建的时候被创建,Java栈中保存着帧信息、局部变量、方法参数,同时和Java方法的调用、返回密切相关。

6)本地方法栈和Java栈非常类似,最大的不同在于Java栈用于Java方法的调用,而本地方法栈则用于Native方法的调用,作为对Java虚拟机的重要扩展,Java虚拟机允许Java直接调用本地方法(通常使用C编写)

7)**PC(Program Counter Register)**也是每一个线程私有的空间,Java虚拟机会为每一个Java线程创建PC。在任意时刻,一个Java线程总是在执行一个方法,这个正在被执行的方法称为当前方法。如果当前方法不是本地方法,PC寄存器就会指向当前正在被执行的指令。如果当前方法是本地方法,那么PC寄存器的值就是undefined

8)执行引擎是Java虚拟机的最核心组件之一,它负责执行虚拟机的字节码,现代虚拟机为了提高执行效率,会使用即时编译技术(JIT)将Java方法编译成机器码后再执行。

4.4 JVM的指令集架构模型

Java编译器输入的指令流基本上是一种基于桟的指令集架构,另外一种指令集架构则是基于寄存器的指令集架构
具体来说,这两种指令集架构之间的区别如下:
* 基于桟的指令集架构的特点
> 1. 设计和实现更简单,适用于资源受限的系统
> 2. 避开了寄存器分配难题,使用零地址指令方式分配;
> 3. 指令流中的指令大部分是零地址指令,其执行过程依赖于操作桟,指令集更小,编译器更容易实现
> 4. 不要需要依赖具体的硬件,可移植性好。

  • 基于寄存器的指令集架构放入特点
  1. 典型的应用是x86的二进制指令集,比如传统的PC以及Android的Davlik虚拟机
  2. 指令集完全依赖硬件,可移植性差
  3. 性能更优秀,指令执行效率高
  4. 花费更少指令去完成一项操作
  5. 在大部分情况下,基于寄存器的架构的指令往往都以一地址指令、二地址指令和三地址指令为主。
4.5 JVM的生命周期

4.5.1 JVM的启动
Java虚拟机的启动是通过引导类加载器(bootstarp class loader)创建一个初始类来完成的,这个类是由虚拟机的具体实现指定的。
4.5.2 JVM的执行
* 一个运行中的JVM有着一个清晰的任务:执行Java程序
* 程序开始执行JVM才启动,程序执行结束JVM就停止
* 执行一个所谓的Java程序的时候,真正在执行的其实是一个Java虚拟机进程

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

留言区

还能输入500个字符