【Java】JVM 入门

Posted by 西维蜀黍 on 2019-03-06, Last Modified on 2021-09-21

前言

JVMJava Virtual Machine( Java 虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。

JVM屏蔽了与具体操作系统平台相关的信息,使 Java 程序只需生成在 Java 虚拟机上一次编译,多次运行(write once, run anywhere),具有跨平台性JVM 在执行字节码时,实际上最终还是把字节码解释成具体平台上的机器指令执行。

JDK、JRE和JVM对比

JDK

JDK(Java Development Kit) 是 Java 语言的软件开发工具包(SDK)。JDKprogramming toolsJREJVM 的一个集合。

JRE

JRE(Java Runtime Environment)Java 运行时环境,JRE 是物理存在的,主要由 Java APIJVM 组成,提供了用于执行 Java 应用程序最低要求的环境。

JVM

JVM 是一种用于计算设备的规范,它是一个虚构的计算机的软件实现,简单的说,JVM 是运行字节码(Byte Code)程序的一个容器。

HotSpot VM

HotSpot VM 是 Sun JDK 和 OpenJDK 中所带的虚拟机,也是目前使用范围最广的 Java 虚拟机。

JVM 的特点

  • 基于堆栈的虚拟机:最流行的计算机体系结构,如英特尔 X86 架构和 ARM 架构上运行基于 寄存器。比如,安卓的 Davilk 虚拟机就是基于 寄存器 结构,而 JVM 是基于栈结构的。
  • 符号引用(symbolic references) :除了基本类型以外的数据 (比如类和接口) ,都是通过符号来引用,而不是通过显式地使用内存地址来引用。
  • 垃圾收集 :一个类的实例是由用户程序创建和垃圾回收自动销毁
  • 网络字节顺序Java class文件用网络字节码顺序来进行存储,保证了小端的Intel x86架构和大端的RISC系列的架构之间的无关性。

JVM 字节码(JVM Byte Code)

JVM 使用 Java 字节码的方式,作为 Java 用户语言机器语言 之间的中间语言。实现一个通用的机器无关 的执行平台。

类文件格式

有趣的是,其实JVM并不关心Java语言或其他编程语言的语义和语法结构。当JVM执行一段程序的时候,它主要关注的是一种称为“类文件”的特定文件格式。

.class 类文件格式和 Java 代码定义的面向对象的类结构毫无关系。编译器将 .java 文件编译成 .class 文件,然后 JVM 对*.class文件进行解译,它不关心这个类文件是由哪种编译器生成的,只要符合类文件的文件格式即可。

Java编译器将一段程序编译为等价的类文件。这些类文件实际上包含了半编译的代码——字节码(byte code)。

之所以称之为半编译,是因为字节码并不像C/C++编译器编译的与机器指令相关的二进制文件一样可以被被直接执行。

字节码要先被输入到 JVM 中,然后再转换为底层平台可以执行的最终指令。所以字节码包含了JVM的指令、符号表和其他的辅助信息。不管何种语言,能根据JVM的语法和结构约束编译生成字节码的编译器,都是一个可以在JVM上执行的候选者。

JVM的定位

JVM将自身定位于字节码和底层平台之间。底层平台是指操作系统(OS)和硬件。

操作系统和硬件体系结构在不同的机器上可能不同,但是同一段Java程序可以不用做任何的代码修改就能在不同的机器上运行。这是在虚拟环境中执行的程序语言的独特之处。

例如,由其他程序语言编译器编译的目标代码如C++和Java相比的不同点在于,C++程序需要被特定平台的编译器重新编译,从而使它能在不同的体系结构上面运行。而Java代码并不需要做任何改变,因为由Java编译器编译的字节码是在外围的JVM上执行。

因此,JVM负责重新解译由Java编译器生成的字节码,并和底层平台协调工作。也就是说,尽管Java编译器生成的结果是平台独立的,但JVM与特定平台相关的。除非两台机器有相同的体系结构,在某个体系结构上安装和使用的 JVM 当被直接复制到另一台机器时,可能就不能正常工作了。

JVM 实例的生命周期

  • 启动:任何一个拥有main方法的class都可以作为JVM实例运行的起点。
  • 运行main函数为起点,程序中的其他线程均有它启动,包括daemon守护线程和non-daemon普通线程。daemonJVM自己使用的线程比如GC线程,main方法的初始线程是non-daemon
  • 消亡:所有线程终止时,JVM实例结束生命。

在JVM上执行Java程序

Java 虚拟机在执行 Java 程序的过程中会把它管理的内存划分成若干个不同的数据区域。

每一个在JRE上运行的Java程序都会创建一个JVM实例。编译后的Java类文件和其他被依赖的类文件会被加载到运行环境中。这一步由类加载器(class loaders)协助完成。

首先,类加载器(class loaders)会以字节码(byte code)的形式,加载程序类文件和与JDK绑定的标准 Java 类文件。标准类文件构成了Java API核心类库。引导程序通过定位通常位于jre/lib目录下的核心API类库启动。

然后,扩展机制定位扩展类库,例如一些为开发或执行代码而被添加到Java里新的(可选)包。扩展类通常位于 jre/lib/ext目录下。有时,扩展类会被放到系统属性java.ext.dirs 定义的其他目录下面。程序包使用JAR或ZIP的扩展名。

最后,如果要加载的类没有在Java的标准类库或扩展类库中被找到,加载器会搜索CLASSPATH环境变量下定义的文件路径,CLASSPATH里面包含了诸多存储类文件的地址。系统属性java.class.path对CLASSPATH环境变量做了映射。

像JAR或ZIP这样的归档文件都是包含了一些其他文件目录的独立文件,通常是压缩文件格式。例如,程序中使用的标准类库包含在归档文件 rt.jar中,该文件会和JDK被一同安装。

一旦文件被定位并加载之后,类加载器会执行不同的功能,例如根据JVM的约束进行校验、内存分配,或者在调用构造器设置定义的变量元素之前使用默认值初始化类变量。

当加载程序结束之后,字节码指令被传递给执行引擎。然后JVM借助于绑定到指定平台的特定JVM实现的本地代码和底层操作系统进行交互。请注意,不同平台的实现可能有略微不同。

Reference