一、运行时数据区概述
(1)整体架构
JVM 启动时,操作系统会为它分配相应的内存空间,接着 JVM 会对分配到的空间进行划分。当 JVM 退出,这些空间会被回收。JVM 将分配到的内存空间主要分成五部分:程序计数器、虚拟机栈、本地方法栈是每个线程独有的;堆和元空间(方法区的实现)是线程共享的
(2)各区域功能
- 程序计数器是每个线程独有的,用于记录当前线程执行的字节码指令地址。当 CPU 发生线程切换时,能让线程下次被调度时知道从哪里继续执行
- 虚拟机栈是每个线程独有的,栈帧是其基本单位,每个方法对应一个栈帧,包含局部变量表、操作数栈、动态链接和方法返回地址等信息。方法调用时栈帧入栈,方法执行结束栈帧出栈
- 本地方法栈:和虚拟机栈类似,为本地方法(通常是用 C、C++ 编写的方法)服务。虽然部分 JVM 实现中二者有合并趋势,但并非所有 JVM 都已合并
- 堆:用于存放对象和数组,是 JVM 中空间占比最大的区域。分为新生代和老年代,新生代又包含伊甸园区、幸存者 0 区、幸存者 1 区
- 方法区:用于存储类信息、运行时常量池、静态变量、即时编译器(JIT)编译后的代码等。Java 8 及以后,方法区由元空间实现,它使用的是本地内存(即操作系统的内存),受 JVM 参数限制和管理,若使用不当也会出现内存溢出问题
二、线程
(1)线程基本概念
- JVM 允许多个线程并行执行。在 Java 21 以前,传统的 Java 线程(也称为平台线程)与操作系统线程是一一对应的。当 Java 线程需要在 CPU 上运行时,会创建对应的操作系统线程,创建完成后调用 Java 线程的run方法。Java 线程执行结束,对应的操作系统线程也会被回收,线程的调度由操作系统负责
- Java 21 引入了虚拟线程的概念。虚拟线程是轻量级的 Java 线程,多个虚拟线程会由 JVM 调度并复用少量的操作系统线程(平台线程)。虚拟线程依附于平台线程,当虚拟线程需要执行时,JVM 会将其挂载到某个平台线程上,让它在操作系统的调度下在 CPU 上运行
(2)JVM 系统线程
- 虚拟机线程:负责当 JVM 到达安全点时执行 “stop - the - world” 操作。安全点如方法调用、循环跳转处,此时 JVM 状态稳定,执行该操作可暂停所有应用线程,便于进行垃圾收集、线程栈收集等
- 周期任务线程:负责调度和执行程序里的定时任务,像定时清理缓存等
- GC 线程:负责堆内存的垃圾回收,不同垃圾回收算法下,GC 线程工作方式不同
- 编译线程:负责调用 JIT 即时编译器对热点代码编译,编译好的代码缓存到方法区(Java 8 及以后是元空间)
- 信号调度线程:接收外部信号(如操作系统信号),并调用 JVM 中相应方法处理信号