一、JVM 的整体理解
如果用一句话概括 JVM:
JVM 是 Java 程序的运行环境,它负责加载字节码、分配内存、执行代码,并自动完成垃圾回收。
从整体结构来看,JVM 主要包含三大部分:
-
类加载子系统 :负责加载
.class文件到内存(方法区/元空间) -
运行时数据区:提供程序运行所需的内存空间
-
执行引擎:执行字节码(解释器 + JIT 编译器 + GC)
二、JVM 运行时内存结构
JVM 内存结构可以按"线程共享"和"线程私有"来理解:
1. 线程共享区域
1)堆(Heap)
-
存储对象实例
-
是垃圾回收的主要区域
2)方法区(Method Area)
-
存储类信息、常量、静态变量、JIT 代码等
-
JDK8 之后由 元空间(Metaspace) 实现
2. 线程私有区域
1)虚拟机栈(Stack)
- 每个方法调用都会创建一个栈帧
2)本地方法栈
- 执行 native 方法
3)程序计数器(PC)
- 记录当前线程执行位置
👉 核心记忆:
堆和方法区是共享的,栈/本地栈/PC 是私有的
三、虚拟机栈详解
虚拟机栈是线程私有的,用于管理方法调用。
1. 栈帧结构
每个方法调用都会创建一个栈帧,包含:
-
局部变量表
-
操作数栈
-
动态链接
-
方法返回地址
2. 栈的特点
-
先进后出(FILO)
-
方法结束自动出栈
-
不需要垃圾回收
3. 常见异常
1)StackOverflowError
-
递归过深
-
方法调用层级过多
2)线程过多导致内存不足
- OutOfMemoryError
4. 面试延伸点
-
栈中存的是对象引用,不是对象本身
-
局部变量通常线程安全(不逃逸)
四、方法区与元空间
1. 方法区作用
用于存储:
-
类信息
-
常量池
-
静态变量
-
JIT 编译代码
2. JDK 版本区别
-
JDK7 及之前:永久代(PermGen)
-
JDK8 之后:元空间(Metaspace)
👉 核心区别:
元空间使用本地内存,不再占用 JVM 堆内存
3. 常见问题
典型异常:
-
OutOfMemoryError: PermGen space -
OutOfMemoryError: Metaspace
常见原因:
-
动态代理生成大量类
-
类加载器泄漏
五、堆内存与分代模型(重点)
堆是 JVM 最重要的内存区域,采用分代思想管理对象。
1. 堆的结构
1)年轻代(Young)
-
Eden 区
-
Survivor 区(S0 / S1)
2)老年代(Old)
- 存放长期存活对象
2. 对象分配流程
-
新对象进入 Eden
-
Eden 满触发 Minor GC
-
存活对象进入 Survivor
-
多次存活后晋升到老年代
3. GC 类型
1)Minor GC
-
发生在新生代
-
频繁且速度快
2)Major GC / Full GC
-
发生在老年代或整堆
-
停顿时间长
六、垃圾回收机制(GC)
1. 为什么需要 GC
Java 中对象创建频繁,如果不自动回收,会导致内存泄漏。
2. STW(Stop-The-World)
GC 过程中,JVM 会暂停所有用户线程,只执行垃圾回收。
👉 核心优化目标:
-
吞吐量(执行效率)
-
停顿时间(用户体验)
七、对象存活判定
1. 引用计数法(已淘汰)
缺点:
- 无法解决循环引用问题
2. 可达性分析(主流)
核心思想:
-
从 GC Roots 出发
-
无法被访问的对象即为垃圾
3. 常见 GC Roots
-
栈中的引用
-
静态变量
-
常量
-
JNI 引用
八、垃圾回收算法
1. 标记-清除
优点:
- 简单
缺点:
- 内存碎片
2. 复制算法
适用于新生代:
- 将存活对象复制到新区域
优点:
- 无碎片
3. 标记-整理
适用于老年代:
- 标记后压缩内存
4. 分代收集
👉 核心思想:
不同区域使用不同算法
九、常见垃圾回收器
1. Serial
-
单线程
-
简单但停顿时间长
2. Parallel
-
多线程
-
追求吞吐量
3. CMS
特点:
-
低停顿
-
并发执行
缺点:
- 内存碎片
4. G1(主流)
特点:
-
Region 分区
-
可控停顿时间
-
高效回收
5. ZGC(了解)
特点:
-
超低延迟
-
适合大内存场景
十、三色标记(进阶)
对象分为:
-
白色(未访问)
-
灰色(处理中)
-
黑色(已完成)
问题:
-
漏标(误删)
-
浮动垃圾
解决:
-
CMS:增量更新
-
G1:SATB
十一、四种引用类型
-
强引用:不会被回收
-
软引用:内存不足才回收
-
弱引用:GC 即回收
-
虚引用:用于跟踪回收
十二、OOM 问题排查(实战重点)
1. 常见类型
-
堆内存溢出
-
元空间溢出
-
直接内存溢出
2. 排查步骤
-
开启 dump
-
使用工具分析(MAT / VisualVM)
-
找出大对象或异常引用
3. 常见原因
-
内存泄漏
-
缓存未清理
-
集合无限增长
-
线程池堆积
十三、线上问题排查
1. CPU 飙高
排查步骤:
-
定位进程
-
找高 CPU 线程
-
分析线程栈
工具:
-
jstack
-
Arthas
2. 死锁问题
-
使用 jstack 检测
-
查看线程锁依赖关系
十四、常用 JVM 参数
1. 堆
-
-Xms:初始大小
-
-Xmx:最大大小
2. 栈
- -Xss:线程栈大小
3. GC
-
-XX:+UseG1GC
-
-XX:MaxGCPauseMillis
4. Dump
- -XX:+HeapDumpOnOutOfMemoryError
十五、总结(面试必背)
可以用这三句话总结 JVM:
-
内存结构:堆和方法区共享,栈私有
-
对象生命周期:新生代 → 老年代 → GC 回收
-
性能核心:GC 停顿、吞吐量、内存管理
十六、一句话终极总结
👉 JVM 本质就是:
类加载 + 内存管理 + 垃圾回收 + 执行引擎