JVM 堆体系

一、JVM 堆核心概念

1. 什么是 JVM 堆(Heap)

  • JVM 内存中最大的一块,线程共享区域
  • 唯一作用 :存放对象实例数组
  • 垃圾收集器(GC)唯一管理的区域
  • JVM 启动时创建,内存空间不连续
  • 所有线程共享,存在线程安全问题(需要同步)

2. 堆内存的两大特点

  1. 自动管理:不需要手动释放内存,GC 自动回收
  2. 可动态调整:通过 JVM 参数控制大小

3. 堆内存 = 两大区域

plaintext

复制代码
┌─────────────────────────────────────────┐
│              堆内存(Heap)               │
├───────────┬─────────────────────────────┤
│  年轻代   │           老年代              │
│ Young Gen │          Old Gen             │
└───────────┴─────────────────────────────┘

元空间 Metaspace 不属于堆!(JDK 8+ 取代永久代)


二、堆内存完整结构(分代模型)

1. 为什么要分代?

不同对象生命周期不同:

  • 朝生夕死 → 放年轻代(高频回收)
  • 长期存活 → 放老年代 (低频回收)分代 = GC 效率最大化

2. 标准分代结构

plaintext

复制代码
堆 Heap
├─ 年轻代 Young Gen(1/3 堆空间)
│  ├─ Eden 区      (8/10)
│  ├─ Survivor 0 区(1/10)
│  └─ Survivor 1 区(1/10)
│
└─ 老年代 Old Gen(2/3 堆空间)

3. 各区域作用

(1)Eden 区
  • 新对象诞生地
  • 满了 → 触发 Minor GC / Young GC
  • 绝大多数对象在这里被回收
(2)Survivor 区(S0 + S1)
  • 角色:对象中转站
  • 必须同时只有一个区有数据,另一个为空
  • 每次 Minor GC 后:存活对象 → 空的 Survivor
  • 解决内存碎片化
(3)Old 区
  • 存活时间长的对象
  • 满了 → 触发 Major GC / Full GC
  • Full GC 速度比 Minor GC 慢 10 倍以上
(4)Metaspace 元空间(JDK8+)
  • 存放:类信息、方法、常量池、静态变量
  • 直接使用本地内存,不受堆大小限制
  • 取代永久代(PermGen)

三、对象分配与晋升机制(核心流程)

1. 对象分配规则(面试必背)

  1. 新对象优先在 Eden 分配
  2. Eden 满 → Minor GC
  3. 存活对象 → Survivor 空区
  4. 年龄达标 → 晋升老年代
  5. 大对象直接进入老年代
  6. 老年代满 → Full GC

2. 对象晋升老年代的 4 个条件

  1. 年龄阈值默认 15 岁(每熬过一次 GC +1)
  2. Survivor 区相同年龄对象大小 > Survivor 一半
  3. 大对象(数组、长字符串)直接进老年代
  4. Minor GC 后存活对象太多放不进 Survivor

3. 一句话总结对象生命周期

Eden 创建 → Survivor 轮换 → 达标晋升 Old → 死亡/Full GC 回收


四、GC 类型与区别(面试高频)

表格

GC 类型 发生区域 速度 触发条件
Minor GC 年轻代 极快 Eden 满
Major GC 老年代 老年代满
Full GC 整堆 + 元空间 最慢 老年代满、System.gc ()、元空间满

重点

  • Full GC 会引发 STW(Stop The World)
  • 线上故障 90% 是 Full GC 频繁导致卡顿

五、JVM 堆参数实战(生产环境必备)

1. 最核心的 4 个参数

plaintext

复制代码
-Xms        初始堆大小
-Xmx        最大堆大小
-Xmn        年轻代大小
-XX:MetaspaceSize  元空间初始大小

2. 生产环境标准配置

plaintext

复制代码
# 堆初始 = 堆最大(避免扩容开销)
-Xms4g -Xmx4g

# 年轻代大小
-Xmn2g

# 元空间
-XX:MetaspaceSize=256m
-XX:MaxMetaspaceSize=512m

# 打印 GC 日志(必须开)
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-Xloggc:gc.log

3. 推荐比例

  • 年轻代:老年代 = 1 : 2
  • Eden : S0 : S1 = 8 : 1 : 1

六、堆内存溢出(OOM)实战

1. 堆 OOM 错误

plaintext

复制代码
java.lang.OutOfMemoryError: Java heap space

2. 常见原因

  1. 死循环创建对象
  2. 集合使用不当(缓存不清理)
  3. 一次性加载大量数据(如全表查询)
  4. 内存泄漏(对象无法被 GC 回收)

3. 快速排查工具

  1. jmap 导出堆快照
  2. jhat / MAT 分析泄漏
  3. jstat 实时查看 GC 情况
  4. Arthas 阿里开源诊断工具

4. 解决方案

  1. 优化代码,避免创建大量无用对象
  2. 增加堆内存 -Xmx
  3. 排查内存泄漏
  4. 优化 GC 算法

七、面试高频 10 问(直接背诵)

1. JVM 堆内存结构?

年轻代(Eden、S0、S1)+ 老年代 + 元空间。

2. 为什么分代?

不同对象生命周期不同,分代提高 GC 效率。

3. Minor GC 和 Full GC 区别?

前者回收年轻代,速度快;后者全堆回收,速度慢,STW。

4. 对象如何进入老年代?

年龄达标、大对象、Survivor 空间不足、年龄动态判断。

5. Eden:S0:S1 比例?

8:1:1。

6. 为什么要设置 -Xms = -Xmx?

避免堆内存动态扩容带来性能损耗。

7. 堆 OOM 如何排查?

看日志 → 导出堆快照 → MAT 分析 → 定位代码。

8. 什么是 STW?

Stop The World,GC 时暂停所有用户线程。

9. JDK8 为什么用元空间?

永久代容易 OOM,元空间使用本地内存,更灵活。

10. 内存泄漏和溢出区别?

泄漏是用不完释放不了;溢出是内存不够用。


八、核心总结

  1. 堆 = 年轻代 + 老年代,是 GC 管理的核心区域
  2. 对象分配流程:Eden → Survivor → Old
  3. Minor GC 轻量,Full GC 重量级
  4. 线上必须配置:-Xms=-Xmx + 开启 GC 日志
  5. 故障排查:GC 频繁 → 堆 OOM → 内存泄漏
相关推荐
庞轩px3 小时前
ThreadLocal 源码分析与内存泄漏问题
java·jvm·线程·threadlocal·内存泄露·key-value
Java成神之路-3 小时前
JVM 绑定机制详解:静态绑定、动态绑定与 invokedynamic
jvm
wuqingshun3141593 小时前
说说你对spring MVC的理解
java·开发语言·jvm
014-code3 小时前
ThreadLocal 详解
java·jvm·数据结构
黄昏恋慕黎明4 小时前
JVM的类加载机制
jvm
wuqingshun3141594 小时前
说一下@RequestBody和@ResponseBody的区别?
java·开发语言·jvm
小王不爱笑1324 小时前
JVM 垃圾收集器 (GC) 完整版
jvm
2401_8747325312 小时前
为你的Python脚本添加图形界面(GUI)
jvm·数据库·python