【JVM原理详解】03-JVM整体架构全景图
2026/7/19 22:48:30 网站建设 项目流程

JVM整体架构全景图

引言

前两篇文章分别从"跨平台机制"和"发展历史"两个视角帮你建立了对JVM的初步印象。现在到了最关键的一步——把JVM的内部结构完整地拆解开来。JVM是一个精密的系统,它的架构设计体现了虚拟机领域的众多经典思想。本篇将"俯瞰"JVM的整体架构,帮你建立起一个清晰的心理地图。当你理解了三大子系统的职责和协作方式,后续深入类加载机制、运行时数据区和GC优化时,就能始终知道当前讨论的内容在整个架构中的位置。

JVM架构全景图

下面是一幅JVM整体架构的ASCII图。建议你花一分钟仔细阅读,把每个模块的位置印在脑海中。后续所有文章的内容都可以回溯到这张图上。

┌──────────────────────────────────────────────────────────────────────┐ │ JVM 整体架构 │ ├──────────────────────────────────────────────────────────────────────┤ │ │ │ ┌──────────────────────────────────────────────────────────┐ │ │ │ 1. 类加载器子系统 (Class Loader Subsystem) │ │ │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │ │ │ Bootstrap │─▶│ Extension/ │─▶│ Application │ │ │ │ │ │ ClassLoader │ │ Platform │ │ ClassLoader │ │ │ │ │ │ (启动类加载器)│ │ ClassLoader │ │ (应用类加载器) │ │ │ │ │ └──────────────┘ └──────────────┘ └──────────────┘ │ │ │ │ │ │ │ │ │ │ │ ▼ ▼ ▼ │ │ │ │ ┌─────────┐ ┌─────────┐ ┌──────────┐ │ │ │ │ │ Loading │──▶│ Linking │──▶│Initialize│ 双亲委派 │ │ │ │ │ (加载) │ │ (链接) │ │ (初始化) │ 模型 │ │ │ │ └─────────┘ └─────────┘ └──────────┘ │ │ │ └──────────────────────────┬───────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌──────────────────────────────────────────────────────────┐ │ │ │ 2. 运行时数据区 (Runtime Data Areas) │ │ │ │ │ │ │ │ ┌──────────────────────────────────────────────┐ │ │ │ │ │ 线程共享区域 (Thread-Shared) │ │ │ │ │ │ ┌─────────────────┐ ┌───────────────────┐ │ │ │ │ │ │ │ Heap │ │ Method Area │ │ │ │ │ │ │ │ (堆/GC堆) │ │ (方法区/元空间) │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ┌─────────────┐ │ │ ┌───────────────┐ │ │ │ │ │ │ │ │ │ Young Gen │ │ │ │ 类元数据 │ │ │ │ │ │ │ │ │ │ (Eden+S0+S1)│ │ │ │ 运行时常量池 │ │ │ │ │ │ │ │ │ ├─────────────┤ │ │ │ 静态变量 │ │ │ │ │ │ │ │ │ │ Old Gen │ │ │ │ JIT编译缓存 │ │ │ │ │ │ │ │ │ │ (Tenured) │ │ │ │ 方法字节码 │ │ │ │ │ │ │ │ │ └─────────────┘ │ │ └───────────────┘ │ │ │ │ │ │ │ └─────────────────┘ └───────────────────┘ │ │ │ │ │ └──────────────────────────────────────────────┘ │ │ │ │ │ │ │ │ ┌──────────────────────────────────────────────┐ │ │ │ │ │ 线程私有区域 (Thread-Private) │ │ │ │ │ │ │ │ │ │ │ │ ┌──────────────┐ ┌──────────────┐ ┌──────┐ │ │ │ │ │ │ │ JVM Stack │ │Native Method │ │ PC │ │ │ │ │ │ │ │ (虚拟机栈) │ │ Stack │ │ 寄存器│ │ │ │ │ │ │ │ │ │ (本地方法栈) │ │ │ │ │ │ │ │ │ │ ┌──────────┐ │ │ │ │ │ │ │ │ │ │ │ │ │栈帧 Frame│ │ │ (Native方法 │ │ │ │ │ │ │ │ │ │ │-局部变量表│ │ │ 调用栈) │ │ │ │ │ │ │ │ │ │ │-操作数栈 │ │ │ │ │ │ │ │ │ │ │ │ │ │-动态链接 │ │ │ │ │ │ │ │ │ │ │ │ │ │-方法返回 │ │ │ │ │ │ │ │ │ │ │ │ │ └──────────┘ │ │ │ │ │ │ │ │ │ │ │ └──────────────┘ └──────────────┘ └──────┘ │ │ │ │ │ └──────────────────────────────────────────────┘ │ │ │ └──────────────────────────┬───────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌──────────────────────────────────────────────────────────┐ │ │ │ 3. 执行引擎 (Execution Engine) │ │ │ │ │ │ │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │ │ │ 解释器 │ │ JIT编译器 │ │ 垃圾回收器 │ │ │ │ │ │ (Interpreter)│ │ (C1 / C2) │ │ (Garbage │ │ │ │ │ │ │ │ │ │ Collector) │ │ │ │ │ │ 逐条解释字节码 │ │ 热点代码编译 │ │ 自动内存管理 │ │ │ │ │ │ 立即开始执行 │ │ 为本地机器码 │ │ 回收无用对象 │ │ │ │ │ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │ │ │ │ │ │ │ │ │ │ │ ▼ ▼ │ │ │ │ │ ┌─────────────────────────────────┐ │ │ │ │ │ │ 分层编译 (Tiered Compilation)│ │ │ │ │ │ │ Level 0: 解释执行 │ │ │ │ │ │ │ Level 1-3: C1编译 (渐进优化) │◀───────┘ │ │ │ │ │ Level 4: C2编译 (深度优化) │ │ │ │ │ └─────────────────────────────────┘ │ │ │ └──────────────────────────────────────────────────────────┘ │ │ │ └──────────────────────────────────────────────────────────────────────┘

