JVM 由哪些部分组成?
- 标签:后端、Java、JVM
🏛 业务架构师
JVM 三大块:类加载子系统(加载-验证-准备-解析-初始化,双亲委派)、运行时数据区(堆、方法区/元空间、虚拟机栈、本地方法栈、程序计数器)、执行引擎(解释器 + JIT 编译器 + GC)。
业务视角,懂 JVM 的价值不在背结构,在三件运营实事:
- 容量规划:堆给多大、用什么收集器,直接决定一台机器能扛多少业务流量。容器时代的高频坑:JVM 堆 + 元空间 + 线程栈 + 堆外(Netty/DirectBuffer)> 容器 memory limit → OOMKilled——容器内存预算表是发布规范的一部分;
- 故障快速恢复(降 MTTR):线上 CPU 100%、内存泄漏、GC 频繁——jstack/jmap/arthas 三板斧 + 对结构的理解,把"重启大法"变成根因定位。Full GC 每 10 秒一次的停顿,懂的人 20 分钟找到大对象缓存,不懂的重启三次还在涨;
- 性能调优定价:交易接口 P99 敏感 → 低停顿收集器(G1/ZGC);离线批处理 → 吞吐优先(Parallel)。GC 调优的第一步永远是看监控定目标,而不是改参数碰运气。
实践标尺:绝大多数业务用默认 G1 + 合理堆大小就够——JVM 调优是"监控驱动的问题解决",不是"参数玄学表演"。真正值钱的能力是:看到指标异常时,知道问题在堆、栈、元空间还是堆外的哪一格——结构图就是排障地图。
🏗 系统架构师
我按"类加载子系统 + 运行时数据区 + 执行引擎"三段讲 JVM 组成:
1. 类加载子系统:加载(读取字节码生成 Class 对象)→ 验证 → 准备(静态变量默认值)→ 解析(符号引用转直接引用)→ 初始化(clinit)。加载器层级:启动类加载器 → 扩展/平台 → 应用 → 自定义,双亲委派保证核心类唯一与安全。
2. 运行时数据区:
- 堆(线程共享):对象实例与数组,GC 主战场,分新生代(Eden + 2 Survivor)/老年代;
- 方法区/元空间(共享):类元信息、运行时常量池(JDK 8 起元空间用本地内存);
- 虚拟机栈(线程私有):栈帧(局部变量表、操作数栈、动态链接、返回地址)——深递归爆 StackOverflowError;
- 本地方法栈:native 方法;
- 程序计数器:当前线程执行字节码行号,唯一不 OOM 的区域。
3. 执行引擎:解释器 + JIT(C1/C2 分层编译热点)+ 垃圾回收器(Serial/ParScavenge/CMS/G1/ZGC)。
架构视角:JVM 是"托管运行时"的典范——自动内存管理换开发效率,代价是必须理解 GC 行为做调优。我用这张全景图定位问题:OOM 在堆(泄漏/缓存滥用)还是元空间(动态类)还是堆外(DirectBuffer/Netty),栈溢出看递归,性能毛刺看 GC——区域划分就是故障排查的第一层索引。
💻 全栈开发专家
三大部分:类加载子系统 + 运行时数据区 + 执行引擎。
1. 类加载子系统:加载 → 验证 → 准备 → 解析 → 初始化;双亲委派(安全 + 避免重复加载)。Spring Boot 的 LaunchedURLClassLoader、热部署打破双亲委派都是它的应用面。
2. 运行时数据区(核心考点):
text
线程共享:堆(新生代 Eden+2*Survivor / 老年代)、方法区(JDK8+ = 元空间,本地内存)
线程私有:虚拟机栈(栈帧:局部变量表/操作数栈/动态链接/返回地址)
本地方法栈(native 方法)
程序计数器(唯一不会 OOM 的区域)3. 执行引擎:解释器 + JIT 即时编译(C1/C2 分层编译)+ 垃圾回收器(Serial/ParNew/Parallel/CMS/G1/ZGC)。
对应到排查(这么答落地):
- 栈溢出
StackOverflowError:递归没出口,-Xss调小更易触发; - 堆 OOM:dump + MAT 分析支配树;
- 元空间 OOM:动态代理/脚本引擎泄漏类加载器,
-XX:MaxMetaspaceSize必配; - 直接内存(堆外,属于 NIO DirectByteBuffer)不在规范五区里但必须提——Netty 用多了会
Direct buffer memoryOOM,靠-XX:MaxDirectMemorySize+ NMT 排查。
一句话:线程私有区看线程行为,共享区看对象生命周期,执行引擎决定"怎么跑、何时 GC"。