news 2026/10/7 2:50:20

【Java面试题】JVM

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【Java面试题】JVM

JVM 面试题汇总

JVM 是 Java 后端面试中的重点内容之一,常见考察方向包括:JVM 内存结构、对象创建、对象内存布局、垃圾回收、类加载机制、双亲委派、JVM 调优等。

本文按照JVM 内存结构 → 对象 → 类加载 → 垃圾回收 → 垃圾回收器 → JVM 调优的顺序进行整理,适合 Java 后端面试前复习。


一、JVM 基础

1. 什么是 JVM?

JVM,即 Java Virtual Machine,Java 虚拟机。

它是 Java 程序运行的基础环境,主要负责:

  • 加载 Java 字节码
  • 执行字节码
  • 管理内存
  • 垃圾回收
  • 提供运行时环境

Java 程序的执行过程可以简单理解为:

Java 源代码 ↓ javac 编译 ↓ .class 字节码 ↓ JVM ↓ 机器指令 ↓ CPU 执行

JVM 的核心优势之一就是:

Java 程序编译成与平台无关的字节码,由不同平台上的 JVM 负责执行。

因此可以实现:

一次编译,到处运行。


二、JVM 内存结构

2. JVM 运行时数据区有哪些?

JVM 运行时数据区主要包括:

JVM 运行时数据区 │ ├── 程序计数器 │ ├── Java 虚拟机栈 │ ├── 本地方法栈 │ ├── 堆 │ └── 方法区

其中:

线程私有

  • 程序计数器
  • Java 虚拟机栈
  • 本地方法栈

线程共享

  • 堆
  • 方法区

三、程序计数器

3. 什么是程序计数器?

程序计数器可以理解成:

当前线程正在执行的字节码指令地址。

例如:

线程 A PC → 指令 10 线程 B PC → 指令 25

每个线程都有自己的程序计数器。


4. 为什么程序计数器是线程私有的?

因为 CPU 在多个线程之间切换时,需要知道:

每个线程上次执行到了哪里。

所以每个线程都需要独立保存自己的指令执行位置。


5. 程序计数器会发生 OutOfMemoryError 吗?

不会。

程序计数器是 JVM 规范中唯一没有规定OutOfMemoryError情况的区域。


四、Java 虚拟机栈

6. 什么是 Java 虚拟机栈?

每个线程都有一个独立的 Java 虚拟机栈。

线程每调用一个方法,就会创建一个:

栈帧 Stack Frame

方法执行完成后,对应的栈帧出栈。

例如:

publicvoidmethodA(){methodB();}publicvoidmethodB(){}

执行过程:

methodA() ↓ 栈帧 A ↓ methodB() ↓ 栈帧 B

当methodB()执行完成:

栈帧 B 出栈

然后methodA()继续执行。


五、栈帧

7. 栈帧中有什么?

一个栈帧主要包含:

栈帧 │ ├── 局部变量表 ├── 操作数栈 ├── 动态链接 └── 方法返回地址

8. 局部变量表是什么?

用于保存:

  • 方法参数
  • 局部变量
  • 基本数据类型
  • 对象引用

例如:

publicvoidtest(){inta=10;Useruser=newUser();}

栈帧中的局部变量表会保存:

a → 10 user → User 对象引用

需要注意:

对象本身通常存储在堆中,栈中保存的是对象引用。


六、栈溢出

9. 什么情况下会出现 StackOverflowError?

当线程请求的栈深度超过 JVM 允许的深度时,会出现:

StackOverflowError

最典型的例子就是无限递归:

publicvoidtest(){test();}

执行过程:

test() ↓ test() ↓ test() ↓ test() ↓ ...

不断创建栈帧,最终栈空间耗尽。


七、堆

10. Java 堆是什么?

Java 堆是 JVM 中最大的一块内存区域之一。

主要用于:

存放对象实例和数组。

例如:

Useruser=newUser();

通常可以理解为:

栈 │ │ user 引用 ↓ 堆 ┌───────────┐ │ User 对象 │ └───────────┘

堆也是:

垃圾回收器主要管理的区域。


八、堆为什么要分新生代和老年代?

