Java虚拟机——JVM(Java Virtual Machine)解析一

news2025/4/16 6:53:28

1.JVM是什么?

1.1 JVM概念

Java Virtual Machine (JVM) 是JDK的核心组件之一,它使得 Java 程序能够在任何支持 JVM 的设备或操作系统上运行,而无需修改源代码

JDK是什么,JDK和JVM是什么关系?

在这里插入图片描述
1.Java IDE(Integrated Development Environment):集成开发环境,专门用于Java编程(例如IDEA),开发Java应用程序前需要选择某具体版本的JDK
在这里插入图片描述
2.JDK(Java Development Kit)
Java开发工具包,用于编写,编译,调试和运行Java应用程序,提供了开发Java程序所需的所有工具和资源。JDK=JRE+其他
3.JRE(Java Runtime Environment)
Java运行时环境,包含Java虚拟机(JVM),类库和其他文件,用于运行Java程序
通过以上的内容梳理可知,JDK包含JRE,JRE包含JVM

1.2 JVM作用

说到这里,我就要搞清楚计算机是如何认识我们编写的代码的
首先,计算机只认识0和1,执行的指令集也是一串一串的01。所以,我们编写的代码一定是被转换为二进制文件才能被计算机执行。
其次,不同的操作系统的指令集是不同的。例如,在Windows系统中0000 0010的意思是加法,而在Linux系统中的意思是减法(只是举例,不是真正的指令集)。
那么将开发者编写的代码直接转换为二进制文件,放在不同的操作系统中运行的结果可能也不同,如果要实现同样的功能就需要在不同的操作系统上编写不同的代码。
Java能跨平台运行的原因
先回忆一下刚开始学习Java编程的时候听过的一句话"一次编译,到处运行",这句话体现了Java的跨平台能力,开发者只需要编写一次Java代码并编译成字节码文件,就可以在任何安装了JVM的机器上执行。
下面是Java代码从编写到运行的过程
在这里插入图片描述

java文件通过javac(Java编译器,Java Compiler)编译成字节码文件(class文件)
可以把JVM看成计算机,字节码文件就相当于JVM的指令集。然后JVM把字节码文件转换为对应系统的指令集,
例如:现在有"Hello World"这么一串代码,Windows系统上的JVM将代码转换为0010,Linux系统上的JVM将代码转换为0110,最后两个系统的执行结果都是"Hello World",这就实现了Java程序的跨平台运行

1.3 JVM执行流程

在这里插入图片描述
程序在执行之前先要把java代码转换成字节码(class文件),JVM 首先需要把字节码通过一定的方式类加载器(ClassLoader) 把文件加载到内存中运行时数据区(Runtime Data Area) ,而字节码文件是 JVM 的一套指令集规范,并不能直接交个底层操作系统去执行,因此需要特定的命令解析器执行引擎(Execution Engine)将字节码翻译成底层系统指令再交由CPU去执行,而这个过程中需要调用其他语言的接口本地库接口(Native Interface) 来实现整个程序的功能

2.深入学习JVM

2.1运行时数据区

运行时数据区是Java程序执行时所需的内存区域。JVM启动时,会根据不同的内存区域分配管理内存。一般来说,线程共享的区域的生命周期和JVM一致;线程私有的区域会随着线程的创建和销毁跟着一起创建和销毁,生命周期和所属线程一致。
在这里插入图片描述

2.1.1.方法区(线程共享)

方法区是JVM内存规范中定义的抽象概念,并不是真实的物理空间。

在JDK8以前,使用永久代来实现方法区。永久代是在堆中开辟的内存空间,主要存储:

(1)类的元信息:类名,修饰符等
(2)常量池
(3)静态变量
注:永久代的大小是在JVM启动时固定的,难以根据实际需求来动态调整,而且永久代是在堆内存中开辟的空间,这也限制了永久代的大小。所以在JDK8之后使用元空间来实现方法区

在JDK8及以后,使用元空间来实现方法区(上面图片中的元数据区/元空间不太准确,但我确实没有找到更好的图片)

元空间存储方式相较于永久代有所改动。首先,元空间不再在堆内存中开辟空间,而是单独向操作系统申请空间,这就不会再受到堆内存大小的限制。其次,元空间可以根据实际需求来动态调整大小。然后,元空间内部存储的数据也发生了一些变化
(1)类的元信息依旧存储在元空间中
(2)常量池转移到堆中
(3)静态变量
在这里插入图片描述

2.1.2.堆(线程共享)

JVM内存中最大的部分,是所有线程共享的空间。
1.在JVM启动时创建(可以动态调整大小,有上限),是垃圾回收的主要位置。
2.几乎所有的对象实例(通过new创建)都存储在堆中
3.从内存回收角度来看java堆可分为:新生代和老生代
注:JVM在编译时会分析对象是否逃逸出方法或者线程,如果对象不会逃逸出方法或者线程,只在内部使用,JVM就可以将其分配在栈(线程私有)上,以提高性能减少垃圾回收的开销,这里不做详细讨论

