当前位置: 首页 > article >正文

JVM整体结构和JMM内存模型

1、什么是JVM

JVM(Java Virtual Machine,Java虚拟机)是执行Java字节码的虚拟环境,它将Java源代码编译成字节码,使得程序可以在各种操作系统和硬件上跨平台运行,而无需重新编译。JVM负责加载字节码、解释或编译字节码为机器码,并提供自动内存管理、垃圾回收等功能。

JVM的主要功能: 跨平台支持、自动内存管理、性能优化、多线程支持、安全性以及诊断和监控。

2、JVM整体结构

JVM (Java Virtual Machine)的整体结构可以分为五个主要部分:类加载子系统、运行时数据区、执行引擎、本地方法接口和运行时类库。分别负责不同的功能模块,为 Java 程序的执行提供支持。

在这里插入图片描述

2.1、类加载子系统

类加载子系统负责将Java字节文件 (.class文件)加载到JVM中,将其转换JVM能够执行的类对象。类加载过程按需加载,解决类的依赖关系。

类加载过程

(1) 加载: 通过类的全限定名获取类的字节码,并将其加载到内存中,生成 Class 对象。

(2) 验证: 校验字节码文件的正确性。

(3) 准备: 给类的静态变量分配内存,并赋予默认值。

(4) 解析: 将常量池的符号引用替换直接引用。

(5) 初始化: 执行类中的静态初始化块,给静态变量赋值。

四种类加载器

(1) 引导类加载器(Bootstrap ClassLoader): 负责加载支撑JVM运行的核心类库,位于JRE的lib目录,比如rt.jar、charsets.jar等。

(2) 扩展类加载器(Extension ClassLoader): 负责加载支撑JVM运行的扩展目录中的JAR类包,位于JRE的lib目录下的ext目录。

(3) 应用类加载器(Application ClassLoader): 负责加载ClassPath路径下的类包,主要加载自己写的类。

(4) 自定义类加载器(Custom ClassLoader): 负责加载用户自定义路径下的类包。

类加载的双亲委派机制

工作原理: 加载某个类时会先委托父加载器寻找目标类,找不到再委托上层父加载器加载。如果所有父加载器在自己的加载类路径下都找不到目标类,则在自己的类加载器路径中查找并载入目标类。

目的: 避免类的重复加载,保证被加载类的唯一性。

优势: 沙箱安全机制,防止核心API库被随意篡改,比如自己写的java.lang.String.Class类不会被加载。

2.2、运行时数据区

运行时数据区包含五部分:线程共享的堆和方法区,线程私有的虚拟机栈、本地方法栈和程序计数器。

(1) 堆(Heap): 存储所有对象实例和数组。管理不同生命周期的对象,分年轻代(Eden、Survivor)和老年代。

(2) 方法区(Method Area): 存储类的元数据:类结构、方法定义、静态变量和运行时常量池等。

(3) 虚拟机栈(JVM Stack): 存储方法调用时的局部变量、操作数栈和方法返回地址等数据。数据以栈帧形式存在,每个栈帧对应一个方法调用。

(4) 本地方法栈(Native Method Stack): 支持本地方法的调用,存储调用本地方法时的局部变量、操作数栈等数据。执行非Java的本地方法。

(5) 程序计数器(Program Counter, PC Register): 记录当前线程执行的字节码指令的地址,随线程切换改变。

2.3、执行引擎

执行引擎执行Java字节码,将字节码转换为机器指令。由解释器、即时编译器和垃圾收集器组成。

解释器: 将字节码逐条解释为机器指令并执行,执行未经过JIT编译的字节码。

即时编译器: 将高频调用的字节码块编译为机器码,将热点代码直接转换为本地机器代码。

垃圾收集器: 执行垃圾回收,释放不再使用的对象占用的内存。

2.4、本地方法接口

本地方法接口提供与本地代码(如C、C++)进行交互的接口,允许Java调用非Java代码。

使用场景: 访问底层系统资源可已有的本地库(如操作系统API),进行高性能操作。

2.5、运行时类库

提供支持Java应用程序运行的核心类库,比如Java核心API、I/O网络和数据结构等。通常以.jar文件形式提供。

