一、JVM 内存结构总览

JVM 运行时数据区分为 线程私有区域线程共享区域,整体结构如下:

二、线程私有区域(每个线程独立拥有,线程结束即释放)

1. 程序计数器(Program Counter Register)
  • 核心作用:记录当前线程执行的字节码指令地址(行号),是 JVM 中唯一不会抛出 OOM/StackOverflowError 的区域
    • 线程切换时,程序计数器会保存当前线程的执行位置,切换回来后能恢复执行;
    • 执行 native 方法(本地方法,如 C/C++ 实现)时,计数器值为 undefined(因为 native 方法不执行字节码)。
  • 特性:占用内存极小,生命周期与线程一致。
2. 虚拟机栈(Java Virtual Machine Stacks)
  • 核心作用:存储方法调用的「栈帧」(Stack Frame),每个方法从调用到执行完成,对应一个栈帧的入栈和出栈。
    • 栈帧包含:局部变量表(存储方法参数、局部变量)、操作数栈(计算临时数据)、方法返回地址(执行完后回到哪里)、动态链接(指向运行时常量池的方法引用)。
  • 核心特性
    • 线程私有,栈深度有限(默认几 MB);
    • 抛出异常:栈帧过多(如无限递归)→ StackOverflowError;栈空间动态扩展失败(极少发生)→ OutOfMemoryError
  • 关键参数-Xss(设置每个线程的栈大小,如 -Xss1m)。
3. 本地方法栈(Native Method Stack)
  • 核心作用:与虚拟机栈功能类似,但专门为 native 方法(如 Object.hashCode()System.currentTimeMillis())服务。
  • 核心特性
    • 线程私有,底层依赖操作系统原生方法;
    • 抛出异常:与虚拟机栈一致(StackOverflowError/OutOfMemoryError);
    • HotSpot 虚拟机将「虚拟机栈」和「本地方法栈」合并实现。

三、线程共享区域(所有线程共用,OOM 主要发生在此)

1. 堆(Java Heap)
  • 核心作用:JVM 中最大的内存区域,唯一目的是存储对象实例和数组(几乎所有 new 出来的对象都在这里)。
  • 核心特性
    • 线程共享,是垃圾回收(GC)的核心区域(常说的「GC 堆」就是指这里);
    • 堆内存可分为「新生代」和「老年代」(基于分代回收思想):
      • 新生代(Young Generation):存储新创建的对象,分为 Eden 区、Survivor 0(S0)、Survivor 1(S1),默认比例 8:1:1;
      • 老年代(Old Generation):存储新生代中存活多次 GC 仍未被回收的对象;
    • 抛出异常:堆空间不足 → OutOfMemoryError: Java heap space(最常见的 OOM)。
  • 关键参数
    • -Xms:堆初始大小(如 -Xms512m);
    • -Xmx:堆最大大小(如 -Xmx1024m,建议与 -Xms 设为相同值,避免动态扩展);
    • -XX:NewRatio:新生代与老年代的比例(如 -XX:NewRatio=2 → 新生代:老年代 = 1:2);
    • -XX:SurvivorRatio:Eden 与 Survivor 的比例(如 -XX:SurvivorRatio=8 → Eden:S0:S1=8:1:1)。
2. 元空间(Metaspace,JDK 8+)
  • 核心作用:替代 JDK 7 及之前的「永久代(PermGen)」,存储类的元数据(类结构、方法信息、常量池、注解等)。
  • 核心特性
    • 线程共享,默认使用操作系统的「本地内存」(而非 JVM 堆内存),但可通过参数限制大小;
    • 元数据由类加载器加载,类加载器被回收时,对应的元数据才会被清理;
    • 抛出异常:元空间不足 → OutOfMemoryError: Metaspace(常见于动态生成大量类的场景,如动态代理、热部署)。
  • 关键参数
    • -XX:MetaspaceSize:元空间初始阈值(触发 GC 的阈值,默认约 21MB);
    • -XX:MaxMetaspaceSize:元空间最大大小(默认无限制,建议显式设置,如 -XX:MaxMetaspaceSize=256m)。
3. 直接内存(Direct Memory,非 JVM 规范)
  • 核心作用:不属于 JVM 规范定义的内存区域,但 NIO 频繁使用(堆外内存),用于提升 IO 效率(减少堆内存与系统内存的拷贝)。
  • 核心特性
    • 不受 JVM 堆大小限制,但受操作系统总内存限制;
    • 抛出异常:直接内存不足 → OutOfMemoryError: Direct buffer memory
  • 关键参数-XX:MaxDirectMemorySize(设置直接内存上限,默认等于 -Xmx)。

四、JVM 内存模型 vs Java 内存模型(JMM)

很多人会混淆这两个概念,核心区别如下:

表格

维度 JVM 内存结构(物理) Java 内存模型(JMM,逻辑)
定义 JVM 运行时的内存物理划分 多线程编程的内存可见性规范
关注对象 内存区域的划分、存储内容、GC 线程间数据同步(可见性、原子性)
核心问题 OOM、StackOverflowError 指令重排序、缓存一致性、线程安全
典型场景 调优 JVM 参数、排查内存泄漏 编写线程安全代码(如 volatile)

五、核心调优与问题排查要点

  1. 堆调优
    • 优先设置 -Xms = -Xmx,避免堆动态扩展的性能开销;
    • jstat -gc <pid> 监控 GC 频率,若 Minor GC 频繁,可增大新生代;若 Full GC 频繁,排查内存泄漏或增大老年代。
  2. 元空间调优
    • 动态生成大量类的应用(如 Spring Boot、MyBatis),需适当增大 MaxMetaspaceSize
    • 避免无用类的加载(如关闭不必要的依赖)。
  3. 栈调优
    • -Xss 不宜过大(否则线程数受限),也不宜过小(否则易栈溢出),默认 1-2MB 足够大部分场景。

总结

  1. JVM 内存结构分为线程私有区(程序计数器、虚拟机栈、本地方法栈)和线程共享区(堆、元空间),直接内存是堆外常用区域;
  2. 程序计数器无内存异常,虚拟机栈易出 StackOverflowError,堆 / 元空间 / 直接内存易出 OutOfMemoryError
  3. JVM 内存结构是物理划分(解决内存分配 / GC),Java 内存模型(JMM)是逻辑规范(解决多线程同步),二者需区分开。
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