【JVM原理详解】03-JVM整体架构全景图
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 │ │ │ │ 类元数据 │ │ │ │ │ │ │ │ │ │ (EdenS0S1)│ │ │ │ 运行时常量池 │ │ │ │ │ │ │ │ │ ├─────────────┤ │ │ │ 静态变量 │ │ │ │ │ │ │ │ │ │ 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 ModelBootstrap ClassLoader (启动类加载器加载rt.jar/jmods中的核心类) ▲ │ 父加载器 Platform/Extension ClassLoader (平台/扩展类加载器) ▲ │ 父加载器 Application ClassLoader (应用类加载器加载classpath下的类) ▲ │ 父加载器 自定义 ClassLoader双亲委派的工作方式是一个类加载器收到加载请求时首先委托给父加载器尝试加载只有父加载器加载失败时才自己尝试。这种机制确保了Java核心类库如java.lang.String始终由Bootstrap ClassLoader加载避免了用户自定义的同名类替换核心类带来的安全风险。子系统二运行时数据区运行时数据区是JVM的内存大脑。JVM在执行Java程序时会将它管理的内存划分为若干个不同的数据区域每个区域有各自的用途和生命周期。这些区域可以分为两大类线程共享区域所有线程都能访问堆HeapJVM内存中最大的一块几乎所有对象实例都在这里分配。堆也是GC管理的主要区域。在分代GC的视角下堆被划分为新生代Young Generation又分为Eden区和两个Survivor区和老年代Old Generation。JDK 8之前还有永久代的概念用于存储类元数据但从JDK 8起已被元空间取代。方法区Method Area存储已被JVM加载的类信息、常量、静态变量、JIT编译后的代码缓存等。在HotSpot的实现中JDK 8之前方法区的实现是永久代PermGenJDK 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、CMSJDK 14移除、G1JDK 9起默认、ZGCJDK 15起生产可用、Shenandoah等。GC的运行会触发Stop-The-World暂停所有用户线程暂停等待GC完成不同的GC算法在吞吐量和暂停时间之间做出不同的权衡。数据在JVM中的流转过程以一段简单的Java代码为例追踪数据在JVM中的完整流转路径publicclassDataFlow{publicstaticvoidmain(String[]args){CalculatorcalcnewCalculator();// ①创建对象intresultcalc.add(10,20);// ②调用方法System.out.println(result);// ③输出结果}}classCalculator{publicintadd(inta,intb){returnab;}}当执行java DataFlow时类加载阶段AppClassLoader找到DataFlow.class和Calculator.class加载到方法区。经过验证、准备为静态变量分配默认值、解析符号引用→直接引用、初始化执行clinit方法调用阶段主线程的PC寄存器指向main方法的字节码JVM栈中压入main的栈帧。字节码new #2触发在堆中分配Calculator对象实例局部变量表第0号槽位存储该对象的引用对象方法调度calc.add(10, 20)被编译为invokevirtual指令。JVM先在堆中定位calc对象通过其对象头中的类型指针找到方法区中的Calculator类元数据再定位到add方法的字节码。add方法执行时JVM栈压入新栈帧局部变量表中存储this引用和两个int参数10, 20操作数栈计算iadd的结果30方法返回add的栈帧弹出返回值30放入main栈帧的操作数栈顶。main方法继续执行后续字节码输出结果垃圾回收当Calculator对象不再被任何引用指向时GC线程会在合适的时机回收其在堆中的内存实践要点内存区域与JVM参数运行时数据区的大小通过JVM启动参数控制。堆大小用-Xms初始和-Xmx最大设定栈大小用-Xss设定默认约1MB/线程元空间用-XX:MetaspaceSize和-XX:MaxMetaspaceSize控制。生产环境应始终显式设置-Xmx避免JVM使用默认值通常是物理内存的1/4导致意外OOM或被操作系统OOM Killer杀死。栈帧与递归深度每个栈帧的大小在编译期就已确定。递归调用过深时即使堆内存充足也会因栈空间耗尽而抛出StackOverflowError。将递归改写为迭代、适当增大-Xss或使用尾递归优化JVM不直接支持尾递归优化需要编译器协助是常用的应对措施。分层编译的观测通过-XX:PrintCompilation可以看到方法在不同编译层级间的迁移过程。通过-XX:-TieredCompilation可以关闭分层编译不推荐生产环境使用除非在排查特定的JIT编译问题。JDK 8中可通过-XX:UnlockDiagnosticVMOptions -XX:PrintInlining查看方法内联的详细信息这是理解JIT优化的有力工具。元空间溢出与永久代溢出JDK 8后的元空间溢出报java.lang.OutOfMemoryError: MetaspaceJDK 8前报java.lang.OutOfMemoryError: PermGen space。排查时需要关注是否有大量的动态代理类生成CGLIB、Javaassist等框架或类加载器泄漏导致已加载的类无法被卸载。元空间的优势在于它不会被固定大小的永久代所限制——但它仍然受限于系统可用内存监控和告警依然是必要的。架构图是理解后续知识的锚点当后续文章深入讲解类加载器机制、堆内存管理或GC算法时返回来看这张全景图你就能迅速定位到当前讨论的内容处于JVM的哪个子系统、哪个内存区域。建议收藏本篇文章作为后续学习的索引。小结JVM由三大子系统构成类加载器子系统负责加载和初始化类→运行时数据区存储各类数据的五块内存区域→执行引擎解释执行JIT编译GC运行时数据区按线程共享/私有划分为两类理解它们的生命周期和存储内容对性能调优和问题排查至关重要执行引擎的解释器JIT编译器混合模式兼顾了启动速度与峰值性能分层编译进一步细化了优化策略方法区从永久代到元空间的演变是JVM内存管理的重要里程碑减少了OOM风险这张全景图是后续深入各个子系统的基础框架每当我们讨论具体的知识点时都可以回到这张图上找到它的位置更多资料【JVM调优实战】

相关新闻

【JVM原理详解】02-JVM发展史与主流实现

【JVM原理详解】02-JVM发展史与主流实现

JVM发展史与主流实现 引言 理解了字节码是Java跨平台的中间表示之后,一个自然的问题是:这些字节码究竟由谁来执行?答案是JVM的实现。HotSpot VM作为目前最主流的选择并非一蹴而就,它经历了从Sun Classic VM到Exact VM再到HotSpo…

2026/7/19 22:48:53阅读更多 →
【JVM调优实战】19-堆dump与线程dump分析

【JVM调优实战】19-堆dump与线程dump分析

堆 dump 与线程 dump 分析方法论 本文是《JVM调优实战》专栏第 19 讲。 引言 在前四讲中,我们走完了 JVM 诊断工具的"全景图":命令行工具(第 15 讲)、可视化工具(第 16 讲)、Arthas 在线诊断(第 17 讲)、火焰图性能分析(第 18 讲)。掌握了这些工具后,你会…

2026/7/19 22:48:53阅读更多 →
ReadCat:解锁纯净阅读体验的5个关键优势

ReadCat:解锁纯净阅读体验的5个关键优势

ReadCat:解锁纯净阅读体验的5个关键优势 【免费下载链接】read-cat 一款免费、开源、简洁、纯净、无广告的小说阅读器 项目地址: https://gitcode.com/gh_mirrors/re/read-cat 在数字阅读软件普遍被广告和付费墙包围的今天,你是否渴望一款真正专注…

2026/7/19 22:48:53阅读更多 →
解决Hypack启动报错:VC++运行库依赖原理与安装修复全指南

解决Hypack启动报错:VC++运行库依赖原理与安装修复全指南

1. 项目概述:当专业软件遇上“拦路虎”搞测绘、做水文的朋友,对Hypack这个软件肯定不陌生。它几乎是内业数据处理和外业导航测量的行业标准工具之一。但很多同行,尤其是刚接触这个软件的新手,在安装Hypack时,十有八九会…

2026/7/20 13:42:45阅读更多 →
Spring循环依赖:为什么原型Bean和构造器依赖无解?三级缓存又为何而生?

Spring循环依赖:为什么原型Bean和构造器依赖无解?三级缓存又为何而生?

一、什么是循环依赖一个“先有鸡还是先有蛋”的编程难题想象一下这个场景:小明说:“我的工作依赖小红完成的数据。”小红说:“不行,我得等小明先把框架搭好,我才能开始处理数据。”两人互相等待,工作永远无…

2026/7/20 13:42:45阅读更多 →
Go语言字符串底层原理与高效处理实践

Go语言字符串底层原理与高效处理实践

1. Go语言字符串基础解析Go语言中的字符串(string)是一种不可变的字节序列,底层实现是一个指向字节数组的指针和长度信息的结构体。从语法层面看,字符串可以包含任意字节,但按照惯例通常存储UTF-8编码的Unicode文本。字符串的零值是空字符串&…

2026/7/20 13:42:45阅读更多 →
AI编程工具协同:Claude Code与Codex插件集成实战指南

AI编程工具协同:Claude Code与Codex插件集成实战指南

如果你正在使用 Claude Code 进行日常开发,最近一定被一个选择困扰:当需要更深入的代码审查、更复杂的调试任务,或者想把一个棘手的 bug 直接“甩”给 AI 时,是该切换到另一个独立的 Codex 应用,还是继续在 Claude Cod…

2026/7/20 13:42:45阅读更多 →
5分钟掌握国家中小学智慧教育平台电子课本下载:智能工具助您轻松获取PDF教材

5分钟掌握国家中小学智慧教育平台电子课本下载:智能工具助您轻松获取PDF教材

5分钟掌握国家中小学智慧教育平台电子课本下载:智能工具助您轻松获取PDF教材 【免费下载链接】tchMaterial-parser 国家中小学智慧教育平台 电子课本下载工具,帮助您从智慧教育平台中获取电子课本的 PDF 文件网址并进行下载,让您更方便地获取…

2026/7/20 13:42:45阅读更多 →
终极指南:如何轻松解锁《鸣潮》游戏模组,打造个性化游戏体验

终极指南:如何轻松解锁《鸣潮》游戏模组,打造个性化游戏体验

终极指南:如何轻松解锁《鸣潮》游戏模组,打造个性化游戏体验 【免费下载链接】wuwa-mod Wuthering Waves pak mods 项目地址: https://gitcode.com/GitHub_Trending/wu/wuwa-mod 还在为《鸣潮》游戏中的挑战而烦恼吗?想要更流畅的战斗…

2026/7/20 13:40:44阅读更多 →
Go语言静态资源打包方案对比与实践指南

Go语言静态资源打包方案对比与实践指南

1. 项目背景与核心需求在Go语言开发中,我们经常需要处理静态资源文件的打包问题。无论是Web应用的模板文件、前端资源,还是配置文件、证书等,都需要随程序一起分发。传统做法是将这些文件与编译后的二进制文件放在同一目录下,但这…

2026/7/20 0:50:54阅读更多 →
Go语言实现高性能LDAP认证服务的架构与实践

Go语言实现高性能LDAP认证服务的架构与实践

1. 项目背景与核心价值LDAP(轻量级目录访问协议)作为企业级身份认证的黄金标准,已经服务了超过80%的财富500强公司。我在金融科技领域实施统一认证体系时,发现传统Java方案存在启动慢、内存占用高等痛点。而Go语言凭借其协程并发模…

2026/7/20 0:50:54阅读更多 →
【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

更多请点击: https://intelliparadigm.com 第一章:AI面试官实战指南的核心价值与适用场景 AI面试官并非替代人类HR的“黑箱工具”,而是以可解释、可审计、可迭代的方式,赋能招聘全链路的关键基础设施。其核心价值在于将主观经验沉…

2026/7/20 0:50:54阅读更多 →
2026 WAIC:努比亚二代“豆包手机”NaviX Ultra亮相,智能体验全面升级!

2026 WAIC:努比亚二代“豆包手机”NaviX Ultra亮相,智能体验全面升级!

7月18日智东西消息,在2026 WAIC期间,努比亚联合字节豆包打造的二代“豆包手机”努比亚NaviX Ultra首次亮相,相比一代有诸多升级。智能体手机理念中兴通讯终端事业部总裁、努比亚总裁倪飞表示,智能体手机要从人操作手机变为手机帮人…

2026/7/20 0:01:04阅读更多 →
努比亚NaviX Ultra亮相WAIC,智能体手机能否让用户生活更简单?

努比亚NaviX Ultra亮相WAIC,智能体手机能否让用户生活更简单?

努比亚NaviX Ultra:外观与功能双升级在2026 WAIC期间,首次亮相的努比亚NaviX Ultra吸引了众多目光。它是努比亚联合字节豆包打造的二代“豆包手机”,与一代努比亚M153相比,外观设计变化较大。其机身背部搭载横向排布的大尺寸影像模…

2026/7/20 0:01:04阅读更多 →
C# 将逗号分割的字符串转换为long,并添加到List<long>

C# 将逗号分割的字符串转换为long,并添加到List<long>

目录 方法1:使用Split和Convert.ToInt64 方法2:使用LINQ的Select和ToList 方法3:使用TryParse进行异常安全转换(推荐) 如果您喜欢此文章,请收藏、点赞、评论,谢谢,祝您快乐每一天…

2026/7/20 0:01:04阅读更多 →
YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

如果你在部署 YOLOv8 时,发现推理速度只有可怜的 1-2 FPS,而别人的演示视频却能跑到 30 FPS 以上,那么问题很可能不在模型本身,而在于你的整个处理链路。很多开发者拿到一个训练好的 YOLOv8 模型后,会直接使用官方示例…

2026/7/19 22:50:49阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

Coze与Dify对比指南:低代码AI应用开发从入门到实战

1. 从零到一:为什么你需要了解 Coze 和 Dify?如果你对 AI 应用开发感兴趣,但一看到“大模型”、“智能体”、“工作流”这些词就头疼,觉得门槛太高,那这篇文章就是为你准备的。很多开发者,包括我自己&#…

2026/7/19 14:50:26阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

AI生图工具怎么选?2026年6月版实测对比

做自媒体的朋友应该都有体会:配图一直是个让人头疼的问题。2026年,AI生图工具已经非常成熟了,但工具太多反而不知道怎么选。以下是截至2026年6月我对主流AI生图工具的实测对比。Midjourney V8.1:速度之王2026年6月11日&#xff0c…

2026/7/19 18:50:36阅读更多 →