JVM 内存模型详解(进阶版)
一、为什么要了解 JVM 内存模型?
除了性能调优和并发编程,JVM 内存模型还直接影响:
- GC 行为分析:不同区域的 GC 策略不同(如 Minor GC vs Full GC)
- 内存泄漏排查:理解对象生命周期有助于定位泄漏点
- 类加载机制:类元信息的存储位置影响类卸载
二、JVM 内存结构总览
+-----------------------------+
| 方法区 Metaspace | ← 类元信息、常量池、静态变量
+-----------------------------+
| 堆 Heap | ← 对象实例、数组
| +-----------------------+ |
| | 新生代(Eden+S0+S1) | |
| | 老年代(Tenured) | |
| +-----------------------+ |
+-----------------------------+
| 虚拟机栈(每线程私有) | ← 局部变量表、操作数栈、动态链接
+-----------------------------+
| 本地方法栈(Native Stack) | ← JNI 调用支持
+-----------------------------+
| 程序计数器(PC Register) | ← 当前执行字节码地址
+-----------------------------+
三、各内存区域详解(进阶)
☕ 1. 堆(Heap)
- GC 分代策略:新生代采用复制算法,老年代采用标记-整理或 CMS/G1。
- 逃逸分析:JIT 编译器可将短生命周期对象分配到栈上,减少 GC 压力。
- 内存溢出案例:频繁创建大对象但未释放,可能导致
OutOfMemoryError: Java heap space
📚 2. 方法区(Metaspace)
- 类卸载机制:类被卸载的前提是 ClassLoader 不再被引用。
- 元空间调优:
-XX:MaxMetaspaceSize设置过小可能导致OutOfMemoryError: Metaspace
🧵 3. 虚拟机栈(VM Stack)
- 栈帧结构:包含局部变量表、操作数栈、方法返回地址等。
- 栈深度调优:
-Xss设置过小可能导致递归调用栈溢出。
🔧 4. 本地方法栈(Native Stack)
- 与 VM Stack 区别:专用于本地方法调用,尤其是 JNI。
- 调试技巧:使用
-XX:+PrintJNIResolving查看 JNI 加载情况。
📍 5. 程序计数器(PC Register)
- 线程切换核心:每个线程独立维护,保证线程上下文切换后能恢复执行。
四、JMM 与并发编程的关系(深入)
- 主内存 vs 工作内存:每个线程从主内存拷贝变量副本到工作内存,修改后再写回。
- volatile 的底层实现:
- 禁止指令重排序(通过内存屏障)
- 强制刷新工作内存到主内存
- synchronized 的实现:
- 依赖对象头中的 Monitor
- 底层使用 CAS + 自旋锁 + 队列阻塞
五、总结与建议
✅ 建议学习路径:
- 掌握 JVM 内存结构图
- 熟悉 GC 日志分析(如
-XX:+PrintGCDetails) - 实战调优:使用
jvisualvm、jmap、jstack等工具 - 阅读源码:如
HotSpot的oop,klass,GC模块
六、HomeWork
问题
对象一定分配在堆上吗?
什么是 TLAB?
GC 如何判断对象是否可回收?
为什么方法区改为 Metaspace?