3、JMM内存模型
3.1、JMM简介

JMM内存模型 (Java Memory Model,简称JMM) 是Java语言中用于定义线程间通信与共享变量可见性的一种规范,目的是保证在多线程环境下数据的一致性和内存访问的有序性。JMM对变量的存储和读写进行了明确规定,并引入了“主内存”和“工作内存”的概念。

主内存(Main Memory): 所有线程共享的存储区域,所有变量(实例变量、静态变量等)都存储在主内存中。线程在主内存中的变量副本上进行操作,通过主内存实现不同线程间的通信。

工作内存(Working Memory): 每个线程都有自己的工作内存(类似于CPU缓存),其中存放了主内存中变量的副本。线程对变量的所有操作(读取、写入)都必须在工作内存中进行,而不是直接操作主内存。每个线程只能在自己的工作内存中看到这些变量的修改。

3.2、JMM关键问题

(1) 可见性: 保证当一个线程修改了变量后,其他线程能够立即看到变化。在JMM中通过volatile关键字、锁(如synchronizedReentrantLock)实现可见性。

(2) 有序性: 保证代码的执行顺序与预期一致,避免重排序带来的执行不一致。JMM在没有依赖时允许指令重排序优化,但是通过“happens-before”规则确保在并发下操作的顺序。volatile和锁机制也可以保证有序性。

(3) 原子性: 保证操作不可分割,要么完全执行要么完全不执行。简单变量的读取和写入是原子性的,但复合操作如自增等不是。锁和CAS操作可以确保复合操作的原子性。

3.3、内存操作规则

线程间通信规则

  • 线程对共享变量的读写需要经过主内存,线程的工作内存存储着共享变量的副本。
  • 所有的修改必须先同步回主内存,其他线程才能看到修改后的值。

happens-before 规则

JMM通过“Happens-Before”规则定义操作的顺序,来保证多线程环境下内存的可见性和一致性:

(1) 程序次序规则:单线程内,按代码的顺序执行。

(2) 锁定规则:一个锁的解锁操作,happens-before 后续对这个锁的加锁操作。

(3) volatile变量规则:对一个volatile变量的写操作,happens-before 后续对该变量的读操作。

(4) 传递性:如果A happens-before B,B happens-before C,那么A happens-before C。

(5) 线程启动规则:Thread对象的start()方法happens-before线程的每一个操作。

(6) 线程中断规则:线程的interrupt()调用,happens-before 检查中断的代码。

4、我的公众号

敬请关注我的公众号:大象只为你,持续更新技术知识…


http://www.kler.cn/a/374774.html

相关文章:

  • Reactor
  • 数据结构与算法学习笔记----质数
  • STM32HAL库中RTC闹钟设置时分秒,年月日
  • 安宝特应用 | 美国OSHA扩展Vuzix AR眼镜应用,强化劳动安全与效率
  • springboot中使用gdal将表中的空间数据转shapefile文件
  • Leetcode-208. 实现Trie(前缀树)
  • EMR Serverless Spark:一站式全托管湖仓分析利器
  • JAVA:常见 JSON 库的技术详解
  • Linux安装部署数据库:PostgreSQL14
  • 【5.5】指针算法-三指针解决颜色分类
  • 插件/贴片沉板 RJ45 网口连接器在网通领域的具体应用
  • linux下一个应用是如何被执行的
  • 便携剃须刀性能王者,小但专业,未野MAX SE剃须刀测评
  • Arduino 74HC595芯片引脚拓展使用详解
  • 使用 python中 pandas 将 Excel 转换为 CSV 文件
  • 无人机3D模拟训练飞行技术详解
  • springboot导出pdf,解决中文问题
  • 在 Android 设备上部署一个 LLM(大语言模型)并通过 Binder 通信提供服务
  • Java 字符流详解
  • Zoho Desk系统解锁工单自动化 分配效率翻倍
  • ffmpeg拉流分段存储到文件-笔记
  • SC5120家庭总线收发器可pin to pin兼容MAX22088
  • WAF+AI结合,雷池社区版的强大防守能力
  • scp免密传输教程
  • QT 跨平台优势独特,效果实例设计精彩呈现
  • 【Redis】内存淘汰策略