当前位置: 首页 > news >正文

JVM面试基础篇

🧠 建议的学习顺序(针对小白)

你可以按以下顺序逐步掌握:

  1. 理解 JVM 内存结构(这几乎是最常问)
  2. 了解 GC 与对象生命周期
  3. 掌握基本参数与调试工具
  4. 深入学习垃圾回收器与调优技巧(进阶)

🔹一、JVM 是什么?

**JVM(Java Virtual Machine)**是 Java 程序运行的虚拟平台。
它的主要职责包括:加载字节码、执行代码、内存管理、垃圾回收(GC)


🔹二、JVM 的内存结构(重点)

面试常问:“你了解 JVM 的内存结构吗?”

💡 JVM 运行时内存区域(JDK 8 之前):

区域名称作用生命周期
程序计数器当前线程执行的字节码行号每个线程独立
虚拟机栈方法调用,局部变量,操作数栈每个线程独立
本地方法栈本地方法执行(如 C)每个线程独立
堆(Heap)存放对象实例(几乎所有 new 的对象)所有线程共享
方法区(JDK8 改为元空间)存放类的元信息、常量池、静态变量等所有线程共享

JDK 8 以后,方法区元空间(Metaspace) 替代,存储在本地内存中。


🔹三、类加载机制(了解即可)

面试可能问:“Java 类是怎么被加载的?”

🌱 类加载生命周期:

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

👨‍🏫 类加载器(ClassLoader)

  1. 启动类加载器(BootstrapClassLoader):加载 JDK 核心类
  2. 扩展类加载器(ExtClassLoader)
  3. 应用类加载器(AppClassLoader)
  4. 自定义类加载器

💡 双亲委派机制:先让父加载器尝试加载,避免重复加载或安全问题。


🔹四、垃圾回收机制(GC) ⭐️⭐️⭐️(重头戏)

4.1 什么是 GC?

JVM 自动帮你清理不用的内存对象,防止内存泄漏。

4.2 内存分代模型(重点)

区域含义特点
新生代(Young)新生对象空间小,GC频繁
老年代(Old)长期存活对象空间大,GC少
永久代 / 元空间类的元信息JDK8 改为元空间,存在本地内存

新生代内部分为:

  • Eden 区(伊甸园)
  • S0 区(Survivor 0)
  • S1 区(Survivor 1)

4.3 常见垃圾收集器(了解即可)

收集器适用区域特点
Serial单线程,简单稳定适合小内存
Parallel多线程,吞吐优先性能较好
CMS并发低延迟,响应快老年代收集,容易碎片
G1(JDK9默认)分区回收,低延迟高级面试会问
ZGC / Shenandoah大内存+低延迟面向大数据应用

🔹五、JVM 常用参数(了解)

-Xms512m      # 初始堆内存
-Xmx1024m     # 最大堆内存
-Xss256k      # 每个线程栈大小
-XX:+PrintGCDetails    # 打印 GC 日志

🔹六、JVM 调优与排查(了解即可)

初学者面试可能只会问你是否了解工具

工具作用
jconsole图形化监控 JVM 运行
jstack查看线程堆栈,排查死锁
jmap查看内存快照(heap dump)
jstat查看 GC 状态
VisualVM图形工具,支持内存分析

🔹高频面试题整理

  1. JVM 内存结构都有哪些?各自存什么?
  2. 对象是如何在内存中分配的?什么情况下进入老年代?
  3. 你知道哪些垃圾回收器?G1 和 CMS 区别?
  4. GC Roots 有哪些?什么是可达性分析?
  5. 如何查看线程死锁?如何排查内存泄漏?
  6. 类加载器的双亲委派机制是怎么回事?

🎯 面试高频题:JVM 内存结构都有哪些?各自存什么?

✅ 一、JVM 内存结构(运行时内存区域)

在 Java 程序运行过程中,JVM 会将内存划分为以下几个主要区域(以 JDK 8 为例):

区域名称是否线程共享存储内容说明
程序计数器否(每线程独立)当前线程执行的字节码行号类比于 CPU 的指令指针
虚拟机栈(Java Stack)否(每线程独立)方法的局部变量、操作数栈、返回地址等每个方法执行时都会创建一个栈帧
本地方法栈(Native Method Stack)本地方法(C/C++)使用的栈与虚拟机栈类似,服务于 native 方法
堆(Heap)✅ 是所有对象实例、数组垃圾回收的主要区域,分为新生代、老年代
方法区(Method Area)/元空间(Metaspace)✅ 是类的元信息(结构)、常量池、静态变量等JDK 8 起从堆中移出,变为 Metaspace 存在于本地内存
运行时常量池(Runtime Constant Pool)✅ 是字面量、符号引用等常量方法区的一部分,用于类加载时填充常量表

