JVM 面试高频 100 题
定位: 按知识领域分类的 TOP JVM 面试题精选。每题标注考察点 + 展开方向 使用方式: 先扫描题目看自己能回答多少,遇到薄弱环节点开链接细读
说明: 面试最高频的 3 大领域——GC 机制、JVM 调优、JMM 与并发——合计占面试题的 70% 以上。JDK 21 新特性(ZGC 分代、虚拟线程)呈快速上升趋势。
一、字节码与 Class 文件(8 题)
1.1 Class 文件结构由哪些部分组成?
考察点: magic number、版本号、常量池、access_flag、this_class/super_class、interfaces、fields、methods、attributes
→ 详阅 **Class文件结构**
1.2 常量池中主要存放哪两类常量?
考察点: 字面量(文本字符串、final 常量值)和符号引用(类/接口全限定名、字段名和描述符、方法名和描述符)
→ 详阅 **Class文件结构**
1.3 什么是符号引用?什么是直接引用?什么时候完成转换?
考察点: class 文件中的符号引用在类加载的"解析"阶段(或初始化后的第一次使用)被替换为直接引用(内存指针或偏移量)
→ 详阅 **Class文件结构**、**类加载生命周期**
1.4 javap 怎么查看 class 文件?怎么看常量池、字节码指令、异常表?
考察点: javap -v、-c、-verbose 的区别和常用组合
→ 详阅 **实战javap反汇编分析**
1.5 字节码有哪些主流指令类别?举例说明。
考察点: 加载存储(aload/istore)、运算(iadd/isub)、对象操作(new/getfield/putfield)、方法调用(invokevirtual/invokespecial/invokestatic/invokeinterface/invokedynamic)、控制转移(ifeq/goto/ tableswitch)
→ 详阅 **字节码指令集概览**
1.6 invokedynamic 是什么?在 lambda 表达式中怎么用?
考察点: JDK 7 引入,调用点(Call Site)+ 引导方法(Bootstrap Method),lambda 的编译原理
→ 详阅 **字节码指令集概览**
1.7 什么是 ASM?怎么修改 class 文件?
考察点: 字节码操作框架的原理:访问者模式解析 class 文件 → 修改指令 → 输出新 class
→ 详阅 **ASM与Javassist字节码操作入门**
1.8 ASM 和 Javassist 有什么区别?怎么选?
考察点: ASM(直接操作字节码指令,性能高,学习曲线陡)vs Javassist(源码级 API,易用性好,性能略低)。AOP 框架选型
→ 详阅 **ASM与Javassist字节码操作入门**
二、类加载机制(12 题)
2.1 类的生命周期有哪几个阶段?
考察点: 加载 → 验证 → 准备 → 解析 → 初始化 → 使用 → 卸载。注意"解析"顺序可以在初始化之后(动态绑定)
→ 详阅 **类加载生命周期**
2.2 什么时候触发类的初始化?
考察点: new/getstatic/putstatic/invokestatic 四条指令、反射 Class.forName()、子类初始化触发父类初始化、Main 方法所在类
→ 详阅 **类加载生命周期**
2.3 什么是双亲委派模型?
考察点: 类加载器层级:Bootstrap ClassLoader → Extension/Platform ClassLoader → Application ClassLoader。父加载器缓存优先
→ 详阅 **双亲委派模型与打破**
2.4 双亲委派的优点是什么?
考察点: 避免重复加载(父加载器先在缓存查找);安全(核心类只能被 Bootstrap ClassLoader 加载,防止恶意替换);保证核心类的唯一性
→ 详阅 **双亲委派模型与打破**
2.5 怎么打破双亲委派?有什么场景?
考察点: 重写 loadClass()(不调 super.loadClass())。场景:SPI(JDBC DriverManager)、Tomcat(隔离各个 Web 应用)、OSGi(模块化)
→ 详阅 **双亲委派模型与打破**
2.6 SPI(Service Provider Interface)是怎么打破双亲委派的?
考察点: DriverManager(BootstrapClassLoader 加载)→ 需要加载第三方驱动(ApplicationClassLoader 加载)→ 利用 Thread Context ClassLoader 反转委派
→ 详阅 **OSGI与SPI模块化**
2.7 Tomcat 怎么实现 Web 应用之间的类隔离?
考察点: Common ClassLoader → Catalina ClassLoader → Shared ClassLoader → Webapp ClassLoader(每个应用一个,优先加载 WEB-INF/lib 和 WEB-INF/classes)
→ 详阅 **Tomcat与Spring类加载分析**
2.8 为什么 Tomcat 要打破双亲委派?
考察点: 两个 Web 应用依赖不同版本的同一个 jar——双亲委派会导致只能有一个版本被加载。Tomcat 优先在 Webapp ClassLoader 加载,打破这个限制
→ 详阅 **Tomcat与Spring类加载分析**
2.9 怎么写一个自定义 ClassLoader?
考察点: 继承 ClassLoader → 重写 findClass() → defineClass() 将字节码数组转为 Class 对象。加密解密、动态代理、热部署等都是典型场景
→ 详阅 **自定义类加载器实战**
2.10 类的验证阶段检查什么?
考察点: 文件格式验证(magic number、版本号)、元数据验证(是否有父类)、字节码验证(指令合法性)、符号引用验证(可访问性)
→ 详阅 **类加载生命周期**
2.11 类加载的"准备"阶段做了什么?
考察点: 为类的静态变量分配内存并设置零值(不是代码中写的初始值)。final 修饰的常量直接赋值
→ 详阅 **类加载生命周期**
2.12 OSGi 的类加载机制有什么特点?
考察点: 模块化 → 每个 Bundle 有自己的类加载器 + 显式声明的 Import-Package/Export-Package。类加载路径不是树(双亲委派)而是图
→ 详阅 **OSGI与SPI模块化**
三、运行时数据区(12 题)
3.1 Java 堆和栈有什么区别?
考察点: 堆(所有线程共享,存对象实例,GC 管理)vs 栈(线程私有,存局部变量/操作数栈,方法结束后自动释放)
→ 详阅 **Java堆**、**Java虚拟机栈**
3.2 方法区(Method Area)在 JDK 7、JDK 8 有什么区别?
考察点: JDK 7 方法区在 PermGen(堆外,可配置大小),JDK 8+ 改为 Metaspace(使用本地内存,不再 OOM as easily)
→ 详阅 **方法区与元空间**
3.3 运行时常量池(Runtime Constant Pool)和 Class 文件常量池的区别?
考察点: Class 常量池是静态存储在 class 文件中。运行时常量池是类加载后放到了方法区中的版本——可以动态添加新常量(如 String.intern())
→ 详阅 **运行时常量池**
3.4 程序计数器为什么是线程私有的?
考察点: 每个线程独立执行不同方法,需要在被切换回来时知道从哪条字节码指令继续执行。不同线程的 PC 互不相干
→ 详阅 **程序计数器**
3.5 本地方法栈和虚拟机栈的区别?
考察点: 虚拟机栈支撑 Java 方法执行(栈帧操作数栈/局部变量表),本地方法栈支撑 native 方法。HotSpot 将两者合二为一
→ 详阅 **本地方法栈**
3.6 直接内存是什么?为什么需要它?
考察点: NIO 的 DirectByteBuffer 使用的堆外内存(通过 Unsafe.allocateMemory 分配)。避免在堆和内核态之间的数据拷贝(零拷贝)。不受 -Xmx 控制但受物理内存限制
→ 详阅 **直接内存**
3.7 什么是栈帧(Stack Frame)?包含什么?
考察点: 局部变量表(this 在 slot 0、参数、局部变量)、操作数栈(执行字节码的操作台)、动态链接(指向运行时常量池的引用)、方法出口(返回地址)
→ 详阅 **Java虚拟机栈**
3.8 栈上分配(Stack Allocation)是什么?条件?
考察点: 逃逸分析判断对象不逃逸 → 直接在栈上分配(对象随方法结束自动销毁,不需要 GC)→ 实际是标量替换(把对象拆成多个局部变量)
→ 详阅 **分配策略(栈上分配与TLAB与大对象)**
3.9 为什么说 JDK 8 的元空间缓解了 OOM?
考察点: PermGen 有固定上限(-XX:MaxPermSize),类加载过多容易撑满。Metaspace 使用本地内存,默认仅受物理内存限制。应用中动态生成大量类(CGLIB 代理/Groovy/热部署)不再轻易 OOM
→ 详阅 **方法区与元空间**
3.10 运行时常量池和 String 常量池有什么关系?
考察点: String 常量池是运行时常量池的一部分(JDK 7 之后移到堆中)。.intern() 方法让堆中的字符串进入常量池
→ 详阅 **运行时常量池**
3.11 栈帧中操作数栈的设计目的是什么?
考察点: Java 虚拟机的指令集是基于栈的(不是寄存器架构)。操作数栈作为字节码指令的工作台——指令从操作数栈取入参,结果推回栈。优点是编译简单、指令紧凑
→ 详阅 **Java虚拟机栈**
3.12 什么是内存溢出(OOM)?栈和堆分别怎么 OOM?
考察点: 堆 OOM(new 太多对象)→ java.lang.OutOfMemoryError: Java heap space。栈 OOM(递归太深)→ StackOverflowError。线程太多创建栈 → OOM: unable to create new native thread
→ 详阅 **Java虚拟机栈**、**Java堆**、**OOM排查方法论**
四、对象分配与内存布局(8 题)
4.1 从 new 一个对象开始,JVM 内部发生了什么?
考察点: 类加载检查(是否已加载/初始化)→ 分配内存(指针碰撞 or 空闲列表,是否使用 TLAB)→ 内存空间清零(零值初始化)→ 设置对象头(MarkWord + KlassPointer)→ 执行 <init> 方法
→ 详阅 **对象创建过程**
4.2 对象的内存布局由哪三部分组成?
考察点: 对象头(MarkWord + KlassPointer [+ 数组长度])、实例数据(字段值)、对齐填充(Padding 到 8 字节倍数)
→ 详阅 **对象内存布局(MarkWord与Klass与实例数据)**
4.3 MarkWord 里存储了什么?它的状态变化?
考察点: 锁状态(无锁/偏向锁/轻量级锁/重量级锁/GC 标记)+ GC 年龄 + identity hashcode。MarkWord 是可变的——不同状态下存不同信息
→ 详阅 **对象内存布局(MarkWord与Klass与实例数据)**
4.4 对象访问的两种方式(句柄 vs 直接指针)?
考察点: 句柄(堆中有句柄池 → 间接引用,对象移动时只改句柄指针)vs 直接指针(reference 直接指向对象,更快但对象移动需要更新 reference)。HotSpot 默认直接指针
→ 详阅 **对象访问定位**
4.5 TLAB(Thread Local Allocation Buffer)是什么?
考察点: 每个线程在 Eden 区预分配的一块小空间(默认占 Eden 的 1%)。小对象直接在 TLAB 分配——不需要同步。大对象直接进入老年代(-XX:PretenureSizeThreshold)
→ 详阅 **分配策略(栈上分配与TLAB与大对象)**
4.6 什么是逃逸分析?怎么影响对象分配?
考察点: JIT 分析对象的作用域是否逃出当前方法或线程。不逃逸 → 标量替换(栈上分配)+ 锁消除。逃逸 → 堆分配
→ 详阅 **方法内联和逃逸分析**、**锁消除与标量替换**
4.7 大对象直接进入老年代的条件是什么?
考察点: -XX:PretenureSizeThreshold — 超过大小的对象直接进入老年代。避免在 Eden->Survivor 复制大对象的开销
→ 详阅 **分配策略(栈上分配与TLAB与大对象)**
4.8 对象的年龄是怎么管理的?什么时候晋升到老年代?
考察点: 每次 Minor GC 存活则 age+1。age 达到阈值(默认 15,-XX:MaxTenuringThreshold)→ 晋升老年代。动态年龄判定(Survivor 空间不足时低于阈值的对象也会晋升)
→ 详阅 **对象内存布局(MarkWord与Klass与实例数据)**
五、GC 机制(20 题——最高频)
5.1 JVM 怎么判断一个对象已死?
考察点: 可达性分析(Reachability Analysis)——从 GC Roots 出发遍历对象图,不可达的即为死亡。不是引用计数(Java 没有选择引用计数的原因:循环引用)
→ 详阅 **对象存活判定算法**
5.2 GC Roots 有哪些?
考察点: 虚拟机栈引用的对象、静态属性引用的对象、常量引用的对象、JNI 引用的对象、synchronized 持有的对象、JVM 内部引用(Class 对象/异常对象)
→ 详阅 **对象存活判定算法**
5.3 四种引用类型是什么?什么场景用?
考察点: 强引用(new,永不回收)、软引用(SoftReference,内存不足时回收 → 缓存)、弱引用(WeakReference,下次 GC 即回收 → ThreadLocal/WeakHashMap)、虚引用(PhantomReference,无法 get 对象,仅用于回收通知 → NIO DirectByteBuffer)
→ 详阅 **对象存活判定算法**
5.4 三种基本 GC 算法是什么?各自优缺点?
考察点: 标记-清除(快,但碎片)、标记-复制(无碎片,但浪费一半空间)、标记-整理(无碎片,但慢)。新生代用复制(大部分对象死,少部分存活),老年代用整理
→ 详阅 **GC基本算法(标记清除与复制与标记整理)**
5.5 分代收集的理论基础是什么?
考察点: 弱分代假说(大部分对象朝生夕死)→ 新生代和老年区分开收集。强分代假说(老年代对象趋向稳定)→ 老年代 GC 频率低。跨代引用用记忆集处理
→ 详阅 **GC基本算法(标记清除与复制与标记整理)**
5.6 什么是 Minor GC / Major GC / Full GC?
考察点: Minor GC(新生代,频繁,STW 短)、Major GC(老年代,通常伴随 Minor GC)、Full GC(整个堆 + 方法区,STW 长)
→ 详阅 **GC日志分析**
5.7 三色标记算法是什么?CMS 和 G1 怎么解决并发标记的问题?
考察点: 白色(未访问)→ 灰色(已访问但引用未扫描完)→ 黑色(已访问 + 引用全扫完)。问题:黑→白引用 → 对象漏标。CMS 方案(增量更新,重新标记)、G1 方案(SATB 快照,标记开始时记录对象图快照)
→ 详阅 **GC基本算法(标记清除与复制与标记整理)**
5.8 CMS 收集器的工作流程?有什么缺陷?
考察点: 初始标记(STW) → 并发标记 → 重新标记(STW) → 并发清除。缺陷:并发失败(Concurrent Mode Failure)→ 退化为 Serial Full GC、标记-清除产生碎片、浮动垃圾
→ 详阅 **垃圾收集器详解(Serial到ZGC)**
5.9 G1 相比 CMS 有哪些改进?
考察点: 分区堆(Region)→ 可预测停顿、标记-整理(不会碎片)、增量回收(选收益最高的 Region)、RSet 记录跨代引用(不需要扫描整个堆)
→ 详阅 **垃圾收集器详解(Serial到ZGC)**
5.10 G1 的 RSet 和记忆集是什么?
考察点: RSet(Remembered Set)记录其他 Region 对该 Region 的引用。GC 时通过 RSet 知道谁引用了当前 Region 的对象,不用扫描整个堆
→ 详阅 **垃圾收集器详解(Serial到ZGC)**
5.11 ZGC 为什么能做到亚毫秒停顿?
考察点: 彩色指针(指针即 metadata)+ 负载屏障(所有 obj.field 通过位运算检查指针颜色)+ 全并发(只有根扫描 <0.1ms 和阶段收尾 <0.1ms 是 STW)
→ 详阅 **ZGC原理**
5.12 分代 ZGC 的优势是什么?
考察点: 不分代每次要标记整个堆 → 对 80-95% 的短期对象来说是浪费。分代后 Young GC 只扫描年轻代 + Remembered Set,吞吐量提升 10-20%
→ 详阅 **ZGC原理**
5.13 CMS 和 G1 什么时候用哪个?
考察点: CMS(JDK 8 及之前的选项,需要低延迟但堆 <8GB)→ G1(JDK 9+ 默认,可预测停顿,适合 4-64GB 堆)。>64GB 考虑 ZGC。另注意 CMS 已标记废弃
→ 详阅 **垃圾收集器详解(Serial到ZGC)**
5.14 G1 的 MaxGCPauseMillis 是怎么控制停顿的?
考察点: 停顿预测模型(历史 Pause Time 平均 + 标准差)→ 选择收集代价最小的 Region 集合。实际效果:在可接受的吞吐量损失下,尽量达到目标停顿时间
→ 详阅 **垃圾收集器详解(Serial到ZGC)**
5.15 GC 日志怎么看?推荐什么工具?
考察点: -Xlog:gc*(JDK 9+ 统一日志)格式:时间戳 + 收集器 + GC 原因 + 堆变化 + 耗时。工具:GCViewer、GCEasy
→ 详阅 **GC日志分析**
5.16 Full GC 频繁怎么排查?
考察点: jstat -gcutil 查看 GC 频率 + jmap -histo 查看对象分布 + GC 日志分析老年代/元空间/Metaspace 扩容 + dump 堆分析最强引用链
→ 详阅 **FullGC频繁排查方法论**
5.17 Concurrent Mode Failure 是什么?
考察点: CMS 并发清理阶段,应用线程继续产生新对象导致老年代填满 → CMS 无法完成 → 退化为 Serial Old GC(STW,极慢)。解决方案:调大老年代/降低 CMS 触发阈值/换 G1
→ 详阅 **垃圾收集器详解(Serial到ZGC)**
5.18 G1 的 Humongous Allocation 是什么?
考察点: 超过 Region 大小 50% 的对象作为巨型对象分配在 Humongous Region。巨型对象不复制(直接清理而非整理),过多会导致 GC 频繁。控制方法:增大 Region 大小(-XX:G1HeapRegionSize)
→ 详阅 **垃圾收集器详解(Serial到ZGC)**
5.19 什么情况会触发 Full GC?
考察点: 老年代空间不足、Metaspace 扩容、System.gc()、CMS 的 Concurrent Mode Failure、G1 的 Evacuation Failure(复制失败)
→ 详阅 **GC调优参数速查**
5.20 常用的 GC 调优参数有哪些?
考察点: -Xms/-Xmx、-XX:NewRatio/-Xmn、-XX:SurvivorRatio、-XX:MaxTenuringThreshold、-XX:PretenureSizeThreshold、各收集器开关参数
→ 详阅 **GC调优参数速查**
六、JVM 调优与排查(15 题)
6.1 JVM 参数分为哪几类?
考察点: 标准参数(-D、-verbose:gc)、-X 参数(-Xmx、-Xms、-Xss)、-XX 参数(-XX:+PrintGCDetails、-XX:MetaspaceSize)
→ 详阅 **JVM参数分类速查**
6.2 -Xms 和 -Xmx 设置多少合适?
考察点: 一般设为相同值(避免运行时动态调整开销)。大小取决于总物理内存(留 20-30% 给 OS + 非堆),服务通常 4-32GB
→ 详阅 **JVM参数分类速查**
6.3 jstat 的常用命令和指标?怎么看 GC 是否正常?
考察点: jstat -gcutil pid (间隔) (次数) → YGC/YGCT/FGC/FGCT/GCT。观察 Young GC 频率(几秒一次)、Full GC 频率(应该极少)
→ 详阅 **常用工具(jps与jstat与jmap与jstack与jinfo)**
6.4 jmap 的使用场景?
考察点: jmap -heap(堆配置和使用情况)、jmap -histo(对象统计)、jmap -dump:format=b,file=heap.hprof(dump 堆,然后 MAT/Eclipse Memory Analyzer 分析)
→ 详阅 **常用工具(jps与jstat与jmap与jstack与jinfo)**
6.5 jstack 怎么查看线程状态?怎么分析死锁?
考察点: jstack pid → BLOCKED/WAITING/RUNNABLE 线程状态。死锁检测:jstack 输出末尾会直接显示 "Found one Java-level deadlock"
→ 详阅 **常用工具(jps与jstat与jmap与jstack与jinfo)**
6.6 OOM 排查的一般步骤?
考察点: 确定 OOM 类型(Java heap / Metaspace / Direct buffer / unable to create native thread)→ jmap dump → MAT 分析 Dominator Tree → 找到最重的 GC Root 链 → 定位到业务代码
→ 详阅 **OOM排查方法论**
6.7 CPU 飙升怎么排查?
考察点: top -H(找最耗 CPU 的线程)→ jstack 转 → 找该线程的堆栈(或使用 Arthas thread 命令直接定位)。常见原因:死循环、GC 线程繁忙、正则回溯、HashMap 并发扩容
→ 详阅 **CPU飙升排查方法论**
6.8 Full GC 频繁的最常见原因?
考察点: 堆太小(分配率 > GC 吞吐量)、大对象过多(直接进入老年代)、Metaspace 扩容导致 Full GC、System.gc() 被 RMI/NIO 等框架触发、内存泄漏(对象被 GC Roots 强引用)
→ 详阅 **FullGC频繁排查方法论**
6.9 Arthas 的 dashboard 和 thread 用法?
考察点: dashboard(实时 JVM 概览:线程/CPU/内存/GC)→ thread(显示线程堆栈 + CPU 时间)→ thread -b(查找当前阻塞其他线程的线程)
→ 详阅 **图形化工具(JVisualVM与Arthas)**
6.10 JVisualVM 有什么功能?
考察点: 图形化监控(CPU/内存/线程/GC 图表)、Sampler(采样分析热点方法)、Profiler(精确统计方法调用)、插件支持(Visual GC、BTrace)
→ 详阅 **图形化工具(JVisualVM与Arthas)**
6.11 生产环境遇到频繁 Young GC 怎么处理?
考察点: 新生代太小 → 增加 -Xmn(或调整 NewRatio)。Eden 很快填满 → 检查对象创建速率(是否过度 new 临时对象)。Survivor 太小导致对象提前晋升
→ 详阅 **GC调优参数速查**、**FullGC频繁排查方法论**
6.12 怎么看 GC 日志的停顿时间是否正常?
考察点: Young GC 应 <50ms,Full GC 应 <200ms(G1 目标可预测停顿)。停顿异常 → 检查堆大小、收集器选择、是否发生 CMS Concurrent Mode Failure / G1 Evacuation Failure
→ 详阅 **GC日志分析**
6.13 大厂实战案例:你能举一个你处理过的 JVM 问题吗?
考察点: STAR 原则(情况 → 目标 → 行动 → 结果)+ 技术细节(工具、参数、分析过程)。没有真实案例可以提前准备一个
→ 详阅 **大厂实战案例库**
6.14 JVM 在 Docker/容器中怎么配置内存?
考察点: JDK 8u131+ 支持 -XX:+UseContainerSupport(自动识别容器内存/CPU 限制)。-XX:ActiveProcessorCount 控制 CPU 核数。内存要设置 -Xmx 低于容器限制
→ 详阅 **JVM参数分类速查**
6.15 怎么看 Java 进程的启动参数?
考察点: jps -v、pgrep java | xargs jcmd 1 VM.command_line、/proc/pid/cmdline(Linux)、或者运行 jinfo -flags pid
→ 详阅 **常用工具(jps与jstat与jmap与jstack与jinfo)**
七、执行引擎与 JIT(8 题)
7.1 解释执行和编译执行的区别?
考察点: 解释执行(逐条翻译 bytecode → 机器码,启动快,执行慢)、编译执行(JIT 将热点方法编译为机器码,启动慢,执行快)。HotSpot 混合模式:先用解释器启动再编译热点
→ 详阅 **解释执行与编译执行**
7.2 C1 和 C2 编译器有什么区别?
考察点: C1(Client Compiler):编译快,优化少——适合客户端/启动快的应用。C2(Server Compiler):编译慢,优化激进(循环展开、内联、逃逸分析)——适合长期运行的服务端
→ 详阅 **C1与C2编译器与分层编译**
7.3 什么是分层编译(Tiered Compilation)?
考察点: 5 层:0(解释器)→ 1-2(C1 带 profiling)→ 3(C1 full profiling)→ 4(C2 极致优化)。方法从解释执行 → 统计信息足够 → C1 → 足够频繁 → C2。在启动速度和峰值性能间平衡
→ 详阅 **C1与C2编译器与分层编译**
7.4 方法内联是什么?什么条件会被内联?
考察点: 把调用方法的目标代码直接插入调用处(消除函数调用开销)。条件:方法体 < 325 字节(-XX:MaxFreqInlineSize)、热点(足够多的调用次数)、final/static/private 方法更容易
→ 详阅 **方法内联和逃逸分析**
7.5 逃逸分析做什么?它和栈上分配的关系?
考察点: 分析对象不逃逸 → 标量替换(把对象字段拆成局部变量,栈上分配),锁消除(去掉不可能有竞争的锁)。逃逸分析是 JIT 的重要优化基础
→ 详阅 **方法内联和逃逸分析**、**锁消除与标量替换**
7.6 锁消除(Lock Elision)和锁粗化(Lock Coarsening)是什么?
考察点: 锁消除:JIT 检测锁对象不会逃逸 → 去掉锁(如 StringBuffer 在方法内部使用时)。锁粗化:多个连续的加解锁合并为一个(如循环内的加解锁提到循环外)
→ 详阅 **锁消除与标量替换**
7.7 什么是 OSR(On-Stack Replacement)?
考察点: 正在解释执行的长时间循环 → JIT 编译完该循环 → 从解释模式"跳转"到编译后的机器码(同一方法的同一位置切换执行模式)。为了避免长循环在解释器里跑太久
→ 详阅 **C1与C2编译器与分层编译**
7.8 JITWatch 怎么使用?
考察点: 启动时加 -XX:+UnlockDiagnosticVMOptions -XX:+PrintCompilation -XX:+TraceClassLoading → JITWatch 加载日志 → 分析哪些方法被编译、内联决策、优化级别
→ 详阅 **JITWatch使用**
八、JMM 与并发(12 题)
8.1 JMM 和运行时数据区(内存区域)有什么区别?
考察点: 很多人把 JMM 和"堆/栈/方法区"搞混。JMM(Java Memory Model)定义的是线程和主内存之间的交互规则(可见性、有序性、原子性),不是内存区域的划分
→ 详阅 **JMM与happens-before**
8.2 volatile 的作用和原理?
考察点: 可见性:volatile 写 → flush 到主内存;volatile 读 → 从主内存读取(禁止寄存器缓存)。有序性:插入内存屏障(LoadLoad/StoreStore/LoadStore/StoreStore)禁止指令重排序。不保证原子性(i++ 不是线程安全的)
→ 详阅 **JMM与happens-before**
8.3 synchronized 的底层实现?
考察点: 字节码层面(monitorenter/monitorexit)。OS 层面(依赖于互斥锁 Monitor)。MarkWord 中的锁状态(无锁→偏向锁→轻量级锁→重量级锁)
→ 详阅 **JMM与happens-before**
8.4 happens-before 原则是什么?举例?
考察点: 程序顺序规则(单线程);volatile 变量规则(写 happens-before 读);锁规则(解锁 happens-before 加锁);传递性(A→B→C 则 A→C);线程启动/中断/终止规则等
→ 详阅 **JMM与happens-before**
8.5 什么是指令重排序?JMM 怎么限制?
考察点: 编译器重排、处理器重排(乱序执行/Store Buffer)。JMM 用 happens-before 划定"在哪些边界不能重排"——边界内 JVM 和硬件可以自由优化
→ 详阅 **JMM与happens-before**
8.6 Java 锁升级的过程?
考察点: 无锁 → 偏向锁(单线程竞争,MarkWord 存线程 ID)→ 轻量级锁(少量竞争,自旋 CAS,MarkWord 存线程栈中 Lock Record)→ 重量级锁(大量竞争,等待队列,MarkWord 存 Monitor 指针)。JDK 15+ 默认关闭偏向锁
→ 详阅 **对象内存布局(MarkWord与Klass与实例数据)**
8.7 CAS(Compare And Swap)的原理和 ABA 问题?
考察点: CPU 原子指令(cmpxchg)实现无锁更新。ABA 问题:值从 A→B→A 但 CAS 检测不出变化 → AtomicStampedReference(版本号)解决。另外 CAS 在竞争激烈时性能下降(自旋)
→ 详阅 **JMM与happens-before**
8.8 什么是 DCL(Double-Checked Locking)?有什么问题?
考察点: if (instance == null) { synchronized (this) { if (instance == null) instance = new Instance(); } }。问题:new 操作不是原子的(分配内存→初始化→赋值)。指令重排可能导致另一个线程看到未初始化完全的实例。需要 volatile 禁止重排
→ 详阅 **JMM与happens-before**
8.9 ThreadLocal 的原理?内存泄漏怎么发生的?
考察点: 每个 Thread 有一个 ThreadLocalMap(Entry 数组),key 是 ThreadLocal 弱引用。内存泄漏:ThreadLocal 被 GC 后 key 为 null,但 value 仍然存在且被强引用(Thread → ThreadLocalMap → Entry → value)。解决方案:用完调用 remove()
→ 详阅 **对象存活判定算法**
8.10 AQS(AbstractQueuedSynchronizer)的基本原理?
考察点: CLH 锁变体——volatile int state(资源状态)+ 双向 FIFO 等待队列。独占(ReentrantLock)vs 共享(Semaphore/CountDownLatch)。CAS 修改 state,失败则入队 park
→ 详阅 **JMM与happens-before**
8.11 final 关键字在 JMM 中的语义?
考察点: final 字段在构造方法结束后对其他线程可见(不需要额外同步)。JMM 禁止将 final 字段的写重排到构造方法之外。String 的不可变性靠这个保证
→ 详阅 **JMM与happens-before**
8.12 自旋锁和适应性自旋是什么?
考察点: 轻量级锁失败时,线程不立即挂起(挂起→恢复是 OS 态,开销大),而是自旋一小段时间等待锁释放。JDK 6 引入适应性自旋:JVM 根据上次自旋等待时间动态调整本次自旋次数
→ 详阅 **JMM与happens-before**
九、前沿与新特性(8 题)
9.1 ZGC、G1、CMS 怎么选?
考察点: 堆 <4GB → G1(ZGC 的 barrier 开销不划算);4-64GB 低延迟 → G1 或 ZGC 都可以;>64GB → ZGC。CMS 已废弃,不再推荐新项目使用
→ 详阅 **ZGC原理**、**垃圾收集器详解(Serial到ZGC)**
9.2 虚拟线程和平台线程的区别?底层原理?
考察点: 1:1(平台:每个虚拟线程一个 OS 线程)vs M:N(虚拟:多个虚拟线程映射到少量 OS 载波线程)。虚拟线程挂起/恢复是用户态(Continuation),不涉及 OS 内核
→ 详阅 **虚拟线程原理**
9.3 虚拟线程什么场景用?什么场景不能用?
考察点: I/O 密集型(HTTP 请求/数据库查询/消息消费)→ 极大提升并发。CPU 密集(计算/加密)→ 无优势。锁竞争激烈 → pinning 问题。synchronized 重的代码 → 建议改用 ReentrantLock
→ 详阅 **虚拟线程原理**
9.4 什么是 pinning?为什么 synchronized 会导致 pinning?
考察点: 虚拟线程进入 synchronized 块时"钉"在载波线程上→载波不释放。原因:synchronized 监视器绑定 OS 线程。解决方案:用 ReentrantLock 替换 synchronized
→ 详阅 **虚拟线程原理**
9.5 Project Valhalla 解决了 Java 的什么问题?
考察点: 对象不必要的 Identity 导致的三重代价:堆分配开销(对象头)、指针追逐(缓存缺失)、GC 压力。值类消除 Identity → 扁平内存布局、零 GC、基本类型泛型的可能
→ 详阅 **Project Valhalla(Value Objects)**
9.6 value class 和普通 class 的区别?
考察点: value class 无 Identity(== 比字段)、final(不可继承)、不可变、无 synchronized、扁平内存布局(数组是内联的)。但支持接口和方法
→ 详阅 **Project Valhalla(Value Objects)**
9.7 你了解哪些 JDK 21 正式发布的重要特性?
考察点: 虚拟线程(正式)、分代 ZGC(正式)、Record Pattern(正式)、Pattern Matching for switch(正式)、Sequenced Collections(正式)
→ 详阅 **虚拟线程原理**、**ZGC原理**
9.8 ZGC 的"彩色指针"和"负载屏障"怎么工作的?
考察点: 64 位指针中 4 位作颜色(M0/M1/Rmtd/Fin),42 位寻址(4TB)。Load barrier 读取时检查颜色——需要处理则走慢速路径更新指针。不需要额外 metadata 查询
→ 详阅 **ZGC原理**
十、综合开放题(7 题)
10.1 "从 new 一个对象开始讲 JVM 内部发生了什么"
考察点: 考察知识串联能力:类加载(02)→ 内存分配(TLAB/Eden)(04)→ 对象头设置(MarkWord)(04)→ 初始化 → 使用 → GC 回收(05)
→ 详阅 **对象创建过程**、**类加载生命周期**
10.2 "说下你对 JVM 内存模型的理解"
考察点: 区分"运行时数据区(堆/栈/方法区)"和"JMM(可见性/有序性/原子性)"——这是面试官常用来判断候选人是背概念还是真懂的陷阱
→ 详阅 **Java堆**、**Java虚拟机栈**、**JMM与happens-before**
10.3 "GC 调优做过吗?怎么做的?"
考察点: 系统性思维:确定指标(吞吐量/P99 延迟/GC 频率)→ 检查当前状态(jstat/jmap/GC 日志)→ 调整参数 → 验证。不是背参数,是方法论
→ 详阅 **FullGC频繁排查方法论**、**GC调优参数速查**
10.4 "遇到 OOM 怎么排查?"
考察点: 类型判断(堆/Metaspace/直接内存/线程)→ 工具使用(jmap dump → MAT Analysis)→ 找到泄漏或配置问题。典型场景:大对象、连接未释放、CGlib 代理类过多
→ 详阅 **OOM排查方法论**、**大厂实战案例库**
10.5 "说下 JVM 的知识体系?"
考察点: 检验是否把 JVM 的知识点串联成一张网,而不是零碎记忆。Class 文件→类加载→运行时数据区→对象分配→GC→调优→JIT→JMM→并发→前沿
→ 详阅 **JVM文库设计文档**
10.6 "为什么 JDK 8 到 JDK 21 的迁移这么受关注?"
考察点: 最关键的几个变化:ZGC(不分代 → 分代)、虚拟线程正式、GC 日志统一、G1 默认、模块化。迁移收益(性能 + 可维护性)和风险(模块化兼容性)
→ 详阅 **ZGC原理**、**虚拟线程原理**
10.7 "你如何自学 JVM 新技术?推荐什么资料?"
考察点: 学习习惯考察。推荐资源:官方 JEP(最新的特性说明)、Inside Java 博客、JVM 源码(OpenJDK)、《深入理解 Java 虚拟机》、本知识库
→ 详阅 **JVM文库设计文档**
📚 相关链接
- 各题目展开阅读请点[→ 详阅]链接
- **大厂实战案例库** — 真实案例参考
- **JVM文库设计文档** — 知识库设计全景
使用建议: 从每个分类中随机选 2-3 题自测。连续 3 轮能流畅回答 → 该分类掌握 搭配: **大厂真题解析** 提供完整的综合题回答结构和原理串联演示