对象具有明显的生命周期特征:

大量对象创建后很快就会死亡,少量对象会长期存活。

因此 JVM 通常会根据对象存活时间进行分代管理。

经典的堆结构:

Java Heap │ ├── 新生代 Young Generation │ │ │ ├── Eden │ ├── Survivor │ └── Survivor │ └── 老年代 Old Generation

九、对象为什么优先分配到 Eden?

例如:

Useruser=newUser();

一般情况下,新创建的对象首先会分配到:

Eden 区。

当 Eden 空间不足时,会触发 Minor GC。

存活下来的对象会进入 Survivor 区,并根据 GC 次数逐渐晋升到老年代。


十、对象晋升老年代

经典的对象晋升过程:

Eden ↓ Minor GC Survivor ↓ 多次 GC 后仍然存活 Survivor ↓ Old

对象年龄达到一定阈值后,可以晋升到老年代。

但需要注意:

实际 JVM 的对象晋升策略比简单的“年龄达到 15 次就进入老年代”更加复杂,可能受到动态年龄判定、大对象分配等因素影响。


十一、方法区

11. 什么是方法区?

方法区是 JVM 规范定义的运行时数据区域。

主要用于存储:

  • 类信息
  • 方法信息
  • 字段信息
  • 运行时常量池
  • 静态变量等

十二、永久代和元空间

12. 永久代和元空间有什么区别?

JDK 8 之前:

方法区 ↓ 永久代 PermGen

JDK 8 开始:

方法区 ↓ 元空间 Metaspace

最大的区别:

永久代使用 JVM 堆内存,而元空间主要使用本地内存。

所以 JDK 8 中:

Heap ├── Eden ├── Survivor └── Old Native Memory └── Metaspace

十三、运行时常量池

13. 什么是运行时常量池?

运行时常量池属于方法区的一部分。

主要用于存放:

  • 类和接口的名称
  • 字段名称
  • 方法名称
  • 常量
  • 符号引用等

例如:

Stringname="Tom";

字符串相关的常量信息会涉及运行时常量池和字符串常量池。


十四、String 常量池

14. 什么是 String 常量池?

为了避免大量创建相同字符串对象,JVM 会维护字符串常量池。

例如:

Strings1="hello";Strings2="hello";

通常:

s1 == s2

结果为:

true

因为两个变量引用的是同一个字符串常量。


15. new String(“hello”) 创建几个对象?

经典面试题:

Strings=newString("hello");

通常从概念上可以理解为可能涉及两个对象:

"hello" ↓ 字符串常量池对象 new String(...) ↓ 堆中的 String 对象

但具体对象数量要结合字符串常量池中是否已经存在"hello"来判断。

面试时不要机械回答“一定两个”。


十六、对象创建过程

16. new 一个对象发生了什么?

例如:

Useruser=newUser();

大致过程:

new ↓ 类加载检查 ↓ 分配内存 ↓ 初始化零值 ↓ 设置对象头 ↓ 执行构造方法 ↓ 返回对象引用

可以总结成:

类加载检查 → 分配内存 → 初始化零值 → 设置对象头 → 执行构造方法。


十七、对象内存布局

17. 一个对象在内存中包含什么?

HotSpot 中通常可以从三个部分理解:

对象 │ ├── 对象头 │ ├── 实例数据 │ └── 对齐填充

18. 对象头有什么?

对象头主要包含:

Mark Word

用于保存:

  • 哈希码
  • GC 年龄
  • 锁状态
  • 锁相关信息等

类型指针

指向对象所属的类信息。


十九、指针压缩

19. 什么是指针压缩?

64 位 JVM 中,普通对象指针可能占用 64 bit。

为了节省内存,HotSpot 提供:

Compressed Oops

即压缩普通对象指针。

在一定条件下可以把对象引用压缩成 32 bit。

这可以减少对象占用的内存,同时提高 CPU Cache 命中率。


二十、垃圾回收

20. 为什么需要垃圾回收?

Java 不需要像 C/C++ 一样手动释放对象内存。

JVM 会自动判断哪些对象已经没有使用价值,然后回收它们占用的内存。

这就是:

Garbage Collection,GC。