🧠 二、各区域详解(简明好记)

1. 📌 程序计数器(每线程独立)

  • 用来记录每个线程执行到哪一行字节码。
  • 线程切换后能恢复到正确位置。
  • 它是唯一一个不会出现 OutOfMemoryError 的区域。

2. 📌 虚拟机栈

  • 每个线程创建时分配一块栈空间,用来执行方法。
  • 每次调用方法就会压入一个“栈帧”,包含局部变量、参数、操作数栈等。
  • 可能出现异常:
    • StackOverflowError(方法递归太深)
    • OutOfMemoryError(栈太大或线程太多)

3. 📌 本地方法栈

  • 用于执行 native 方法(C/C++ 实现)。
  • 与虚拟机栈类似,但专门为本地方法服务。

4. 📌 堆(Heap)(面试重点)

  • 所有 Java 对象(new 出来的)都会分配在堆中。
  • 是垃圾收集器的核心管理区域。
  • 分区模型(默认):
    • 新生代(Young):Eden + S0 + S1,GC频繁
    • 老年代(Old):存活时间长的对象
    • (JDK 8 之前:永久代;JDK 8 起:元空间)

5. 📌 方法区 / 元空间(JDK8 起)

  • 存储类的结构、常量池、静态变量、类加载信息等。
  • 以前叫“永久代”(PermGen),现在是“元空间”(Metaspace),存储在本地内存。

6. 📌 运行时常量池

  • 存储类中的符号引用、字符串字面量等常量。
  • 是方法区的一部分。
  • 常见的如 "abc" 字符串常量、final 基本类型值等。

✅ 三、图示记忆(结构层次)

                 ┌────────────────────────┐│      JVM 进程内存       │└────────────────────────┘↓┌────────────────────────────────────────────────────┐│                    线程私有区域                      │├─────────────┬────────────┬─────────────────────────┤│程序计数器    │Java 虚拟机栈 │本地方法栈                 │└─────────────┴────────────┴─────────────────────────┘↓┌────────────────────────────────────────────────────┐│                    线程共享区域                      │├────────────────────────┬───────────────────────────┤│        Java 堆         │       方法区 / 元空间       │└────────────────────────┴───────────────────────────┘

🧪 面试回答模板(记住这个版本可以直接说出口):

JVM 在运行时将内存分为线程私有和线程共享区域。线程私有的包括程序计数器、虚拟机栈和本地方法栈,主要用于方法执行和本地代码支持;线程共享的区域包括堆和方法区。堆是对象实例的主要存储区域,方法区(或 JDK8 起的元空间)则用于存储类结构信息、静态变量和常量池数据。


🎯 面试高频题:对象在内存中的分配过程

当我们用 new 创建一个对象时,对象通常被分配在 JVM 堆内存,具体是在 新生代的 Eden 区域

✅ 默认流程如下:

  1. 大部分新创建的对象 → 新生代的 Eden 区
    Eden 容量较小,创建对象快,GC(垃圾回收)也频繁。
  2. 当 Eden 区满了,会触发 Minor GC(小型垃圾回收)
    存活下来的对象会进入 Survivor 区(S0 或 S1)
    多次 Minor GC 后,仍然存活的对象可能晋升到 老年代

🔄 二、对象晋升到老年代的情况(重点)

以下是对象进入老年代的常见 三种情况

✳️ 1. 对象存活时间足够长

  • JVM 为每个对象设置一个 年龄计数器(Age)
  • 每经过一次 Minor GC,Age +1。
  • 达到阈值(如 15,可通过 -XX:MaxTenuringThreshold 设置)→ 晋升老年代。

✅ 示例:某对象在 Eden 中经历了多次 GC 仍然存活,Age 达到阈值 10 → 进入老年代。


✳️ 2. 大对象直接进入老年代

  • 大对象(如大量连续内存的数组)可能直接在老年代分配,避免在新生代复制耗时。

  • JVM 参数控制阈值:

    -XX:PretenureSizeThreshold=大小(如1M)
    
  • 常见于图片处理、大数据系统。


✳️ 3. Survivor 区放不下

  • 当 Survivor 区无法容纳所有存活对象时,部分对象会被直接晋升到老年代。

📦 补充:TLAB(线程本地分配缓冲区)