三大子系统详解

子系统一:类加载器子系统

类加载器子系统是整个JVM的"入口"。没有它,字节码就只是磁盘上的二进制文件,无法被JVM执行。这套子系统负责三项核心任务:

加载(Loading):通过类的全限定名找到对应的.class文件(或网络数据流),读取其二进制数据,在内存中生成代表该类的java.lang.Class对象。加载源可以是本地文件系统、jar包、网络(Applet时代的URLClassLoader)甚至运行时动态生成(动态代理、CGLIB等)。

链接(Linking):分为三个子阶段——验证(确保字节码符合JVM规范,不会破坏JVM安全)→准备(为类的静态变量分配内存并设置默认值,注意此时尚未执行Java代码)→解析(将常量池中的符号引用替换为直接引用)。

初始化(Initialization):执行类构造器<clinit>方法,为静态变量赋初始值和执行静态代码块。这是类加载过程中首次执行Java代码的阶段。

类加载器的组织遵循双亲委派模型(Parent Delegation Model):

Bootstrap ClassLoader (启动类加载器,加载rt.jar/jmods中的核心类) ▲ │ 父加载器 Platform/Extension ClassLoader (平台/扩展类加载器) ▲ │ 父加载器 Application ClassLoader (应用类加载器,加载classpath下的类) ▲ │ 父加载器 自定义 ClassLoader

双亲委派的工作方式是:一个类加载器收到加载请求时,首先委托给父加载器尝试加载,只有父加载器加载失败时才自己尝试。这种机制确保了Java核心类库(如java.lang.String)始终由Bootstrap ClassLoader加载,避免了用户自定义的同名类替换核心类带来的安全风险。

子系统二:运行时数据区

运行时数据区是JVM的"内存大脑"。JVM在执行Java程序时会将它管理的内存划分为若干个不同的数据区域,每个区域有各自的用途和生命周期。这些区域可以分为两大类:

线程共享区域(所有线程都能访问):

  • 堆(Heap):JVM内存中最大的一块,几乎所有对象实例都在这里分配。堆也是GC管理的主要区域。在分代GC的视角下,堆被划分为新生代(Young Generation,又分为Eden区和两个Survivor区)和老年代(Old Generation)。JDK 8之前还有永久代的概念,用于存储类元数据,但从JDK 8起已被元空间取代。

  • 方法区(Method Area):存储已被JVM加载的类信息、常量、静态变量、JIT编译后的代码缓存等。在HotSpot的实现中,JDK 8之前方法区的实现是永久代(PermGen),JDK 8之后改为使用本地内存的元空间(Metaspace)。这一变化解决了永久代-XX:MaxPermSize参数调优困难、容易OOM的痛点——元空间默认只受限于系统可用内存。

线程私有区域(每个线程独有,随线程创建而创建,随线程结束而销毁):

  • 程序计数器(PC Register):当前线程所执行的字节码指令的行号指示器。分支、循环、跳转、异常处理、线程恢复等基础功能都依赖PC寄存器。如果线程执行的是Native方法,PC寄存器的值为undefined。

  • Java虚拟机栈(JVM Stack):描述Java方法执行的内存模型。每个方法执行时JVM都会创建一个栈帧(Stack Frame),用于存储局部变量表、操作数栈、动态链接、方法返回地址等信息。方法的调用到执行完成,对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。当线程请求的栈深度超过允许的最大值时抛出StackOverflowError,当栈动态扩展无法获得足够内存时抛出OutOfMemoryError

  • 本地方法栈(Native Method Stack):与虚拟机栈功能相似,但服务于Native方法(通过JNI调用的C/C++代码)。在HotSpot中,本地方法栈和虚拟机栈是合并实现的,不单独区分。

子系统三:执行引擎

执行引擎是JVM的"心脏",负责将字节码指令翻译为机器指令并执行。HotSpot的执行引擎采用混合模式

解释器(Interpreter):逐条读取字节码指令,查表找到对应的机器码片段执行。启动速度快,但相同代码每次执行都要重复解释。HotSpot使用模板解释器——每条字节码指令在JVM启动时预先生成一段机器码(stub),执行时直接跳转即可。

JIT编译器(Just-In-Time Compiler):将热点方法/循环的字节码一次性编译为本地机器码,后续直接执行编译后的代码,不再需要解释。HotSpot内置了两个JIT编译器:

  • C1编译器(Client Compiler):编译速度快,但优化程度较低。适合对启动速度敏感的场景(如桌面应用)。
  • C2编译器(Server Compiler):编译速度慢,但能进行深度优化(内联、逃逸分析、循环展开、无用代码消除等)。适合长时间运行的服务器端应用。

在JDK 7及之后,HotSpot默认启用分层编译(Tiered Compilation),结合C1和C2的优势:

Level 0: 解释执行(不收集性能数据)→ Level 1: C1简单编译(不收集profiling数据)→ Level 2: C1编译(进行简单profiling)→ Level 3: C1编译(进行完整profiling)→ Level 4: C2深度优化编译

系统根据方法的"热度"决定其当前应处于哪个编译层级。热点方法会逐步从Level 0提升到Level 4,非热点方法可能停留在较低层级甚至退回到解释执行。

垃圾回收器(Garbage Collector):自动管理堆内存,回收不再使用的对象。GC是JVM与C/C++等需要手动内存管理的语言最显著的差异之一。HotSpot提供多种GC策略——Serial、Parallel、CMS(JDK 14移除)、G1(JDK 9起默认)、ZGC(JDK 15起生产可用)、Shenandoah等。GC的运行会触发Stop-The-World暂停(所有用户线程暂停,等待GC完成),不同的GC算法在吞吐量和暂停时间之间做出不同的权衡。

数据在JVM中的流转过程

以一段简单的Java代码为例,追踪数据在JVM中的完整流转路径:

publicclassDataFlow{publicstaticvoidmain(String[]args){Calculatorcalc=newCalculator();// ①创建对象intresult=calc.add(10,20);// ②调用方法System.out.println(result);// ③输出结果}}classCalculator{publicintadd(inta,intb){returna+b;}}

当执行java DataFlow时:

  1. 类加载阶段AppClassLoader找到DataFlow.classCalculator.class,加载到方法区。经过验证、准备(为静态变量分配默认值)、解析(符号引用→直接引用)、初始化(执行<clinit>

  2. 方法调用阶段:主线程的PC寄存器指向main方法的字节码,JVM栈中压入main的栈帧。字节码new #2触发在堆中分配Calculator对象实例,局部变量表第0号槽位存储该对象的引用

  3. 对象方法调度calc.add(10, 20)被编译为invokevirtual指令。JVM先在堆中定位calc对象,通过其对象头中的类型指针找到方法区中的Calculator类元数据,再定位到add方法的字节码。add方法执行时,JVM栈压入新栈帧,局部变量表中存储this引用和两个int参数(10, 20),操作数栈计算iadd的结果30

  4. 方法返回add的栈帧弹出,返回值30放入main栈帧的操作数栈顶。main方法继续执行后续字节码,输出结果

  5. 垃圾回收:当Calculator对象不再被任何引用指向时,GC线程会在合适的时机回收其在堆中的内存

实践要点

  1. 内存区域与JVM参数:运行时数据区的大小通过JVM启动参数控制。堆大小用-Xms(初始)和-Xmx(最大)设定;栈大小用-Xss设定(默认约1MB/线程);元空间用-XX:MetaspaceSize-XX:MaxMetaspaceSize控制。生产环境应始终显式设置-Xmx,避免JVM使用默认值(通常是物理内存的1/4)导致意外OOM或被操作系统OOM Killer杀死。

  2. 栈帧与递归深度:每个栈帧的大小在编译期就已确定。递归调用过深时,即使堆内存充足,也会因栈空间耗尽而抛出StackOverflowError。将递归改写为迭代、适当增大-Xss或使用尾递归优化(JVM不直接支持尾递归优化,需要编译器协助)是常用的应对措施。

  3. 分层编译的观测:通过-XX:+PrintCompilation可以看到方法在不同编译层级间的迁移过程。通过-XX:-TieredCompilation可以关闭分层编译(不推荐生产环境使用,除非在排查特定的JIT编译问题)。JDK 8中可通过-XX:+UnlockDiagnosticVMOptions -XX:+PrintInlining查看方法内联的详细信息,这是理解JIT优化的有力工具。

  4. 元空间溢出与永久代溢出:JDK 8后的元空间溢出报java.lang.OutOfMemoryError: Metaspace,JDK 8前报java.lang.OutOfMemoryError: PermGen space。排查时需要关注是否有大量的动态代理类生成(CGLIB、Javaassist等框架),或类加载器泄漏导致已加载的类无法被卸载。元空间的优势在于它不会被固定大小的永久代所限制——但它仍然受限于系统可用内存,监控和告警依然是必要的。

  5. 架构图是理解后续知识的锚点:当后续文章深入讲解类加载器机制、堆内存管理或GC算法时,返回来看这张全景图,你就能迅速定位到当前讨论的内容处于JVM的哪个子系统、哪个内存区域。建议收藏本篇文章,作为后续学习的索引。

小结

  • JVM由三大子系统构成:类加载器子系统(负责加载和初始化类)→运行时数据区(存储各类数据的五块内存区域)→执行引擎(解释执行+JIT编译+GC)
  • 运行时数据区按线程共享/私有划分为两类,理解它们的生命周期和存储内容对性能调优和问题排查至关重要
  • 执行引擎的解释器+JIT编译器混合模式兼顾了启动速度与峰值性能,分层编译进一步细化了优化策略
  • 方法区从永久代到元空间的演变是JVM内存管理的重要里程碑,减少了OOM风险
  • 这张全景图是后续深入各个子系统的基础框架,每当我们讨论具体的知识点时,都可以回到这张图上找到它的位置

更多资料:【JVM调优实战】

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询