JVM 运行时数据区的 7 大组成部分

目录

[先定核心划分:线程私有 vs 线程共享](#先定核心划分:线程私有 vs 线程共享)

[一、7 大组成部分](#一、7 大组成部分)

[(一)线程私有区:线程执行的 "专属执行空间"](#(一)线程私有区:线程执行的 “专属执行空间”)

[1. 程序计数器(Program Counter Register)](#1. 程序计数器(Program Counter Register))

[2. Java 虚拟机栈(Java Virtual Machine Stack)](#2. Java 虚拟机栈(Java Virtual Machine Stack))

[3. 本地方法栈(Native Method Stack)](#3. 本地方法栈(Native Method Stack))

[(二)线程共享区:全局数据的 "公共存储仓库"](#(二)线程共享区:全局数据的 “公共存储仓库”)

[4. 堆(Heap)](#4. 堆(Heap))

[5. 方法区(Method Area)](#5. 方法区(Method Area))

[6. 运行时常量池(Runtime Constant Pool)](#6. 运行时常量池(Runtime Constant Pool))

[7. 直接内存(Direct Memory)](#7. 直接内存(Direct Memory))

[二、7 大组成部分的核心关联关系](#二、7 大组成部分的核心关联关系)

[核心执行链路:线程私有区为 "执行引擎",线程共享区为 "数据仓库"](#核心执行链路:线程私有区为 “执行引擎”,线程共享区为 “数据仓库”)

关键节点的关联细节

[核心关联总结:3 层协同逻辑](#核心关联总结:3 层协同逻辑)

三、总结

JVM 运行时数据区是 JVM 执行 Java 程序时内存分配与管理的核心区域 ,JVM 规范定义了 7 大组成部分,其中线程私有区 随线程创建 / 销毁,线程共享区 由 JVM 进程统一管理、所有线程共用,且各区域分工明确、协同完成字节码的加载、执行、资源调度全流程。HotSpot 作为 JDK 默认 JVM 实现,对规范做了少量落地优化(如合并本地方法栈与 Java 虚拟机栈),以下均以HotSpot 虚拟机为核心讲解,先明确核心划分,再逐一拆解作用,最后梳理整体关联。

先定核心划分:线程私有 vs 线程共享

这是理解 7 大区域的基础,私有区保证线程执行的隔离性,共享区实现数据的全局复用,划分如下:

划分类型 包含的运行时数据区 核心特征
线程私有区 程序计数器、Java 虚拟机栈、本地方法栈 与线程生命周期绑定,独立无冲突
线程共享区 堆、方法区、运行时常量池、直接内存 全局唯一,需处理线程安全问题

注:运行时常量池物理上属于方法区 、逻辑上独立;直接内存不属于 JVM 规范原生定义,但因 NIO 广泛使用成为 HotSpot 必谈的核心区域,归为共享区。

一、7 大组成部分

(一)线程私有区:线程执行的 "专属执行空间"

每个线程启动时,JVM 会为其分配独立的程序计数器、Java 虚拟机栈、本地方法栈 ,仅服务于当前线程的字节码执行,线程结束后立即释放,无内存共享冲突、无需 GC 回收

1. 程序计数器(Program Counter Register)
  • 核心作用 :记录当前线程正在执行的字节码指令的行号指示器 ,是 JVM实现线程切换、指令顺序执行的核心
  • 关键细节
    1. 线程切换时,JVM 会保存当前线程的程序计数器值,恢复时读取该值,保证线程切换后能继续执行未完成的指令;
    2. 执行Java 方法 时,记录字节码指令地址;执行Native 本地方法 时,计数器值为undefined(本地方法由底层系统执行,无需 JVM 记录);
    3. JVM 运行时数据区中唯一不会抛出 OOM(内存溢出)的区域,内存占用极小,JVM 规范未规定具体大小。
2. Java 虚拟机栈(Java Virtual Machine Stack)
  • 核心作用 :为Java 方法的执行 分配和管理Java 栈帧,是 Java 方法调用、执行、返回的 "核心载体"。
  • 关键细节
    1. 每次调用一个 Java 方法,会在栈中压入一个栈帧 ,存储方法的局部变量表、操作数栈、方法出口、动态链接 等信息;方法执行完成,栈帧弹出并释放内存
    2. 局部变量表存储基本数据类型(8 种)、对象引用(引用地址,非对象本身)、returnAddress 类型(指向字节码指令地址);
    3. 可通过 JVM 参数-Xss设置栈大小(如-Xss1m),超出则抛出StackOverflowError(栈溢出) ,若无法为新线程分配栈内存则抛出OOM
    4. 仅存储方法执行的临时数据,不存储对象实例(对象实例存于堆)。
3. 本地方法栈(Native Method Stack)
  • 核心作用 :为Native 本地方法(C/C++ 实现) 提供执行支撑,管理本地栈帧,是 Java 代码与底层操作系统 / 本地代码的 "连接桥梁"。
  • 关键细节
    1. 功能与 Java 虚拟机栈一致,仅支撑的方法类型不同,存储本地方法的局部变量、寄存器状态、返回地址等;
    2. HotSpot 中与 Java 虚拟机栈合并实现 ,通过-Xss统一设置两者总内存大小(其他 JVM 如 J9 则独立分配);
    3. 异常类型与 Java 虚拟机栈一致:栈深度超出限制抛StackOverflowError,内存不足抛OOM

(二)线程共享区:全局数据的 "公共存储仓库"

由 JVM 进程启动时创建,所有线程共用,存储程序运行的全局数据 (对象实例、类信息、常量、全局资源等),是 GC(垃圾回收)的核心区域(堆是 GC 主战场,方法区为软回收),也是 OOM 的高频发生区域。

4. 堆(Heap)
  • 核心作用 :JVM存储对象实例和数组唯一区域 ,是 Java 程序中内存占用最大、GC 最频繁的区域,也是线程共享区的核心。
  • 关键细节
    1. JVM 启动时初始化,可通过-Xms(初始堆大小)、-Xmx(最大堆大小)配置,通常设置两者相等避免内存动态扩容的性能损耗;
    2. 现代 JVM(JDK1.8+)将堆划分为新生代(Eden 区 + Survivor0 区 + Survivor1 区)老年代,新生代存储新创建的对象、GC 频率高(Minor GC),老年代存储存活时间长的对象、GC 频率低(Major GC/Full GC);
    3. 堆中无引用的对象会被 GC 回收,若堆内存无法分配新对象且无法通过 GC 释放空间,抛出OOM
    4. 堆中仅存储对象实例数据 ,对象的类元信息、方法、常量等存于方法区。
5. 方法区(Method Area)
  • 核心作用 :存储 Java 程序的类元信息 (类的名称、访问修饰符、字段、方法、接口信息)、静态变量即时编译器编译后的代码缓存运行时常量池,是类加载后数据的 "存储仓库"。
  • 关键细节
    1. JVM 规范称其为 "方法区",JDK1.8 前 通过永久代(PermGen) 实现,JDK1.8 及以后移除永久代,改用元空间(Metaspace) 实现,元空间直接使用本地内存(而非 JVM 堆内存),避免永久代的 OOM 问题;
    2. 可通过参数配置:JDK1.7--XX:PermSize/-XX:MaxPermSize;JDK1.8+-XX:MetaspaceSize/-XX:MaxMetaspaceSize
    3. 方法区并非 "永久不变",其中的类元信息可被 GC 回收(如类卸载),但回收条件严苛,仅当类的所有实例被回收、类加载器被回收、类的 Class 对象无引用时才会卸载;
    4. 若方法区 / 元空间无法分配新的类元信息,抛出OOM
6. 运行时常量池(Runtime Constant Pool)
  • 核心作用 :存储类的常量信息 ,是编译期常量运行期动态生成常量的全局存储区域,是方法区的 "核心子区域"。
  • 关键细节
    1. 物理上属于方法区 ,逻辑上独立,类加载时会将字节码文件中的常量池(编译期生成的字面量、符号引用) 加载到运行时常量池中;
    2. 支持运行期动态生成常量 (如String.intern()方法,若字符串常量池无该字符串,会将其加入运行时常量池);
    3. 常量池中的数据会随类的卸载而回收,若运行时常量池满,抛出OOM
    4. 与字符串常量池的关系:字符串常量池是运行时常量池的子集,专门存储字符串字面量。
7. 直接内存(Direct Memory)
  • 核心作用 :为NIO(非阻塞 IO) 提供直接内存访问,绕开 JVM 堆的拷贝,提升 IO 效率,是 HotSpot 的 "扩展共享区"。
  • 关键细节
    1. 不属于 JVM 规范原生定义 ,但因 NIO 的广泛使用成为 JVM 运行时数据区的必谈部分,直接占用操作系统的本地内存
    2. java.nio.ByteBufferallocateDirect()方法分配,不受 JVM 堆大小(-Xmx)限制,但受本机总内存JVM 参数-XX:MaxDirectMemorySize 限制,超出则抛出OOM
    3. 直接内存的回收不依赖 JVM 的 GC ,而是通过Unsafe 类freeMemory()方法手动回收,若未及时回收会导致本地内存泄漏
    4. 核心优势:避免 JVM 堆与操作系统内核缓冲区之间的数据拷贝 (传统 IO 需两次拷贝:堆→内核缓冲区→磁盘 / 网络),NIO 通过直接内存实现零拷贝,大幅提升高并发 IO 性能。

二、7 大组成部分的核心关联关系

Java 程序的字节码加载→方法调用→对象创建→执行→资源回收 全流程,均由 7 大区域协同完成,核心关联可通过一次简单的 Java 方法调用串联,整体逻辑如下:

核心执行链路:线程私有区为 "执行引擎",线程共享区为 "数据仓库"

复制代码
类加载 → 方法区存储类元信息+运行时常量池加载常量 → 线程启动 → 程序计数器记录指令 → Java虚拟机栈/本地方法栈创建栈帧 → 堆创建对象实例 → 直接内存支撑NIO操作 → GC回收共享区无用资源

关键节点的关联细节

  1. 类加载与方法区 / 运行时常量池 :Java 类通过类加载器加载后,其类元信息(字段、方法、接口)存入方法区 ,编译期生成的常量(字面量、符号引用)存入运行时常量池(方法区子区域),为后续方法调用提供 "元数据支撑"。
  2. 线程启动与私有区初始化 :线程启动时,JVM 为其分配独立的程序计数器、Java 虚拟机栈、本地方法栈,程序计数器初始化为当前要执行的字节码指令地址,成为线程执行的 "导航仪"。
  3. Java 方法调用与栈 / 堆 / 程序计数器
    • 调用 Java 方法时,JVM 在Java 虚拟机栈压入栈帧,局部变量表存储方法的参数和临时变量;
    • 若方法中创建对象(如new Object()),JVM 在 中分配内存创建对象实例,栈帧的局部变量表中存储对象的引用地址(指向堆中的实例);
    • 程序计数器实时更新当前执行的字节码指令地址,保证方法按顺序执行,嵌套调用 / 递归调用时,栈帧依次压入 / 弹出。
  4. Native 方法调用与本地方法栈 / 堆 :调用native方法(如System.currentTimeMillis())时,JVM 切换到本地方法栈 创建本地栈帧,本地代码执行时若需操作 Java 对象,通过栈帧中的引用地址访问中的对象实例,执行完成后将结果返回给 Java 虚拟机栈。
  5. NIO 操作与直接内存 / 堆 :使用 NIO 进行文件 / 网络 IO 时,通过allocateDirect()直接内存分配缓冲区,若需将堆中的数据写入直接内存,仅需一次拷贝,实现零拷贝;IO 完成后,直接内存的数据可通过引用回写到堆中。
  6. 垃圾回收与共享区 :JVM 的 GC 核心针对线程共享区
    • 是 GC 主战场:Minor GC 回收新生代无用对象,Major GC/Full GC 回收老年代无用对象,回收后释放的内存用于新对象创建;
    • 方法区:回收未被引用的类元信息和无用常量,释放方法区 / 元空间内存;
    • 直接内存:需手动通过 Unsafe 类回收,若未回收,会导致本地内存泄漏,间接引发 JVM OOM。
  7. 异常触发与各区域边界 :各区域均有内存上限,超出上限则触发对应异常,核心关联:
    • 程序计数器:无异常;
    • Java 虚拟机栈 / 本地方法栈:栈深度超出→StackOverflowError,内存不足→OOM
    • 堆 / 方法区 / 运行时常量池 / 直接内存:内存不足→OOM

核心关联总结:3 层协同逻辑

  1. 数据存储层 :堆(对象实例)、方法区(类元信息)、运行时常量池(常量)、直接内存(NIO 数据)------ 为程序执行提供所有全局数据
  2. 执行引擎层 :程序计数器(指令导航)、Java 虚拟机栈(Java 方法执行)、本地方法栈(Native 方法执行)------ 为程序执行提供独立的线程执行空间,操作数据存储层的资源;
  3. 资源管理层 :GC(垃圾回收)------ 对数据存储层的无用资源进行回收,保证 JVM 内存的可持续使用。

三、总结

区域名称 归属类型 核心作用 关键特征 异常类型
程序计数器 线程私有 记录当前字节码指令行号 唯一不抛 OOM,内存极小
Java 虚拟机栈 线程私有 支撑 Java 方法执行,管理栈帧 与线程绑定,存储临时数据 StackOverflowError、OOM
本地方法栈 线程私有 支撑 Native 方法执行,管理本地栈帧 HotSpot 中与 Java 栈合并,由 - Xss 配置 StackOverflowError、OOM
线程共享 存储对象实例和数组 GC 主战场,-Xms/-Xmx 配置 OOM
方法区 线程共享 存储类元信息、静态变量、代码缓存 JDK1.8 + 为元空间,用本地内存 OOM
运行时常量池 线程共享 存储编译期 / 运行期常量 方法区子区域,支持动态常量 OOM
直接内存 线程共享 支撑 NIO 零拷贝,直接访问本地内存 非 JVM 规范定义,-XX:MaxDirectMemorySize 配置 OOM
相关推荐
CaracalTiger9 小时前
如何解决Unexpected token ‘<’, “<!doctype “… is not valid JSON 报错问题
java·开发语言·jvm·spring boot·python·spring cloud·json
江湖有缘17 小时前
自托管RSS解决方案:Docker化Fusion安装教程
java·jvm·docker
Chan1620 小时前
《深入理解Java虚拟机》| 类加载与双亲委派机制
java·开发语言·jvm·面试·java-ee·intellij-idea
闻哥1 天前
GET和POST请求的本质区别
java·网络·jvm·spring·http·面试·https
野犬寒鸦2 天前
从零起步学习并发编程 || 第八章:ThreadLocal深层解析及常见问题:避坑指南与最佳实践
java·服务器·开发语言·jvm·算法
生命因何探索2 天前
JVM知识汇总
jvm
heartbeat..2 天前
Java 中的类加载器的双亲委派模型:原理、层级与实现
java·开发语言·jvm·类加载器
Mr Aokey3 天前
JVM三剑客:内存模型、类加载机制与垃圾回收精讲
java·jvm
程序员ken3 天前
献给自己的一款个人管理的桌面软件(一)
java·开发语言·jvm