二十一、如何判断对象是否可以被回收?

主要有两种思路:

① 引用计数法

每个对象维护一个引用计数器。

对象 A ↑ 引用 1 对象 A ↑ 引用 2

当引用为 0 时,可以回收。

但是引用计数无法解决循环引用问题。

例如:

A → B ↑ ↓ └───┘

即使外部已经没有引用:

A → B → A

引用计数仍然不为 0。

所以 JVM 主要使用:

可达性分析算法。


二十二、可达性分析

21. 什么是可达性分析?

从 GC Roots 开始向下搜索。

如果对象:

从 GC Roots 出发不可达

那么就说明这个对象已经没有被使用的可能,可以被回收。

例如:

GC Roots │ ↓ 对象 A │ ↓ 对象 B

A、B 都是可达对象。

而:

对象 C

没有任何 GC Root 可以到达,那么 C 就可能成为垃圾。


二十三、什么是 GC Roots?

常见 GC Roots 包括:

  • 虚拟机栈中的局部变量引用
  • 方法区中的静态变量引用
  • 方法区中的常量引用
  • JNI 本地方法栈中的引用
  • JVM 内部的一些对象

简单理解:

GC Roots 是垃圾回收进行可达性分析时的起点。


二十四、垃圾回收算法

常见垃圾回收算法:

标记-清除 标记-复制 标记-整理

二十五、标记-清除

过程:

第一步:标记垃圾对象 第二步:清除垃圾对象

例如:

[A][垃圾][B][垃圾][C] ↓ [A][空闲][B][空闲][C]

缺点:

容易产生内存碎片。


二十六、标记-复制

把内存划分成两块:

From To

存活对象复制到另一块区域。

例如:

From [A][垃圾][B][垃圾] ↓ To [A][B]

然后清空 From。

优点:

没有内存碎片。

缺点:

需要额外空间。

所以非常适合:

存活对象较少的新生代。


二十七、标记-整理

首先标记垃圾对象。

然后将存活对象移动到内存的一侧:

[A][垃圾][B][垃圾][C] ↓ [A][B][C][空闲空间]

相比标记-清除:

可以减少内存碎片。

常用于老年代。


二十八、Minor GC、Major GC、Full GC

22. 什么是 Minor GC?

主要针对:

新生代。

因为新生代对象死亡率比较高,所以 Minor GC 通常比较频繁。


23. 什么是 Full GC?

Full GC 通常会涉及整个堆以及方法区等区域,成本较高。

因此:

Full GC 过于频繁通常意味着 JVM 存在内存压力或配置问题。

面试时不要简单认为:

“Full GC 就一定等于整个堆完全回收。”

具体行为取决于垃圾收集器和 JVM 实现。


二十九、Stop-The-World

24. 什么是 STW?

STW:

Stop-The-World。

垃圾回收过程中,为了保证对象状态的一致性,JVM 可能暂停应用线程。

例如:

业务线程 ────────暂停────────→ 继续 ↑ │ GC

STW 时间越长:

应用响应延迟越明显。

因此现代垃圾收集器都会尽量降低 STW 时间。


三十、常见垃圾收集器

常见垃圾收集器包括:

Serial ParNew Parallel CMS G1 ZGC Shenandoah

实际面试中重点关注:

CMS、G1、ZGC。


三十一、CMS

CMS:

Concurrent Mark Sweep。

主要目标:

降低老年代垃圾回收时的停顿时间。

主要过程:

初始标记 ↓ 并发标记 ↓ 重新标记 ↓ 并发清除

其中:

  • 初始标记:STW
  • 并发标记:与用户线程并发
  • 重新标记:STW
  • 并发清除:与用户线程并发

CMS 的主要缺点:

使用标记-清除算法,会产生内存碎片。

CMS 已经在现代 JDK 中被淘汰/移除,因此更多作为 JVM 历史知识和面试题出现。


三十二、G1

25. 什么是 G1?

G1:

Garbage First。

G1 将整个堆划分成大量大小相等的 Region:

Heap ┌───┬───┬───┬───┐ │ R │ R │ R │ R │ ├───┼───┼───┼───┤ │ R │ R │ R │ R │ ├───┼───┼───┼───┤ │ R │ R │ R │ R │ └───┴───┴───┴───┘