在高并发环境中,为了避免锁竞争,对象优先在线程的 TLAB(Thread Local Allocation Buffer)中分配。TLAB 是堆中为每个线程预留的一小块区域。


🧠 总结记忆口诀:

对象先到 Eden,小GC就清理;
死不了的进 Survivor,多次GC再升级;
对象大就老年代,Survivor装不下也去那儿待。

✅ 建议答题模板(面试时这样回答)

Java 中对象一般是在堆内存中分配,优先分配在新生代的 Eden 区。当 Eden 区满时会触发 Minor GC,存活对象移动到 Survivor 区。经历多次 GC 仍然存活的对象会根据年龄阈值晋升到老年代。此外,如果是大对象或 Survivor 区空间不足,也可能直接进入老年代。JVM 通过这种机制来优化垃圾回收效率,减少复制成本。

🎯 面试高频题:你知道哪些垃圾回收器?

JVM 中的垃圾回收器分为 新生代收集器老年代收集器,以下是主流的 GC 组合:

🔹 新生代垃圾回收器(Minor GC)

名称特点
Serial单线程,简单稳定,停顿时间长
ParNewSerial 的多线程版本
Parallel Scavenge高吞吐量,适合批处理系统

🔹 老年代垃圾回收器(Major/Full GC)

名称特点
Serial OldSerial 的老年代版本
Parallel Old高吞吐,Parallel 的搭档
CMS并发低延迟,响应快
G1区域化回收,适合大堆,低延迟

🔹 新一代垃圾回收器(JDK 11+)

名称特点
ZGC超低延迟(<10ms),大堆友好
Shenandoah红帽出品,停顿与堆大小无关

✅ 二、CMS 和 G1 的区别(重点)

维度CMS(Concurrent Mark Sweep)G1(Garbage First)
分代模型有:新生代 + 老年代逻辑分代,但统一管理多个 Region
GC 方式老年代并发标记 + 并发清除并发标记 + 区域化回收(Region)
停顿时间低(并发回收)但可能出现碎片更低,支持预测停顿时间(可配置)
空间整理不做整理,可能有碎片自动压缩整理,避免碎片
GC 并发阶段可能与应用线程同时运行支持并发标记、复制,吞吐更稳定
失败场景会触发 Full GC(Serial Old)不容易 Full GC,容错能力强
适用场景响应时间敏感,如 Web 应用大堆 + 低延迟场景,如大数据系统
是否过时是(JDK 14 起被标记为废弃)是 JDK 默认垃圾收集器(JDK 9+)

✅ 三、答题示范(面试模板)

Java 中常见的垃圾回收器包括 Serial、ParNew、CMS、G1、Parallel、ZGC 等。其中 CMS 和 G1 是低延迟收集器。CMS 采用“标记-清除”算法,虽然回收时停顿短,但存在内存碎片问题;G1 将堆划分为多个 Region,采用“标记-复制-整理”方式,能更好地控制停顿时间,同时避免内存碎片。相比 CMS,G1 更适合大内存、对响应时间有要求的应用场景。


🧠 小技巧:记忆口诀

CMS 扫地式回收,快但碎;G1 智能分区,稳又全。

🎯 面试高频题:什么是 GC Roots

GC Roots 是一组固定的、不会被垃圾回收器回收的引用对象,它们作为起点,通过引用链向下查找,从而决定哪些对象是“可达的(活着的)”。

简单理解:GC Roots 就是“活人名单”,从他们出发能找到的对象都不会被回收。


✅ 二、常见的 GC Roots 有哪些?

GC Root 来源举例或说明
✅ 虚拟机栈(栈帧中的局部变量表)方法中的局部变量、参数等(线程私有)
✅ 方法区中类的静态字段引用的对象如:static User user = new User();
✅ 方法区中常量引用的对象如:final String s = "abc",字符串常量池中的对象
✅ 本地方法栈中 JNI 引用的对象native 方法中引用的对象
✅ 活跃线程对象每个运行中的线程本身不会被 GC 回收
✅ JVM 内部的一些引用如系统类加载器、线程上下文类加载器等

补充:在某些 GC 场景中(如 G1),还可能包括:

  • 断点调试时的临时对象
  • JNI 的全局引用

✅ 三、什么是 可达性分析算法(Reachability Analysis)

🧠 定义:

可达性分析就是从 GC Roots 出发,沿着对象的引用链(reference)向下搜索,凡是能从 GC Roots 找到的对象就是“可达”的,反之就是“不可达”对象,可被 GC 回收。

GC Roots│▼
对象A → 对象B → 对象C↑被引用

