JVM之运行时数据区域
JAVA程序启动
安装 JRE 或 JDK 只是将虚拟机程序、Java 核心类库等静态文件存放在硬盘中,不会占用运行内存;
当执行java -jar命令时,操作系统会创建一个全新的独立 JVM 进程,加载并启动 HotSpot 虚拟机本体;
JVM 进程启动后,会向操作系统申请专属的进程内存,并按照自身规范统一划分管理,形成 JVM 运行时数据区;
运行时数据区域
整体划分:线程私有(随线程生灭) + 线程共享(全局共用)
JDK8 重大改动:方法区废除,替换为本地内存的元空间 Metaspace
一、线程私有区域
特点:每个线程独有一份、线程隔离、线程销毁自动释放内存、无 GC 回收
1. 程序计数器(PC 寄存器)
作用
记录当前线程正在执行的字节码行号,线程切换后恢复执行位置。
特殊点
唯一一个 JVM 规范无 OOM 的区域。
场景
多线程切换、断点续执行依赖它。
2. Java 虚拟机栈(Java 栈)
作用
存放 Java 方法执行的上下文,方法调用就入栈,方法结束就出栈。
最小单元:栈帧
每个方法运行都会创建一个栈帧,栈帧内部结构:
局部变量表
存放基本数据类型、对象引用、方法参数,编译期确定大小。
操作数栈
临时存放计算中间结果,做加减、赋值等运算的临时容器。
动态链接
把字节码里的符号引用,运行时转为直接内存地址。
方法出口
方法执行完,返回上层调用方法,处理正常返回 / 异常返回。
StackOverflowError 触发场景
- 方法无限递归,栈帧不断入栈,栈深度超限
- 单个线程虚拟机栈空间设置过小(
-Xss)
3. 本地方法栈
作用
专门服务 native 本地方法(C/C++ 写的底层方法)
和虚拟机栈结构类似,也会抛出 StackOverflowError、OOM
二、线程共享区域
特点:全局所有线程共享、生命周期和 JVM 一致、主要 GC 回收区域
1. Java 堆 Heap
- 存放:所有对象实例、数组,是 GC 最频繁的区域
- 堆内存分代结构(HotSpot)
新生代
Eden 伊甸区 + Survivor0 / Survivor1(S0、S1 存活区,随时互换)
存放:新建短命对象,回收频率极高
老年代
存放:存活时间久、年龄大、大对象、晋升过来的对象
回收频率低,主要触发 Full GC
2. 元空间 Metaspace(JDK8+)
替代 JDK7 及之前的「方法区」
内存位置:直接使用操作系统本地内存,不在堆内
存放内容:
类的 Class 文件元数据、方法信息、常量、注解、接口信息等
优势:默认无固定上限,不容易溢出
3. 直接内存
- 不属于堆、不属于虚拟机栈,操作系统直接内存
- 代表:NIO 的
DirectBuffer - 优点:读写快,减少堆内存拷贝
- 隐患:不受 JVM 堆参数控制,溢出难排查
极简背诵版
- 私有区:程序计数器、虚拟机栈、本地方法栈,线程独有,无 GC,递归爆栈报 StackOverflow。
- 共享区:堆、元空间、直接内存,全局共用,堆是对象主力存放地,分新生代 + 老年代。
- JDK8 方法区改为元空间,用本地内存;三大常见 OOM:堆、元空间、直接内存。