Java 内存模型全解析

69 阅读2分钟

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 + 自旋锁 + 队列阻塞

五、总结与建议

✅ 建议学习路径:

  1. 掌握 JVM 内存结构图
  2. 熟悉 GC 日志分析(如 -XX:+PrintGCDetails
  3. 实战调优:使用 jvisualvmjmapjstack 等工具
  4. 阅读源码:如 HotSpotoop, klass, GC 模块

六、HomeWork

问题

对象一定分配在堆上吗?

什么是 TLAB?

GC 如何判断对象是否可回收?

为什么方法区改为 Metaspace?