JVM
2024年3月25日大约 4 分钟
JVM
🔥 JVM 面试八股文
1️⃣ JVM 内存结构 🚀
✅ JVM 运行时数据区域
区域 | 作用 | GC 影响 |
---|---|---|
程序计数器 | 记录当前线程执行的字节码指令地址,线程私有 | ❌ 不会发生 OOM |
虚拟机栈 | 方法执行时存储局部变量、操作数栈、动态链接等 | ⚠ 栈溢出 (StackOverflowError ) |
本地方法栈 | 存放 native 方法的调用栈信息 | ⚠ 可能导致 OOM |
堆 | 存放对象实例,垃圾回收主要区域 | ✅ 受 GC 影响 (OutOfMemoryError ) |
方法区(元空间) | 存储类信息、常量、静态变量、JIT 编译代码 | ✅ JDK8 及以上使用 元空间 |
✅ 堆内存划分
- 新生代(Young):Eden(8)+ S0(1)+ S1(1),使用 标记-复制算法。
- 老年代(Old):存放存活时间长的对象,使用 标记-整理算法。
✅ 字符串常量池
- JDK7 及以前:位于方法区(永久代)。
- JDK8 及以后:位于堆内存,回收更高效。
2️⃣ JVM 对象创建 & 访问方式 🛠
✅ 对象创建过程
- 类加载检查:先检查类是否已经加载(类信息存储在方法区)。
- 分配内存:
- 指针碰撞(无内存碎片)。
- 空闲列表(有内存碎片)。
- 内存分配安全性:
- CAS + 重试
- TLAB(Thread Local Allocation Buffer):线程独享的小块堆内存。
- 初始化零值。
- 设置对象头(哈希码、GC 分代年龄、锁信息等)。
- 调用构造方法
init()
。
✅ 对象访问方式
方式 | 特点 | 优缺点 |
---|---|---|
句柄池 | 通过句柄找到对象地址 | 间接访问,修改对象地址时只需维护句柄池 |
直接指针 | 直接存储对象地址 | 访问速度更快(HotSpot 采用) |
3️⃣ JVM 垃圾回收(GC)♻
✅ 四种引用类型
引用类型 | GC 影响 | 用途 |
---|---|---|
强引用 | 不会被回收 | 普通对象引用 |
软引用 | 内存不足时回收 | 缓存 |
弱引用 | GC 时回收 | ThreadLocal |
虚引用 | 仅用于监听对象回收 | 管理直接内存 |
✅ 对象可达性判断
- 引用计数法(已淘汰):无法解决循环引用问题。
- 可达性分析算法(GC Roots):
- 栈帧局部变量
- 静态变量
- 类加载器
- JVM GC 采用此方法
✅ GC 算法
GC 算法 | 特点 | 优缺点 |
---|---|---|
标记-清除 | 标记存活对象,清除未标记对象 | 容易产生内存碎片 |
标记-复制 | 将存活对象复制到另一块区域 | 浪费一半空间 |
标记-整理 | 清理碎片,整理成连续空间 | 适用于老年代 |
分代回收 | 新生代(标记-复制)+ 老年代(标记-整理) | 优化垃圾回收效率 |
✅ 垃圾回收器
GC | 作用 | 特点 |
---|---|---|
Serial GC | 单线程,新生代(标记-复制) | Stop The World(STW) |
Parallel GC | 多线程,吞吐量优先 | JDK8 默认 GC |
CMS GC | 老年代,并发标记清除 | 低延迟,但有碎片 |
G1 GC | JDK9 默认 GC,分区回收 | 低延迟 + 高吞吐 |
✅ STW(Stop The World)
- GC 时暂停所有用户线程,确保 GC 过程一致性。
- G1 降低 STW 影响,通过增量式 GC 进行优化。
4️⃣ JVM 类加载机制 🔥
✅ 类加载过程
阶段 | 作用 |
---|---|
加载 | 读取 .class 文件,转换为 Class 对象 |
连接 | 验证(校验字节码)、准备(分配静态变量内存)、解析(符号引用转为直接引用) |
初始化 | 执行静态代码块 clinit() |
✅ 双亲委派模型
- 加载顺序:
Bootstrap
(rt.jar) →ExtClassLoader
(ext 目录) →AppClassLoader
(classpath) - 作用:防止 核心 API 被篡改(如
String
类)。 - 自定义类加载器:
- 不破坏双亲委派:继承
ClassLoader
,重写findClass()
。 - 打破双亲委派:重写
loadClass()
。
- 不破坏双亲委派:继承
✅ Tomcat 类加载机制
- 每个 Web 应用有独立的类加载器,避免类冲突。
- 打破双亲委派,优先加载 Web 应用中的类。
5️⃣ JVM 调优 🎯
✅ JVM 参数
参数 | 作用 | 示例 |
---|---|---|
-Xms | 初始堆大小 | -Xms2G |
-Xmx | 最大堆大小 | -Xmx5G |
-Xss | 每个线程栈大小 | -Xss256k |
-XX:NewRatio | 新生代与老年代比例 | -XX:NewRatio=2 |
-XX:MetaspaceSize | 元空间初始大小 | -XX:MetaspaceSize=256m |
✅ GC 监控工具
工具 | 作用 |
---|---|
jstack | 查看线程堆栈 |
jmap | 查看堆内存分布 |
jconsole | 图形化查看 GC |
Arthas | 阿里开源 JVM 诊断神器 |
✅ GC 调优策略
- 避免创建过多对象(减少 GC 频率)。
- 调整 GC 线程数(
-XX:ParallelGCThreads
)。 - 使用
intern()
进行字符串池优化。 - 减少 Full GC 触发频率(避免
System.gc()
)。
6️⃣ 其他 JVM 相关知识
✅ Java 为什么能跨平台?
- JVM 是与平台相关的,不同平台的 JVM 翻译
.class
文件,确保一次编译,处处运行。
✅ JIT(即时编译器)优化
优化方式 | 作用 |
---|---|
缓存 | 热点代码缓存,提高执行效率 |
逃逸分析 | 对象未逃出方法,直接在栈上分配 |
锁消除 | 移除不必要的锁,减少同步开销 |
✅ 如何判断两个类是否相同?
- 类加载器不同,则类不同。
🔥 总结
1️⃣ JVM 运行时数据区 & 内存分配机制。
2️⃣ GC 机制 & JVM 调优策略。
3️⃣ 类加载过程 & 双亲委派模型。
4️⃣ JIT 编译优化 & 逃逸分析。
5️⃣ GC 监控工具 & 调优方法。