不再严格要求一块区域必须属于 Eden、Survivor 或 Old。

一个 Region 可以根据实际情况扮演不同角色。


26. G1 为什么叫 Garbage First?

G1 会根据 Region 中:

垃圾最多、回收收益最高

的区域优先进行回收。

所以叫:

Garbage First。


三十三、G1 的特点

G1 主要优势:

  • 支持大堆内存
  • 可预测停顿时间
  • Region 化管理
  • 可以混合回收年轻代和老年代

因此:

G1 是现代 Java 应用中非常重要的垃圾收集器。


三十四、ZGC

27. ZGC 是什么?

ZGC 是一种低延迟垃圾收集器。

主要目标:

在非常大的堆内存下,仍然保持很低的 GC 停顿时间。

它大量使用并发处理,并通过着色指针、读屏障等技术降低 STW 时间。

适用于:

对延迟非常敏感的大型 Java 应用。


三十五、类加载机制

28. 什么是类加载?

当 JVM 第一次使用某个类时,需要将.class字节码加载到 JVM 中。

整体过程:

加载 ↓ 验证 ↓ 准备 ↓ 解析 ↓ 初始化

通常把:

验证 + 准备 + 解析

称为连接。

因此完整过程:

加载 → 连接 → 初始化

三十六、加载

29. 加载阶段做什么?

主要完成:

  1. 获取类的二进制字节流
  2. 将字节流转换为方法区中的运行时数据结构
  3. 创建对应的 Class 对象

最终:

.class ↓ JVM ↓ Class 对象

三十七、验证

验证阶段主要保证:

字节码符合 JVM 规范,不会危害 JVM 的安全。

包括:

  • 文件格式验证
  • 元数据验证
  • 字节码验证
  • 符号引用验证

三十八、准备

准备阶段:

为类的静态变量分配内存并设置默认值。

例如:

publicstaticintcount=10;

准备阶段可能是:

count = 0

而不是:

count = 10

真正赋值10的过程属于初始化阶段。


三十九、初始化

初始化阶段才真正执行类中的初始化代码。

例如:

publicstaticintcount=10;

最终:

count = 10

如果存在:

static{System.out.println("hello");}

也会在初始化阶段执行。


四十、类加载器

30. JVM 中有哪些类加载器?

常见的:

Bootstrap ClassLoader ↓ Platform ClassLoader ↓ Application ClassLoader

Bootstrap ClassLoader

负责加载 Java 核心类。

例如:

java.lang.*

Platform ClassLoader

负责加载平台相关类。

Application ClassLoader

负责加载应用程序 ClassPath 下的类。


四十一、什么是双亲委派?

类加载器收到类加载请求后:

Application ClassLoader ↓ Platform ClassLoader ↓ Bootstrap ClassLoader

先让父加载器尝试加载。

父加载器加载不了:

再由子加载器加载

所以叫:

双亲委派模型。


四十二、为什么要使用双亲委派?

主要有两个作用。

① 避免类重复加载

父加载器已经加载过:

java.lang.String

子加载器就不会再次加载。

② 保证 Java 核心类安全

例如有人自己写:

java.lang.String

正常情况下不会让自定义类加载器优先加载这个类。

因此可以防止核心 API 被随意替换。


四十三、什么情况下会打破双亲委派?

典型例子:

Tomcat 类加载机制、SPI 等。

例如 SPI:

JDBC ↓ DriverManager ↓ ServiceLoader ↓ 具体数据库驱动

这里存在:

父加载器加载的代码需要调用子加载器加载的实现类。

所以需要使用类似线程上下文类加载器等机制解决。

面试时知道这个例子即可。


四十四、JVM 调优

31. JVM 出现 OOM 怎么排查?

如果出现:

java.lang.OutOfMemoryError: Java heap space

可以按照以下思路排查:

出现 OOM ↓ 确认是哪一种 OOM ↓ 查看 JVM 参数 ↓ 查看堆使用情况 ↓ 分析 Heap Dump ↓ 定位大对象 / 内存泄漏 ↓ 优化代码或 JVM 参数

