跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书GitHub 精选镜像AI 生图工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
Javajava算法

JVM 运行时数据区域详解与内存结构分析

JVM 运行时数据区域涵盖程序计数器、虚拟机栈、本地方法栈、堆和方法区五大板块。其中前三个为线程私有,后两个为线程共享。堆用于存储对象实例,是垃圾回收的主要区域;方法区存储类信息、常量池及静态变量。理解各区域的职责与交互机制,有助于排查内存溢出问题并优化 JVM 参数配置。

微码行者发布于 2026/3/29更新于 2026/8/1746 浏览
JVM 运行时数据区域详解与内存结构分析

JVM 运行时数据区域,通常也被称为内存布局,是理解 Java 并发与性能优化的基石。需要注意的是,它与 Java 内存模型(JMM)是两个完全不同的概念。该区域主要由以下五大部分组成:

JVM 内存结构示意图

1. 程序计数器(线程私有)

程序计数器的核心作用是记录当前线程执行的行号。它是一块较小的内存空间,可视为当前线程所执行字节码的行号指示器。

如果当前线程正在执行的是 Java 方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是 Native 方法,这个计数器值为 undefined。值得注意的是,程序计数器是唯一一个在 JVM 规范中没有规定任何 OOM(内存溢出)情况的区域。

在 JVM 中,多线程是通过线程轮流切换来获得 CPU 执行时间的。因此,在任一具体时刻,一个 CPU 内核只会执行一条线程中的指令。为了线程切换后能恢复到正确的执行位置,每个线程都需要有一个独立的程序计数器,且不能互相干扰。

我们通过一段简单的代码及对应的字节码指令来看看它的作用:

public class PcDemo {
    public static int add(int a, int b) {
        return a + b;
    }
}

对应的字节码指令大致如下:

0: iload_0 // 从局部变量表中加载变量 a 到操作数栈
1: iload_1 // 从局部变量表中加载变量 b 到操作数栈
2: iadd    // 两数相加
3: ireturn // 返回

执行流程如下:

  1. 初始状态:当方法开始执行时,PC 计数器设置为 0,指向第一条指令 iload_0。
  2. 执行第一条指令:将局部变量表中索引为 0 的整数(即参数 a)加载到操作数栈顶。完成后 PC 更新为 1。
  3. 执行第二条指令:将索引为 1 的整数(即参数 b)加载到操作数栈顶。完成后 PC 更新为 2。
  4. 执行第三条指令:弹出栈顶两个整数相加,结果压入栈顶。完成后 PC 更新为 3。
  5. 执行最后一条指令:弹出结果作为返回值,方法结束。

若发生线程切换,例如线程 A 和线程 B 同时运行:

  • 线程 A 的 PC = 2(下一条应执行 iadd)
  • 线程 B 的 PC = 4(假设其已执行完,或处于不同状态)

这确保了每个线程都能独立维护自己的执行上下文。

2. Java 虚拟机栈(线程私有)

Java 虚拟机栈(JVM Stack)是线程私有的运行时数据区,生命周期与线程相同。当一个方法被调用时,JVM 会为该方法创建一个栈帧(Stack Frame)并压入虚拟机栈中。栈帧主要包含局部变量表、操作数栈、动态链接和方法返回地址等信息。当方法执行结束时,对应的栈帧会被弹出。

虚拟机栈结构示意图

栈帧内部的具体构成包括:

  1. 局部变量表(Local Variable Table):存储方法参数和局部变量(基本类型和对象引用)。对象引用存储的是堆中对象的地址而非对象本身。局部变量表大小在编译期确定,以 slot(槽位)形式存放。
  2. 操作数栈:用于临时存放参与运算的数据和保存计算结果的栈结构。字节码指令通过入栈(push)和出栈(pop)完成计算,相当于 JVM 执行计算的'工作台'。
  3. 动态链接:将常量池中的符号引用解析为实际方法或字段引用的过程。好比通讯录中通过名字查找电话号码,名字是符号引用,号码是真实引用。
  4. 方法返回地址:方法执行结束后,程序需要返回到调用者继续执行的位置。简单来说,就是'这个方法执行完后,下一条要执行的字节码在哪里'。

3. 本地方法栈(线程私有)

