每个JAVA开发者都知道字节码将会被JRE(java运行时环境)所执行,但是许多人不知道的是JREjava虚拟机(JVM)分析字节码,解释代码,以及执行它的那部分实现。对于一个开发者来说知道了解JVM的架构是非常重要的,因为它可以让我们更有效地编写代码。在这篇文章,我们将更深入的了解java中的JVM架构以及JVM的不同组成部分

原文地址:https://dzone.com/articles/jvm-architecture-explained


JVM架构简析

一个JVM不同组成部分的综述,以及和其相关的一些图表

20181119154259959133032.png

什么是JVM?

一个虚拟机是一个物理机的软件实现。java开发的概念是WORA(一次编写到处运行),所以运行在虚拟机上。编译器编译java文件生成.class文件。然后.class文件被放入到一个可以加载执行他的虚拟机中。下面是一个JVM 架构的图。

JVM是如何工作的?

在上述的架构图中,JVM被分成了三个主要的子系统:

  1. 类加载子系统。
  2. 运行时数据区域。
  3. 执行引擎。

1. 类加载子系统

java的动态类加载机制在类加载子系统中被处理。由它加载,连接,以及在运行时第一次引用类时初始化类文件,而不是在编译时

1.1. 加载

类们将会被这个组件加载。BootStrap 类加载器 ,Extension 类加载器,和Application类加载器将会帮助完成这个过程。

  1. BootStrap类加载器 - 它的责任是加载那些启动相关路径里的类,不仅仅只是rt.jar。最高的优先权被给于给这个加载器。

  2. Extension类加载器 - 它的责任是加载那些包含在jre/lib下的ext文件夹内的类。

  3. Application类加载器 - 他的责任是加载用户定义的classpath下以及路径涉及的环境变量等等里面的类。

上述的类加载器都遵循双亲委派模型去加载类文件。

1.2 连接
  1. 验证 - 字节码验证器将会验证生成的字节码是否正确,如果验证失败那么我们将得到一个验证错误。

  2. 准备 - 为所有静态变量分配内存并且设置初始值。

  3. 解析 - 所有的符号引用将会被替换成方法区的直接引用。

1.3 初始化

这是类加载的最后时期,这时候所有的静态变量都会被赋予初值,并且执行类中的静态块。

2. 运行时数据区

运行时数据区被分成了五个主要部分:

  • 方法区 - 所有类相关数据将会被存储在这里,包括静态变量。每一个JVM只有一个方法区,所以它中的资源是被共享的。
  • - 所有的对象以及他们相关的实例化变量和数组被存储在这里。每一个JVM也只有一个堆。因为方法区是线程间共享的,因此数据的存储不是线程安全的。
  • - 对于每个线程,都将创建一个单独的运行时栈。对于每一个方法调用,一个被叫做栈桢的实体将会在栈内存中被创建。所有的局部变量将会在栈空间被创建。栈空间是线程安全的因为它不共享资源。栈桢被分成如下三个子部分:
    • 局部变量表 - 涉及方法的所有本地变量以及和他相关的值被储存在这里。
    • 操作数栈 - 如果需要执行任何中间操作,操作数栈扮演者运行时工作空间的作用去执行这些操作。
    • 桢数据 - 该方法对应的所有符号都存储在这里。在任何异常情况下,捕获块信息将在帧数据中维护。
  • 程序计数器 - 每一个线程都有一个独立的程序计数器,来保存当前指令地址,一旦指令执行完成就更新程序计数器为下一条指令地址。
  • 本地方法栈 - 本地方法栈保存本地方法信息。对于每一个线程,一个独立的本地方法栈将会被创建。

3. 执行引擎

分配到运行时数据区的字节码会被执行引擎执行。执行引擎读取字节码并逐步的执行。

  • 解释器 - 解释器可以很快的解释字节码,但是却执行的很慢。解释器的一个缺点就是当一个方法被调用多次后,每次都需要重新被解释。
  • 实时编译器 - 实时编译器弥补了解释器的不足。执行引擎将在转换字节代码时使用解释器的帮助,但是当它发现重复的代码时,它使用的是JIT编译器,它编译整个字节码并将其更改为本地代码。这种本地方法将会直接被使用在重复的方法调用上,来提升系统的性能。

    • 中间代码生成器 - 生成中间代码。
    • 代码优化器 - 负责优化上面生成的中间代码。
    • 目标代码生成器 - 负责生成机器代码或本地代码
    • 分析器 - 一个特殊的组件,负责找热点,例如,是否一个方法被多次调用等等。
  • 垃圾收集器 - 收集以及移除未被引用的对象。垃圾回收将会被这个”System.gc()“调用触发,但是这个执行时不确定的。JVM的垃圾收集器收集被创建的对象。

Java Native Interface (JNI): JNI将与本地方法库交互,并提供执行引擎所需的本地库。
本地方法库:它是执行引擎所需的本地库的集合。