如果一个对象没有被 GC Roots 直接或间接引用,那它就是“不可达对象”,GC 会标记为“垃圾”准备回收。


✅ 与“引用计数法”的对比:

算法特点缺点
引用计数法计数 +1/-1无法处理循环引用
可达性分析从 GC Roots 开始遍历引用图成本高一些,但准确可靠(现代 JVM 使用)

✅ 四、答题模板(可直接在面试中说):

JVM 在判断一个对象是否可以回收时,并不是通过引用计数法,而是通过“可达性分析算法”实现的。它以一组称为 GC Roots 的对象作为起点,遍历对象之间的引用链。凡是能从 GC Roots 追踪到的对象就是“可达”的,存活下来;否则就是“不可达”的对象,会被回收。常见的 GC Roots 包括虚拟机栈中的引用、本地方法栈中的 JNI 引用、方法区中静态字段和常量、以及活跃线程本身。


📌 图解助记(简化版)

          GC Roots│┌─────┴─────┐↓           ↓对象A         对象B↓对象C(被引用)

对象A/B/C 是可达对象;无法从 GC Roots 追踪到的对象就是垃圾对象。

🎯 面试高频题:如何查看线程死锁?

🔹 什么是线程死锁?

死锁是指多个线程互相等待对方释放资源,导致程序无法继续执行。比如:

Thread 1 获取锁 A,等待锁 BThread 2 获取锁 B,等待锁 A;
→ 相互等待,进入死锁状态。

🔹 如何查看死锁(面试 + 实战通用):

✅ 方法一:使用 jps + jstack
  1. 使用 jps 查看 Java 进程 ID:
jps
  1. 使用 jstack 查看线程堆栈信息:
jstack <pid> > threadDump.txt
  1. 打开 threadDump.txt,搜索关键词:
Found one Java-level deadlock:

若存在死锁,jstack 会标记并打印出互相等待的线程、锁信息等,非常直观。


✅ 方法二:IDEA 内置工具(图形化)
  1. 打开 Run → Debug,点击 Threads 视图。
  2. 看到红色标记线程就是陷入死锁的线程。

✅ 方法三:使用 VisualVMJConsole
  • 在“线程”面板可以查看线程状态。
  • 死锁线程通常会显示为“blocked”或“waiting”状态,VisualVM 能直接点出 Deadlock 的线程对。

✅ 二、如何排查内存泄漏?


🔹 什么是内存泄漏?

在 Java 中,对象不再被使用,但仍然被引用(没有被 GC 回收),造成堆内存不断增加,最终导致 OutOfMemoryError


🔹 内存泄漏常见场景:

场景示例
静态集合类static List list = new ArrayList();
监听器/回调未移除注册了监听器但未注销
ThreadLocal使用不当未清理 ThreadLocal.remove()
缓存不当自定义 Map 缓存没过期策略
数据库连接、文件流未关闭长期占用堆资源

🔹 如何排查内存泄漏?

✅ 方法一:使用 VisualVM
  1. 打开 VisualVM,连接目标进程。
  2. 查看 Monitor 面板的 Heap Usage 是否持续增长不回落。
  3. Heap Dump 面板中生成快照,对比“对象实例”是否持续增加。
  4. 使用“类实例树”查看谁在引用大对象。
  5. 使用“引用链”(Reference Tree)定位是哪个类保留了引用。

✅ 方法二:使用 MAT(Memory Analyzer Tool) 深度分析
  1. 导出堆快照(.hprof 文件):
jmap -dump:format=b,file=heap.hprof <pid>
  1. 使用 Eclipse MAT 打开:
  • 分析 Dominator Tree,查找内存占用最多的对象。
  • 使用 Leak Suspects Report 自动生成泄漏分析报告。

✅ 方法三:使用 Arthas

阿里开源的 Java 诊断神器,适合生产排查。

# 连接目标进程
./as.sh
# 查找类实例数量
sc -d com.example.YourClass

✅ 面试答题模板(精简版):

查看线程死锁可以使用 jstack 工具,它会直接输出 Java 层的死锁信息;也可以借助 VisualVMIDEA 等图形工具查看线程状态。排查内存泄漏则需要结合堆分析工具,如 VisualVM 或 MAT,通过 heap dump 快照分析哪些对象无法被 GC 回收,以及是哪些类/字段保留了引用,从而找出泄漏根源。


🎁 Bonus:一图速记

