好好学Java:从零基础到项目实战
上QQ阅读APP看书,第一时间看更新

1.3.2 JVM的体系结构

Java虚拟机这个名称听起来很高端,其实它不过是一套程序包而已,这些程序构成了系统平台里面的Java运行环境。在Java编程领域,人们更习惯将Java虚拟机称作JVM,免得产生误解。

JVM主要分为5大模块,包括类加载器、运行时数据区、执行引擎、本地方法接口和垃圾收集模块,简要说明如下:

1.类加载器

顾名思义,类加载器用于加载Java的类文件(class文件),也就是把一个或若干个class文件读到内存中,并为其指定唯一标识,以便外部访问class文件内部的数据与接口。

2.运行时数据区

数据区存放着Java程序在运行时需要的各种数据空间,包含栈区、堆区、PC寄存器、本地方法栈、方法区5类数据,分别说明如下。

  • 栈区:存放当前正在执行的方法所需的各项数据,包括基本类型变量、对象的引用、方法的返回地址等。每个线程都拥有自己的栈区,且栈内数据不可被其他线程访问。
  • 堆区:存放程序用到的所有对象信息,堆区里的数据可被程序的所有线程共享。
  • PC寄存器(程序计数寄存器):注意此PC指的是Program Counter(程序计数器),而非Personal Computer(个人计算机)。JVM支持多线程运行,每个线程都拥有自己的PC寄存器,寄存器中保存着当前正在执行的指令地址。
  • 本地方法栈:这是一个特殊的栈区,用于同本地方法接口之间的数据交互。
  • 方法区:主要存放已经被虚拟机加载的类信息、常量、静态变量、即时编译后的代码等  数据。

其中,栈区、堆区、方法区存放的数据是有关联的。每段Java代码定义了各自的类,这些类信息就保存在方法区;一个类可以创建多个对象,这些对象信息就保存在堆区;每个对象又会被多次用到,每次使用的时候,该对象的引用就保存在栈区。但不同版本的JDK对字符串常量的处理方式并不一样,在JDK 1.7之前,字符串常量保存在方法区,而在JDK 1.7之后,字符串常量改为保存到堆区了。

3.执行引擎

因为JVM采用自己的一套指令系统,所以JVM通过执行引擎来运行字节码。不同JVM采用的执行技术不尽相同,常见的主要有3种,分别是解释、即时编译和自适应优化,简要介绍如下。

  • 解释:第一代JVM采用解释执行,它将字节码解释为本地系统所能理解的机器码。由于在运行时才一条一条解释成机器码,因此该方式的执行效率较低。
  • 即时编译:即时编译(Just-In-Time Compilation,JIT)属于第二代JVM。当JVM发现某个代码块会特别频繁的运行时,就把该代码块全部编译成机器码,再执行编译好的这段机器码。通过将多次解释减少到一次编译,从而提高程序的运行效率。
  • 自适应优化:该方式在即时编译的基础上继续改进,首次运行时对所有代码都采取解释执行,接着监视代码的执行情况,对那些经常调用的方法启动一个后台线程,将其编译为机器码并加以优化。如果某个方法不再频繁使用,就取消它对应的编译代码,恢复对其解释执行。

4.本地方法接口

本地方法接口(Java Native Interface,JNI)允许JVM上的程序调用本地程序和类库,或者被对方调用。这些本地程序和类库由其他语言编写,例如C语言、C++语言或汇编语言。通过JNI的协助,JVM能够让Java程序调用已经存在的本地支持库,而无须关心底层的实现过程,这类本地服务包括但不限于:文件的输入输出、图像的加工处理、音视频的录制与播放等。

5.垃圾收集模块

因为任何设备的内存都是有限的,所以JVM提供了垃圾收集机制,也就是回收那些已经不用的对象,把它们占用的内存空间释放出来重新利用。譬如租房子,等到原来的房客离去之后,管家就要重新收拾这个房子,打扫干净再去招徕新的房客。

总结一下,JVM各模块的体系结构及其相互关系如图1-38所示。

图1-38 JVM各模块的体系结构