JVM之运行时数据区域

JAVA程序启动

安装 JRE 或 JDK 只是将虚拟机程序、Java 核心类库等静态文件存放在硬盘中,不会占用运行内存;

当执行java -jar命令时,操作系统会创建一个全新的独立 JVM 进程,加载并启动 HotSpot 虚拟机本体;

JVM 进程启动后,会向操作系统申请专属的进程内存,并按照自身规范统一划分管理,形成 JVM 运行时数据区;

运行时数据区域

整体划分:线程私有(随线程生灭) + 线程共享(全局共用)

JDK8 重大改动:方法区废除,替换为本地内存的元空间 Metaspace

一、线程私有区域

特点:每个线程独有一份、线程隔离、线程销毁自动释放内存、无 GC 回收

1. 程序计数器(PC 寄存器)

  • 作用

    记录当前线程正在执行的字节码行号,线程切换后恢复执行位置。

  • 特殊点

    唯一一个 JVM 规范无 OOM 的区域。

  • 场景

    多线程切换、断点续执行依赖它。

2. Java 虚拟机栈(Java 栈)

  • 作用

    存放 Java 方法执行的上下文,方法调用就入栈方法结束就出栈

  • 最小单元:栈帧

    每个方法运行都会创建一个栈帧,栈帧内部结构:

  1. 局部变量表

    存放基本数据类型、对象引用、方法参数,编译期确定大小。

  2. 操作数栈

    临时存放计算中间结果,做加减、赋值等运算的临时容器。

  3. 动态链接

    把字节码里的符号引用,运行时转为直接内存地址。

  4. 方法出口

    方法执行完,返回上层调用方法,处理正常返回 / 异常返回。

StackOverflowError 触发场景

  1. 方法无限递归,栈帧不断入栈,栈深度超限
  2. 单个线程虚拟机栈空间设置过小(-Xss

3. 本地方法栈

  • 作用

    专门服务 native 本地方法(C/C++ 写的底层方法)

  • 和虚拟机栈结构类似,也会抛出 StackOverflowError、OOM


二、线程共享区域

特点:全局所有线程共享、生命周期和 JVM 一致、主要 GC 回收区域

1. Java 堆 Heap

  • 存放:所有对象实例、数组,是 GC 最频繁的区域
  • 堆内存分代结构(HotSpot)
  1. 新生代

    Eden 伊甸区 + Survivor0 / Survivor1(S0、S1 存活区,随时互换)

    存放:新建短命对象,回收频率极高

  2. 老年代

    存放:存活时间久、年龄大、大对象、晋升过来的对象

    回收频率低,主要触发 Full GC

2. 元空间 Metaspace(JDK8+)

  • 替代 JDK7 及之前的「方法区」

  • 内存位置:直接使用操作系统本地内存,不在堆内

  • 存放内容:

    类的 Class 文件元数据、方法信息、常量、注解、接口信息等

  • 优势:默认无固定上限,不容易溢出

3. 直接内存

  • 不属于堆、不属于虚拟机栈,操作系统直接内存
  • 代表:NIO 的 DirectBuffer
  • 优点:读写快,减少堆内存拷贝
  • 隐患:不受 JVM 堆参数控制,溢出难排查

极简背诵版

  1. 私有区:程序计数器、虚拟机栈、本地方法栈,线程独有,无 GC,递归爆栈报 StackOverflow。
  2. 共享区:堆、元空间、直接内存,全局共用,堆是对象主力存放地,分新生代 + 老年代。
  3. JDK8 方法区改为元空间,用本地内存;三大常见 OOM:堆、元空间、直接内存。

JVM之运行时数据区域
http://hanqichuan.com/2019/07/23/jvm/JVM之运行时数据区域/
作者
韩启川
发布于
2019年7月23日
许可协议