🔍 死锁排查
└── jstack → Found one Java-level deadlock
└── VisualVM → Threads 显示 BLOCKED
└── IDEA Threads → 红色标记🧠 内存泄漏排查
└── VisualVM → Heap Dump + Reference Tree
└── MAT → Dominator Tree + Leak Suspects
└── jmap + jhat → 分析堆内存快照
└── Arthas → 查询实例、引用链

🎯 面试高频题:类加载器的双亲委派机制是怎么回事?

✅ 一、什么是类加载器(ClassLoader)?

Java 中的类加载器负责.class 字节码文件加载进内存并生成 Class 对象。JVM 启动时并不会一次性加载所有类,而是按需加载。


✅ 二、Java 中的类加载器分类

类加载器作用加载路径
Bootstrap ClassLoader启动类加载器加载核心类(rt.jar、java.lang.*)
Extension ClassLoader扩展类加载器加载 $JAVA_HOME/lib/ext 目录下的类
App ClassLoader应用类加载器加载 classpath 下的类(如我们写的代码)
自定义 ClassLoader用户自定义覆盖或扩展默认加载机制

✅ 三、什么是双亲委派机制?

🌟 定义:

双亲委派机制是一种类加载层级结构,每个类加载器在加载某个类时,首先不会自己加载,而是把请求交给“父类加载器”去加载,只有父加载器无法加载时,才由当前加载器尝试加载。


🔄 加载流程:

AppClassLoader.loadClass("java.lang.String")│├──> 父:ExtClassLoader│└──> 父:BootstrapClassLoader│└──> 找到并加载 String.class

如果 Bootstrap 能加载,则其他加载器都不会重复加载。防止类的重复加载与安全问题。


✅ 优点:

优势说明
避免重复加载确保同一个类只由一个类加载器加载(如 String 类)
防止篡改核心类用户不能自定义一个假的 java.lang.String 被误加载
层级清晰、便于管理从上到下,清晰有序

✅ 四、能否打破双亲委派机制?

可以,但需要自定义 ClassLoader 并覆盖 loadClass() 方法的委派逻辑。很多框架(如 Tomcat、OSGi、SPI 插件机制)就会打破双亲委派,实现类隔离热部署等需求。


✅ 五、面试答题模板(推荐背诵):

Java 类加载器在加载类时采用双亲委派机制。即一个类加载器接到类加载请求时,会先把请求交给它的父类加载器去处理,只有父加载器无法完成加载时,才由当前加载器去尝试加载。这种机制可以避免重复加载,防止核心类被篡改,保障 JVM 的安全性。常见的类加载器有 Bootstrap、Ext、App 以及用户自定义加载器。


✅ 六、配套口诀助记

类加载有三层:启动、扩展、应用层;
加载类先问爹,不行才自己干;
安全防重复,双亲保平安。
CMS 扫地式回收,快但碎;G1 智能分区,稳又全。

相关文章:

  • 界面控件Kendo UI在实战应用——打通数据链路,重塑业务效率
  • leetcode0684. 冗余连接-medium
  • 3 Studying《深入理解Android卷(邓凡平)》1
  • [特殊字符] React 与 Vue 源码级对比:5大核心差异与实战选择指南
  • 盒模型小全
  • HTML5 浮动
  • ACL 2025 | 多维阅卷,智识觉醒:打开多模态大模型看图写作评估的认知之门
  • 湖北理元理律师事务所债务优化实践:法律框架下的生活重建方案
  • Java@Data 与 @NotNull 注解冲突问题
  • StackOverflowError
  • spring:使用注解@获取第三方bean实例
  • 表格里的图片链接怎么变成图片【附工具+源码演示】
  • 如何彻底删除Neo4j中的所有数据:完整指南
  • Java八股文——Spring「Spring 篇」
  • 2024蓝桥杯C/C++ B组国赛
  • EtherCAT转CANopen网关实现与伺服系统连通的配置实例探究
  • Spring Cache+Redis缓存方案 vs 传统redis缓存直接使用RedisTemplate 方案对比
  • Oracle集群OCR磁盘组掉盘问题处理
  • git pull 和 git fecth 的区别,远程仓库创建了新分支,可以用git fetch更新,可以看到远程创建的新分支
  • K8S中应用无法获取用户真实ip问题排查
  • 电脑上自己做科目一的网站/汕头网站制作设计
  • wordpress 悬赏功能/化工seo顾问
  • 门户网站制作需要多少钱/蚁坊软件舆情监测系统
  • UltraEdit做网站教程/竞价推广营销
  • wordpress美女图片站源码/江小白网络营销案例
  • 营销型网站的建设/北京百度科技有限公司电话