本地方法栈(Native Method Stack)与 Java 虚拟机栈类似,区别在于前者为虚拟机使用的 Native 方法服务,后者则为 Java 方法服务。其运行机制与虚拟机栈基本一致,此处不再赘述。

4. 堆(线程共享)

堆(Heap)是 JVM 中用于存放对象实例和数组的运行时内存区域,所有线程共享。当代码创建对象时,本质上就是在堆里申请一块内存。

User user = new User();
// 线程栈:user (引用地址)
// 堆:-------> User 对象实例
// 栈里只保存引用(reference),真正的数据在堆里。这也是为什么 Java 对象通常说是'在堆上分配'。

为什么堆必须共享? 设想这样一个场景:

User user = new User();
ThreadA 修改 user.name
ThreadB 读取 user.name

如果堆不是共享的,每个线程都有自己的对象副本,数据就会不同步。因此 JVM 的设计非常明确:对象数据在堆里共享,执行过程在栈里隔离。

常见的 JVM 参数 -Xms10m(最小启动内存)和 -Xmx10m(最大运行内存)都是针对堆的设置。

堆也是垃圾收集器管理的主要区域,因此被称为 GC 堆。基于分代收集算法,堆细分为新生代和老年代。新生代又包含 Eden 空间、From Survivor、To Survivor 等。进一步划分旨在更好地回收内存或更快分配内存。

堆最容易出现的错误是 OutOfMemoryError:

  • OutOfMemoryError: GC Overhead Limit Exceeded:JVM 花太多时间执行垃圾回收却只能回收很少的堆空间。
  • java.lang.OutOfMemoryError: Java heap space:创建新对象时,堆内存不足以存放。这与物理内存无关,主要取决于配置的虚拟机内存大小。

5. 方法区(线程共享)

方法区是 JVM 规范上的逻辑区域,在不同 JDK 版本实现不同。JDK 7 时称为永久代(PermGen),JDK 8 起被元空间(Metaspace)取代。

注意:方法区 ≠ 元空间。方法区是规范里的概念,元空间是一种实现方式。

方法区主要存储什么?

  1. 类的结构信息:JVM 加载类时,会将类名、父类、接口、字段、方法、访问修饰符等元数据存储在此。
  2. 运行时常量池:Class 文件中的常量池,包含字符串常量、类名、方法名、字段名及符号引用。例如 String s = "hello"; 中的 "hello" 引用会进入运行时常量池。
  3. 静态变量(static 变量):类级别变量,不随对象销毁而销毁,存放在方法区。
  4. JIT 编译后的代码:热点方法被即时编译器(JIT)编译成的本地机器码也存储在此区域。

理解了这些区域的职责与交互机制,对于排查内存溢出问题及优化 JVM 参数配置至关重要。

目录

  1. 1. 程序计数器(线程私有)
  2. 2. Java 虚拟机栈(线程私有)
  3. 3. 本地方法栈(线程私有)
  4. 4. 堆(线程共享)
  5. 5. 方法区(线程共享)
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • 基于 LangChain 的 AI Agent 智能体:技术原理与实践
  • 没有项目经验,如何快速转行 AIGC 产品经理?
  • 10 款降低 AIGC 检测率工具免费功能横向评测及排行榜
  • 从 BERT 到 GPT:Transformer 模型在 AI 发展中的作用
  • OpenClaw 框架深度解析:AI Agent 的安全挑战与演进路径
  • 前端微前端实践:如何避免应用沦为巨石单体
  • 基于 Java 与高德地图的县域烟花销售点 GIS 盘点实践
  • 手势控制电脑方案分析与 Python 实现示例
  • OpenClaw 本地 AI 助手安装与配置实战指南
  • Android 插件化开发:代理 Activity 模式详解
  • 使用 LangChain 搭建本地知识库系统
  • 零基础自学Python的五个实用方法与学习路线
  • Face Fusion 人脸融合实战:云端部署与参数调优指南
  • Android 快递物流信息布局实现详解
  • C++多线程编程:测试 Windows 下最大线程数
  • 原生多模态AI架构:统一训练与跨模态推理的系统实现与性能优化
  • 本地服务器部署Text generation并添加code llama实现远程多人协作
  • Java 动态代理核心原理与实战对比
  • 变分量子分类器 (VQC) 在医疗诊断中的 Python 实现
  • C++ String 赋值运算符重载:从“砸碗”到“换碗仪式”

相关免费在线工具

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online