2.1.3.虚拟机栈(线程私有)

每个线程都有一个独立的虚拟机栈,用于存储栈帧(Stack Frame)。每个方法在执行时都会创建一个栈帧,用于存储方法的局部变量,方法调用和返回地址

2.1.4.本地方法栈(线程私有)

存储本地方法(Native Methods)调用的信息。本地方法是使用非Java语言(如C、C++)编写的方法,它们通过JNI(Java Native Interface,Java本地方法接口)与Java代码进行交互

2.1.5.程序计数器(线程私有)

存储当前线程执行的字节码指令的地址

2.2类加载器

2.2.1类加载过程

类加载包括:加载(Loading),连接(Linking)和初始化(Initialization)三个步骤
在这里插入图片描述

(1)加载(Loading)

通过类加载器将硬盘中的字节码文件加载到运行时数据区,并生成一个类对象存储在方法区。当然,也不一定是硬盘中的字节码文件,还可能来自于网络、数据库,甚至是即时生成的字节码文件
在这里插入图片描述
注意一:着重区分加载和类加载的区别。加载(Loading)只是类加载的第一个阶段;而类加载包括加载(Loading),连接(Linking)和初始化(Initialization)三个步骤

(2)验证(Verification)

确保类文件符合JVM规范中定义的类文件格式。
文件格式验证:检查文件是否是以0xCAFEBABE开头,这是Java类文件的标识;检查类文件的版本号是否和JVM对应,Java 8的JVM不支持Java 9的类文件
元数据验证:确保类的元数据信息没有语法错误
字节码验证:确保类的字节码指令是合法的,不会导致JVM崩溃或者执行不安全操作
注意二:在今天,验证操作不单单是验证(Verification)这一个阶段了。在解析阶段还有符号引用验证,解析阶段可以发生在初始化之前,也可能发生在初始化之后(代码中发生多态来实现后期绑定),而且JVM的开发人员还在不断完善验证策略,所以验证操作分散在各个阶段内,并不是单一的阶段。

(3)准备(Preparation)

为类的静态变量分配内存并设置默认值。

  1.  将类的静态变量分配到方法区(有一些静态变量不在方法区)
    
  2.  基本数据类型初始化默认值,int类型初始化为0,boolean类型初始化为false
    
  3.  引用类型初始化为null
    
  4.  如果是静态常量,直接赋目标值,跳过默认值
    
(4)解析(Resolution)

将符号引用替换为直接引用。
直接引用:指向内存中的实际地址的指针或者偏移量
符号引用:是一种文本形式的引用,使用字符串或其他符号来描述目标类,字段和方法
问题:为什么要引入符号引用?
因为在class文件加载到运行时数据区之前,class文件是在硬盘或者其他空间中存储的(反正不是内存),没有地址和指针这个概念,如果要定位一个类,只能使用其他形式的标识符
在这里插入图片描述
动态解析举例:加入B类是一个抽象类,实现的是身份选择功能,C和D类继承了B,分别代表普通用户和管理员。那么A到底引用C还是D,这可能需要用户来决定。此时,A类就会先进行初始化阶段,当用户选择完身份后再来解析

(5)初始化(Initialization)