常见工具:

jps jstat jmap jstack jcmd VisualVM MAT

四十五、常见 JVM 参数

32. 常见堆内存参数有哪些?

例如:

-Xms-Xmx

分别表示:

-Xms → 初始堆大小 -Xmx → 最大堆大小

例如:

-Xms512m-Xmx2g

表示:

初始堆 512 MB,最大堆 2 GB。


33. 为什么一般建议 Xms 和 Xmx 设置成相同?

如果:

Xms < Xmx

JVM 运行过程中可能需要不断扩展堆。

如果设置:

Xms = Xmx

可以减少堆扩容相关的开销,使堆大小更加稳定。

但实际生产环境仍然需要结合具体应用和容器资源合理配置。


四十六、如何排查 CPU 过高?

假设 Java 服务 CPU 突然达到 100%。

可以:

1. top ↓ 找到高 CPU 进程 2. top -H -p PID ↓ 找到高 CPU 线程 3. 线程 ID 转成十六进制 4. jstack PID ↓ 找到对应线程 5. 分析线程正在执行什么代码

核心思路:

操作系统定位进程 → 定位线程 → jstack 定位 Java 代码。


四十七、如何排查内存泄漏?

常见思路:

发现内存持续上涨 ↓ 生成 Heap Dump ↓ MAT / VisualVM 分析 ↓ 查看对象数量和占用 ↓ 寻找 GC Roots ↓ 定位异常引用 ↓ 修复代码

常见原因:

  • 静态集合不断保存对象
  • ThreadLocal 使用不当
  • 缓存没有设置过期策略
  • 监听器没有正确注销
  • 集合只增加不删除

四十八、JVM 面试重点总结

如果时间有限,可以优先掌握下面这些。

第一梯队:必须掌握

JVM 内存

  • JVM 内存结构
  • 堆
  • 栈
  • 方法区
  • 程序计数器
  • 元空间
  • 栈帧

对象

  • 对象创建过程
  • 对象内存布局
  • 对象头
  • String 常量池
  • 对象晋升

GC

  • GC Roots
  • 可达性分析
  • 标记-清除
  • 标记-复制
  • 标记-整理
  • Minor GC
  • Full GC
  • STW

类加载

  • 类加载过程
  • 类加载器
  • 双亲委派
  • 为什么需要双亲委派

第二梯队:重点理解

  • G1
  • CMS
  • ZGC
  • 元空间
  • 指针压缩
  • OOM 排查
  • CPU 过高排查
  • Heap Dump 分析
  • JVM 常用参数

第三梯队:了解即可

  • TLAB
  • 逃逸分析
  • 标量替换
  • JIT
  • 栈上分配
  • 字节码执行过程
  • GC 日志分析
  • Shenandoah

四十九、JVM 知识体系

最后可以把整个 JVM 知识体系总结成:

JVM │ ┌─────────────────┼─────────────────┐ ↓ ↓ ↓ 运行时数据区 类加载机制 执行引擎 │ │ │ ┌────┼────┐ ┌────┼────┐ ┌────┴────┐ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ 栈 堆 方法区 加载 双亲 初始化 解释执行 JIT │ │ │ ├── 新生代 │ └── 老年代 │ └── 栈帧 │ ├── 局部变量表 ├── 操作数栈 └── 返回地址 │ ↓ GC │ ┌───────────┼───────────┐ ↓ ↓ ↓ GC Roots 回收算法 垃圾收集器 │ ┌─────┼─────┐ ↓ ↓ ↓ 清除 复制 整理 │ G1 / ZGC

五十、JVM 面试最重要的一条主线

如果把 JVM 面试题压缩成一条主线,可以这样理解:

Java 对象 ↓ 创建在哪里? ↓ 堆 ↓ 什么时候被回收? ↓ GC Roots 可达性分析 ↓ 怎么回收? ↓ 标记-清除 / 复制 / 整理 ↓ 谁负责回收? ↓ 垃圾收集器 ↓ 为什么 GC 会暂停程序? ↓ STW ↓ 如何降低停顿? ↓ G1 / ZGC

另一条主线:

.class 文件 ↓ 类加载器 ↓ 加载 ↓ 验证 ↓ 准备 ↓ 解析 ↓ 初始化 ↓ Class 对象 ↓ JVM 执行

因此,JVM 不应该只靠死记硬背。

真正理解下面这几个问题:

对象放在哪里?

对象什么时候可以被回收?

垃圾怎么回收?

类是怎么加载进 JVM 的?

JVM 出现 OOM、CPU 飙高时怎么排查?

基本就掌握了 JVM 面试的主干知识。


总结

对于 Java 后端实习面试来说,JVM 不需要一开始就钻到 HotSpot 源码。

建议学习顺序:

JVM 内存结构 ↓ 堆和栈 ↓ 对象创建与对象内存布局 ↓ GC Roots + 可达性分析 ↓ 垃圾回收算法 ↓ G1 / ZGC ↓ 类加载机制 ↓ 双亲委派 ↓ JVM 调优与故障排查

其中最值得深入理解的是:

JVM 内存结构 + 对象 + GC + 类加载

这四部分是 JVM 面试题的核心。

当面试官继续追问时,再往:

JIT、逃逸分析、TLAB、GC 日志、G1 源码

这些更底层的方向深入即可。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/7 2:50:20

2026拆解小程序系统:从工具属性看实体商家数字化破局路径

随着私域经营理念不断普及&#xff0c;小程序系统已经成为实体商家数字化转型里的重要一环&#xff0c;不少商家看到同行小程序取得不错的经营效果&#xff0c;就仓促上线系统&#xff0c;后续才发现系统和自身业务流程不兼容&#xff0c;数据分散割裂&#xff0c;难以实现用户…

作者头像 李华
网站建设 2026/10/7 2:49:53

Excel多行合并成一行并复制数据的四种高效方案

这几年的数据表格&#xff0c;大体逃不开两类活儿&#xff1a;一类是把一条数据拆成多行&#xff0c;另一类是把多行数据并成一行。前者容易&#xff0c;分列、转置、透视表拖两下就完事&#xff1b;后者才磨人&#xff0c;尤其像“多行合并成一行&#xff0c;同时把数据完整复…

作者头像 李华
网站建设 2026/10/7 2:47:52

嵌入式 Linux 共享内存访问延迟优化

一、问题引入 在嵌入式多进程开发中,跨进程大数据传输一直是性能瓶颈高发区:比如视频采集场景下每秒 30 帧 2MB 的原始图像传输、工业传感器高频数据汇总等。传统 IPC 方案(管道、消息队列、socket)需要经过两次内核态 / 用户态拷贝,传输 MB 级数据时会带来毫秒级延迟,C…

作者头像 李华
网站建设 2026/10/7 2:45:07

数字孪生驱动大模型工业知识库:为具身机器人植入领域专业经验

数字孪生工业知识库原理&#xff1a;机理数据与大模型语义融合 工业知识库技术演进&#xff1a;文档检索到孪生机理增强知识库 孪生知识库工程落地&#xff1a;机理知识抽取与知识更新难题 孪生知识库业务场景&#xff1a;工业运维具身机器人 工业机理知识库治理&#xff1a;工…

作者头像 李华
网站建设 2026/10/7 2:44:45

长辈的新爱好:帮家里人搭了个系统

我爸65岁&#xff0c;退休后管着老家一个家庭仓库——我妈的货源、我哥的农资、我姐的酒水&#xff0c;全堆在三间房里。 以前找我爸拿货靠打电话&#xff0c;他翻半天本子。上个月回家&#xff0c;我教他用搭贝&#xff0c;他一句话&#xff1a;「给家庭仓库建管理系统&#x…

作者头像 李华
网站建设 2026/10/7 2:44:30

Redis命令:HSETNX

Redis HSETNX 命令详细教程 HSETNX 仅在字段不存在时设置其值&#xff0c;字段已存在则不做任何操作。它返回 0 或 1&#xff0c;是字段级的“不存在才写入”原语。 资料合集&#xff1a;https://pan.quark.cn/s/10e98d308913、https://pan.quark.cn/s/f56bc69c5338 一、概览…

作者头像 李华