JVM - 运行时数据区概述及线程
# 1. 前言
本节主要讲述的是运行时数据区,它是在类加载完成后的阶段。
当我们完成类加载的各个阶段(加载 -> 验证 -> 准备 -> 解析 -> 初始化)后,执行引擎会使用这些类来执行相应的操作。在这个过程中,执行引擎会使用到运行时数据区。
我们可以将运行时数据区类比为大厨做饭所需要的各种原料和工具(切好的菜、刀、调料等),而执行引擎则类比为大厨,它通过使用这些准备好的东西来制作出精美的菜品。
内存是非常重要的系统资源,是硬盘和 CPU 的中间仓库及桥梁,承载着操作系统和应用程序的实时运行。JVM 内存布局规定了 Java 在运行过程中内存申请、分配、管理的策略,保证了 JVM 的高效稳定运行。不同的 JVM 对于内存的划分方式和管理机制存在着部分差异。结合 JVM 虚拟机规范,我们来探讨经典的 JVM 内存布局。
# 运行时数据区的组成
运行时数据区的完整结构如下图所示:
方法区(Method Area):
- 存储类的元数据、常量、静态变量和即时编译器编译后的代码。
- 在 Java 8 之前,方法区通常被称为永久代(Permanent Generation);在 Java 8 及之后版本,方法区被称为元空间(Metaspace)。
堆(Heap):
- 存储所有的对象实例和数组,是 Java 内存管理的主要区域。
- 堆是线程共享的,每个 JVM 实例只有一个堆内存。
栈(Stack):
- 每个线程都有一个私有的栈,栈帧中保存了方法的局部变量、操作数栈、动态链接和方法返回地址。
- 栈帧是线程私有的数据结构,随着方法的调用和返回,栈帧在栈中入栈和出栈。
本地方法栈(Native Method Stack):
- 为了支持本地方法调用,JVM 会使用到本地方法栈。
- 它与 Java 虚拟机栈类似,只不过本地方法栈为本地方法服务。
程序计数器(Program Counter Register):
- 记录每个线程所执行的字节码的行号指示器。
- 在任何时候,一个处理器核心只会执行一个线程的指令,因此每个线程都有一个独立的程序计数器。
# 内存的作用与重要性
- 内存作为桥梁:内存是 CPU 和硬盘之间的桥梁。通过磁盘或网络 IO 获取的数据都需要先加载到内存中,然后 CPU 从内存中获取数据进行处理。因此,内存在数据传输和处理过程中起到了重要的桥梁作用。
- 内存管理:内存管理策略决定了 Java 程序的性能和稳定性。通过合理的内存分配和回收机制,JVM 可以确保程序在运行过程中高效稳定。
总结
- 运行时数据区:是类加载完成后,执行引擎进行操作的主要内存区域,包括方法区、堆、栈、本地方法栈和程序计数器。
- 执行引擎与运行时数据区:执行引擎通过操作运行时数据区的内容,来完成程序的实际运行。
- 内存的桥梁作用:内存在硬盘和 CPU 之间起到了桥梁的作用,是数据处理的中间仓库。
- 内存管理:JVM 的内存布局和管理策略是保证程序高效稳定运行的关键。
# 2. 线程
# 线程的内存空间
Java 虚拟机定义了若干种程序运行期间会使用到的运行时数据区。其中一些数据区会随着虚拟机启动而创建,随着虚拟机退出而销毁;另外一些则与线程一一对应,这些数据区域会随着线程的开始和结束而创建和销毁。
内存空间划分
灰色的为单独线程私有的,红色的为多个线程共享的。即:
- 线程独有:独立的内存区域包括程序计数器、栈、本地方法栈。
- 线程共享:共享的内存区域包括堆、永久代或元空间(以及代码缓存)。
# Runtime类
- 单例模式:每个 JVM 只有一个
Runtime
实例,提供了应用程序与运行时环境的接口。 - 作用:
Runtime
类代表运行时环境,相当于内存结构的中间框框:运行时环境。
# JVM线程
- 定义:线程是一个程序中的运行单元。JVM 允许一个应用有多个线程并行执行。
- 映射:在 Hotspot JVM 中,每个线程都与操作系统的本地线程直接映射。Java 线程准备执行时,一个操作系统的本地线程也同时创建。Java 线程执行终止后,本地线程也会回收。
线程的调度
- 操作系统调度:操作系统负责所有线程的调度,将线程安排到可用的 CPU 上。一旦本地线程初始化成功,它就会调用 Java 线程中的
run()
方法。
# JVM系统线程
如果你使用 console 或者是任何一个调试工具,都能看到在后台有许多线程在运行。这些后台线程不包括调用 public static void main(String[])
的 main 线程以及所有这个 main 线程自己创建的线程。
在后台运行的主要系统线程包括:
虚拟机线程:
- 操作:需要 JVM 达到安全点才会执行,涉及操作包括“stop-the-world”垃圾收集、线程栈收集、线程挂起及偏向锁撤销。
周期任务线程:
- 功能:处理时间周期事件(如中断),用于周期性操作的调度执行。
GC线程:
- 功能:支持 JVM 中不同种类的垃圾收集行为。
编译线程:
- 功能:在运行时将字节码编译成本地代码。
信号调度线程:
- 功能:接收信号并发送给 JVM,通过调用适当的方法进行处理。
总结
- 线程独有内存:程序计数器、栈、本地方法栈。
- 线程共享内存:堆、永久代或元空间。
- Runtime类:提供应用程序与运行时环境的接口。
- JVM线程:每个线程与操作系统的本地线程直接映射,负责执行 Java 线程的
run()
方法。 - JVM系统线程:包括虚拟机线程、周期任务线程、GC 线程、编译线程和信号调度线程,分别处理不同的系统任务。