JVM 有哪些组成部分:面试必问的 Java 运行时架构

张开发
2026/4/21 15:56:18 15 分钟阅读
JVM 有哪些组成部分:面试必问的 Java 运行时架构
我刚学 Java 的时候以为 JVM 就是个黑盒代码写完往里一扔就能跑。后来才知道JVM 里面学问大了去了。今天这篇文章就是我复习 JVM 时整理的笔记。面试问到 JVM 结构如果你只能说出堆和栈那大概率要被追问到怀疑人生。一、先画个整体框架在说每个组件之前先把 JVM 的结构捋清楚。记住这个图这是整个 JVM 的骨架。三个核心模块类加载器ClassLoader—— 把 class 文件加载进来运行时数据区Runtime Data Area—— 分配内存存数据执行引擎Execution Engine—— 执行字节码指令二、类加载器 ClassLoader它干什么的ClassLoader 负责把.class文件字节码加载到内存中转换成 JVM 能认识的数据结构。加载流程.java 源文件 ↓ 编译 .class 字节码文件 ↓ ClassLoader 内存中的 Class 对象java.lang.Class三个阶段1. 加载Loading通过类的全限定名找到 class 文件把字节流转换成方法区的数据结构在堆里生成一个 Class 对象作为访问入口2. 链接Linking验证检查字节码格式是否正确安全检查准备给静态变量分配内存设置默认值解析把符号引用替换成直接引用比如把方法名变成内存地址3. 初始化Initialization执行静态代码块和静态变量的赋值这是真正执行代码的地方双亲委派模型这块是面试高频追问。三层类加载器加载器负责加载例子Bootstrap ClassLoaderJDK 核心类String, ObjectExtension ClassLoaderJDK 扩展类javax.*Application ClassLoader用户 classpath自己写的类双亲委派流程加载请求 ↓ ApplicationClassLoader ↓ 问爸 ExtensionClassLoader ↓ 问爸 BootstrapClassLoader ↓ 找不到 ExtensionClassLoader 尝试加载 ↓ 找不到 ApplicationClassLoader 尝试加载 ↓ 成功 返回 Class为什么这样设计我当初也被问懵了后来想明白了一个例子如果你自己写一个java.lang.String没有双亲委派的话JVM 就会加载你自己写的 String安全性全没了。双亲委派保证核心类库优先被 Bootstrap 加载防止用户代码覆盖 JDK。三、运行时数据区 Runtime Data Area这是 JVM 里最重要的部分也是面试追问的重灾区。1. 程序计数器Program Counter Register特点很小只存一个指针指向下一条要执行的字节码指令线程私有每个线程都有自己的 PC为什么不会 OOM因为它只存一个指令地址大小固定不会动态增长。唯一没有 GC 的区域。2. 虚拟机栈VM Stack特点线程私有存储方法调用的数据每个方法叫一个栈帧Stack Frame一个栈帧包含局部变量表方法参数 局部变量操作数栈计算时的临时空间动态链接指向常量池的引用返回地址会 OOM 吗会。如果递归没写好栈深度超过限制就会StackOverflowError。我之前写递归算法就踩过这个坑// 死递归栈溢出 public int sum(int n) { return sum(n) 1; // 没有终止条件 }3. 本地方法栈Native Method Stack和虚拟机栈一样只不过服务的是本地方法native keyword 修饰的方法。JNI 调用C/C 写的代码会用到这里。4. 堆HeapJVM 里最大的内存区域。存储对象实例和数组线程共享所有线程都能访问GC 的主要战场为什么要分代因为对象的生命周期不同新生代刚创建的对象朝生夕死老年代存活时间长的对象分代后GC 更有针对性效率更高5. 方法区Method Area存储类的元信息类名、修饰符、父类类的结构字段、方法运行时常量池字面量和符号引用JIT 编译后的代码缓存JDK 1.8 的变化之前方法区在堆里叫 Permanent Generation永久代。JDK 1.8 改成了 Metaspace放在本地内存里。版本方法区实现位置JDK 1.7PermGen堆内JDK 1.8Metaspace本地内存为什么要改因为 PermGen 容易 OOM。你要是项目中依赖了一堆 jar 包PermGen 分分钟爆掉。四、执行引擎 Execution Engine它干什么的把字节码翻译成机器码让 CPU 执行。解释器 vs JIT 编译器组件特点适用场景解释器逐行翻译立即执行启动阶段一次性执行JIT 编译器编译成机器码缓存起来热点代码多次执行JIT 的热点探测JVM 会监控方法的执行频率热点代码会被 JIT 编译成机器码。两个计数器方法调用计数器回边计数器循环体超过阈值就触发编译。垃圾回收器GC 是 JVM 里最复杂也最常问的部分。常见 GC 算法算法原理缺点标记-清除标记存活对象清理未标记的产生内存碎片复制把活对象复制到另一半空间浪费一半内存标记-整理标记后整理内存整理耗时常见垃圾回收器Serial单线程Parallel多线程吞吐量优先CMS并发标记清除低停顿G1REGION划分可预测停顿ZGC / Shenandoah超低停顿JDK 11五、本地接口 JNIJava 不是万能的有些场景需要调用本地代码C/C。JNI 就是 Java 和 native 代码之间的桥梁。典型场景调用操作系统 API追求极致性能数学计算访问硬件public native String hello(); // native 方法调用 C 实现六、记忆口诀JVM 三大部分 ┌─────────────────────────────────┐ │ 类加载器 ──── 加载字节码 │ │ 运行时数据区 ── 分配内存 │ │ 执行引擎 ──── 执行字节码 │ └─────────────────────────────────┘ ​ 运行时数据区五块 计数器无GC 虚拟机栈存方法调用会OOM 本地方法栈native方法 堆对象会GC 方法区类信息 ​ 双亲委派 Bootstrap → Extension → Application写在最后JVM 这块知识面试基本必问。我踩过的坑是只背概念不画图。后来我把 JVM 的结构画了 5 遍把每个区域的作用、特点、常见问题都写上面试时直接画给面试官看。面试官当时眼睛一亮说这个理解方式不错。技术表达很重要。能画出来、讲清楚比背出来强 100 倍。

更多文章