任务:执行类的初始化代码
触发条件(以下任一情况都会触发初始化):
1.创建类的实例(new
2.访问类的静态变量(非final)或静态方法
3.反射调用类(Class.forName("com.example.MyClass")
4.子类初始化时,其父类会先被初始化
5. 作为程序入口的主类(包含main()方法的类)
执行顺序
1.父类静态变量和静态代码块(按代码顺序执行)
2.子类静态变量和静态代码块(按代码顺序执行)
3.父类实例变量和构造代码块
4.父类构造函数
5.子类实例变量和构造代码块
6.子类构造函数

2.2.2类加载器

在上述类加载过程中,第一个阶段"加载"涉及到JVM中一个非常重要的模块——类加载器。类加载器主要负责根据类的全限定名找到对应的.class文件
在这里插入图片描述

什么是全限定名?
全限定名指的是包含**包名**在内的**类**的完整名称。例如,假设有一个ArrayList类,属于java.util包,那么它的全限定名就是java.util.ArrayList
类加载器的搜索范围:不同的类加载器负责不同路径的类加载。在JVM中,不算自定义的类加载器,默认的类加载器有三种:

(1) 启动/引导类加载器(Bootstrap ClassLoader):加载 `JAVA_HOME/lib` 下的核心类库
(如 `rt.jar`)

注:这里的JAVA_HOME一般指的是JDK的安装路径,如下图
在这里插入图片描述

rt.jar(以JDK8为例):包含Java标准库,如java.lang,java.util等,至于标准库有哪些在Java语言规范中有明确规定,这里不过多赘述。这些标准库中的类由启动/引导类加载器负责加载。

(2) 扩展类加载器(Extension ClassLoader):加载 `JAVA_HOME/lib/ext` 下的扩展类

Java语言规范中没有的类,并且是由JVM开发者添加的类,称为扩展类,这些类由扩展类加载器负责加载。JVM的版本有很多,所以扩展类有哪些和JVM的具体版本有关

(3) 应用类加载器(Application ClassLoader):加载用户类路径(ClassPath)下的类

一般包括开发者编写的类和第三方依赖库

2.2.3 双亲委派机制(不考虑自定义类加载器)

核心思想:当类加载器收到类加载请求时,不会自行立即加载,而是先将该加载请求委派给父类加载器,最终请求会到达顶层类加载器。

完整过程:

(1)顶层加载器(启动类加载器,Bootstrap ClassLoader)检查JAVA_HOME/lib路径下的核心类库,如果能找到就加载
(2)如果启动类加载器找不到,请求返回给扩展类加载器,检查JAVA_HOME/lib/ext路径下的扩展类,如果能找到就加载
(3)如果扩展类加载器找不到,请求返回给应用类加载器,检查用户类路径下的类
(4)如果所有类加载器均无法加载请求类,则抛出ClassNotFoundException

双亲委派机制的优势:

1. 避免核心类被篡改
安全性:通过优先由启动类加载器加载核心类(如 java.lang.String),确保用户无法定义同名类覆盖核心类
示例:若用户自定义 java.lang.String,JVM 会直接加载核心库中的版本,用户类被忽略
2.防止重复加载
唯一性:每个类由父类优先加载,确保同一个类在多个类加载器中只加载一次
示例:若父类已加载 com.example.MyClass,子类不会再重复加载,避免内存浪费和类冲突
3.天然的类隔离性
隔离性:不同类加载器加载的类属于不同的命名空间,天然隔离。
4.灵活扩展
可定制性:允许子类加载器扩展加载范围(如从网络、数据库加载类),同时不破坏核心类的稳定性

3.小结

下篇博文将继续介绍JVM剩下核心机制——垃圾回收

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2334800.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【源码】SpringMvc源码分析

文章目录 SpringMVC 基础回顾​核心组件源码分析​DispatcherServlet​HandlerMapping​HandlerAdapter​ViewResolver​ 请求处理流程源码解析​ 在当今的 Java Web 开发领域,SpringMVC 无疑是最为广泛应用的 Web 框架之一。它以其强大的功能、灵活的配置以及高度的…

tcp特点+TCP的状态转换图+time_wait详解

tcp特点TCP的状态转换图time wait详解 目录 一、tcp特点解释 1.1 面向连接 1.1.1 连接建立——三次握手 1.1.2 连接释放——四次挥手 1.2 可靠的 1.2.1 应答确认 1.2.2 超时重传 1.2.3 乱序重排 1.2.4 去重 1.2.5 滑动窗口进行流量控制 1.3 流失服务(字节…

高支模自动化监测解决方案

1.行业现状 高大模板支撑系统在浇筑施工过程中,诸多重大安全风险点进行实时自动化安全监测的解决方案主要监测由于顶杆失稳、扣件失效、承压过大等引起的支撑轴力、模板沉降、相对位移、支撑体系倾斜等参数变化。系统采用无线自动组网、高频连续采样,实时…

OpenCV 图形API(24)图像滤波-----双边滤波函数bilateralFilter()

操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 应用双边滤波到图像。 该函数对输入图像应用双边滤波,如 http://www.dai.ed.ac.uk/CVonline/LOCAL_COPIES/MANDUCHI1/Bilateral_Fil…

HarmonyOS中的多线程并发机制

目录 多线程并发1. 多线程并发概述2 多线程并发模型3 TaskPool简介4 Worker简介4.1 Woker注意事项4.2 Woker基本用法示例 5. TaskPool和Worker的对比5.1 实现特点对比5.2 适用场景对比 多线程并发 1. 多线程并发概述 并发模型是用来实现不同应用场景中并发任务的编程模型&…

【随手笔记】QT避坑一(串口readyRead信号不产生)

问题描述: 使用QT5.15.2版本 测试串口readyRead绑定槽函数,接收到数据后 不能触发 试了很多网友的程序,他们的发布版本可以,但是源码我编译后就不能触发,判断不是代码的问题 看到有人提到QT版本的问题,于…

【产品】ToB产品需求分析

需求分析流程 合格产品经理 帮助用户、引导用户、分析需求、判断需求、设计方案 不能苛求用户提出合理、严谨的需求,这不是用户的责任和义务,而应该通过自己的专业能力来完成需求的采集工作 #mermaid-svg-ASu8vocank48X6FI {font-family:"trebuche…

驱动开发硬核特训 · Day 10 (理论上篇):设备模型 ≈ 运行时的适配器机制

🔍 B站相应的视屏教程: 📌 内核:博文视频 - 总线驱动模型实战全解析 敬请关注,记得标为原始粉丝。 在 Linux 驱动开发中,设备模型(Device Model)是理解驱动架构的核心。而从软件工程…

flutter 打包mac程序 dmg教程

✅ 前提条件 ✅ 你已经在 macOS 上安装了 Android Studio Flutter SDK。 ✅ Flutter 支持 macOS 构建。 运行下面命令确认是否支持: Plain Text bash 复制编辑 flutter doctor ---## 🧱 第一步:启用 macOS 支持如果是新项目,…

【数据结构与算法】——堆(补充)

前言 上一篇文章讲解了堆的概念和堆排序,本文是对堆的内容补充 主要包括:堆排序的时间复杂度、TOP 这里写目录标题 前言正文堆排序的时间复杂度TOP-K 正文 堆排序的时间复杂度 前文提到,利用堆的思想完成的堆排序的代码如下(包…

atypica.AI:用「语言模型」为「主观世界」建模

人们不是在处理概率,而是在处理故事。 —— 丹尼尔卡尼曼 People dont choose between things, they choose between descriptions of things. —— Daniel Kahneman 商业研究是一门理解人类决策的学问。人并不只是根据纯粹理性做决策,而是受到叙事、情…

LLaMA-Factory双卡4090微调DeepSeek-R1-Distill-Qwen-14B医学领域

unsloth单卡4090微调DeepSeek-R1-Distill-Qwen-14B医学领域后,跑通一下多卡微调。 1,准备2卡RTX 4090 2,准备数据集 医学领域 pip install -U huggingface_hub export HF_ENDPOINThttps://hf-mirror.com huggingface-cli download --resum…

【WPF】自定义控件:ShellEditControl-同列单元格编辑支持文本框、下拉框和弹窗

需要实现表格同一列,单元格可以使用文本框直接输入编辑、下拉框选择和弹窗,文本框只能输入数字,弹窗中的数据是若干位的二进制值。 本文提供了两种实现单元格编辑状态下,不同编辑控件的方法: 1、DataTrigger控制控件的…

Seq2Seq - GRU补充讲解

nn.GRU 是 PyTorch 中实现门控循环单元(Gated Recurrent Unit, GRU)的模块。GRU 是一种循环神经网络(RNN)的变体,用于处理序列数据,能够更好地捕捉长距离依赖关系。 ⭐重点掌握输入输出部分输入张量&#…

从零开始学Python游戏编程19-游戏循环模式1

在《从零开始学Python游戏编程18-函数3》中提到,可以对游戏代码进行重构,把某些代码写入函数中,主程序再调用这些函数,这样使得代码程序更容易理解和维护。游戏循环模式实际上也是把代码写入到若干个函数中,通过循环的…

Java获取终端设备信息工具类

在很多场景中需要获取到终端设备的一些硬件信息等,获取的字段如下: 返回参数 参数含义备注systemName系统名称remoteIp公网iplocalIp本地ip取IPV4macmac地址去掉地址中的"-“或”:"进行记录cpuSerialcpu序列号hardSerial硬盘序列号drive盘符…

【Linux网络与网络编程】08.传输层协议 UDP

传输层协议负责将数据从发送端传输到接收端。 一、再谈端口号 端口号标识了一个主机上进行通信的不同的应用程序。在 TCP/IP 协议中,用 "源IP","源端口号","目的 IP","目的端口号"&…

没音响没耳机,把台式电脑声音播放到手机上

第一步,电脑端下载安装e2eSoft VSC虚拟声卡(安装完成后关闭,不要点击和设置) 第二步,电脑端下载安装(SoundWire Server)(安装完成后不要关闭,保持默认配置) 第…

XDocument和XmlDocument的区别及用法

因为这几天用到了不熟悉的xml统计数据,啃了网上的资料解决了问题,故总结下xml知识。 1.什么是XML?2.XDocument和XmlDocument的区别3.XDocument示例1示例2:示例3: 4.XmlDocument5.LINQ to XML6.XML序列化(Serialize)与反序列化(De…

Blender安装基础使用教程

本博客记录安装Blender和基础使用,可以按如下操作来绘制标靶场景、道路标识牌等。 目录 1.安装Blender 2.创建面板资源 步骤 1: 设置 Blender 场景 步骤 2: 创建一个平面 步骤 3: 将 PDF 转换为图像 步骤 4-方法1: 添加材质并贴图 步骤4-方法2:创…