【JVM深入理解系列补充1】HotSpot架构

主要组成部分

  1. 类加载器子系统(Class Loader Subsystem):负责加载Java类文件到虚拟机中,并对类文件进行校验、准备、解析和初始化操作。
  2. 运行时数据区(Runtime Data Area):包括方法区、堆、虚拟机栈、本地方法栈和程序计数器等几个部分,用于存储Java程序运行时所需要的各种数据。
  3. 执行引擎(Execution Engine):负责执行编译后的Java字节码指令,将其转化为机器码并执行。
  4. 本地方法接口(Native Interface):允许Java程序调用本地的C/C++函数,实现与操作系统和硬件的交互。
  5. 垃圾回收器(Garbage Collector):负责管理堆内存中的对象,自动回收不再使用的内存空间。

架构图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
     +----------------+
| Application |
+----------------+
|
v
+-------------------+
| Execution Engine |
+-------------------+
|
v
+------------------------+
| Runtime Data Area |
+------------------------+
|
+----------------------+ +----------------------+
| Method Area | | Heap |
+----------------------+ +----------------------+
| |
+----------------------+ +----------------------+
| Native Method | | Garbage Collector |
| Interface (JNI) | | |
+----------------------+ +----------------------+

其中,应用程序位于最上方,其余四个部分分别是:
执行引擎、运行时数据区、本地方法接口和垃圾回收器。
在运行时数据区中,方法区和堆是最重要的两个部分,分别用于存储类信息和对象实例。
执行引擎负责将字节码翻译为机器码并执行,本地方法接口允许Java程序与本地C/C++函数交互,
而垃圾回收器则负责回收不再使用的内存空间,防止内存泄漏。

JVM运行时数据区(JVM内存)

1
2
3
4
5
6
7
8
9
10
11
+------------------------+
| Metaspace |
+------------------------+
| Thread Stacks |
+------------------------+
| Heap |
+------------------------+
| Method Area (PermGen)|
+------------------------+
| Native Memory |
+------------------------+

其中各部分的含义和作用如下:

  1. Metaspace:存储类的元数据,包括类的名称、访问修饰符、字段信息、方法信息等。它会随着应用程序的运行而不断地进行调整,可以通过JVM参数来设置大小。在JDK8及之后的版本,Metaspace已经取代了永久代(PermGen)。
  2. Thread Stacks:每个线程都会有一个独立的线程栈,用于存储该线程所执行的方法信息、局部变量等。线程栈是一个后进先出(LIFO)的数据结构,每个栈帧存储一个方法的信息。线程栈的大小可以通过JVM参数来设置。
  3. Heap:存储对象实例和数组等数据,是Java程序中最大的一块内存区域。Heap可以通过JVM参数来设置大小。
  4. Method Area:存储已经加载的类信息、常量池、静态变量、即时编译器编译后的代码等数据。在JDK8及之前的版本,Method Area被实现为永久代(PermGen)。在JDK8及之后的版本,Metaspace代替了永久代。
  5. Native Memory:JVM使用的本地内存,包括虚拟机内部使用的数据结构和代码、JNI(Java Native Interface)使用的本地代码、操作系统内核等。

Heap区

1
2
3
4
5
6
7
8
9
10
11
12
13
14
+----------------------------------------------------+
| |
| Heap |
| |
| +----------------+ +----------------+ |
| | Eden | | Survivor | |
| | | | space | |
| +----------------+ +----------------+ |
| | | | | |
| | Old Gen | | Perm Gen | |
| | | | | |
| +----------------+ +----------------+ |
| |
+----------------------------------------------------+
  1. 新生代(Young Generation):它是所有Java Heap中占比最大的一部分,它主要用于存放新创建的对象。新生代被进一步细分为一个Eden空间和两个Survivor空间,其中Eden空间是对象的初始分配区域,Survivor空间则是用来保存在Eden空间和Survivor空间之间来回复制的对象。
  2. 老年代(Old Generation):它主要用于存放生命周期较长的对象。在新生代中经历了多次垃圾回收仍然存活的对象会被移动到老年代中。
  3. 永久代(Permanent Generation):它主要用于存储静态的类、方法等信息。
  4. 元空间(Metaspace):从Java 8开始,永久代被替换为了元空间。元空间主要用于存储类的元数据信息。

执行引擎

执行引擎是JVM的核心组成部分之一,它负责解释Java字节码并执行指令。执行引擎通常包含三个主要部分:解码器、解释器和即时编译器(JIT)。

  1. 解码器:解码器负责将字节码文件解码成JVM能够理解的指令流。JVM使用一种基于栈的指令集来执行Java字节码,解码器将字节码文件中的指令转换为栈指令并发送给解释器或JIT进行执行。
  2. 解释器:解释器负责执行Java字节码中的指令。解释器将字节码解释为本地机器指令并在虚拟机上执行。解释器的执行速度较慢,因为它每次执行都需要将字节码解释为本地机器指令。
  3. 即时编译器:即时编译器(JIT)是执行引擎中的一个重要组成部分,它能够将Java字节码编译成本地机器代码并直接在CPU上执行。JIT使用一种称为“热点探测”的技术来确定哪些代码被频繁调用,并对这些代码进行即时编译以提高执行效率。JIT编译的代码通常比解释器执行的代码快几倍甚至更多。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
                    +--------------------+
| Java Bytecodes |
+--------------------+
|
v
+--------------------+
| Bytecode Loader |
+--------------------+
|
v
+---------------------------------------+
| JVM |
+---------------------------------------+
| |
v v
+---------------------+ +---------------------+
| Interpreter | | JIT Compiler |
+---------------------+ +---------------------+
| Interprets bytecode| |Compiles frequently |
| line by line | |executed bytecode to |
| and executes it. | |native machine code. |
+---------------------+ +---------------------+

从图中可以看出,Java字节码首先由字节码加载器加载到JVM中。执行引擎负责解释Java字节码并执行指令。在执行过程中,解释器将字节码解释为本地机器指令并执行,而即时编译器会将Java字节码编译为本地机器指令并直接在CPU上执行。

No Comment Yet