【JVM】感觉弗如...类加载机制

【JVM】感觉弗如…类加载机制

image-20240512123540411

在Java开发过程中,从源代码(.java文件)到字节码(.class文件)再到运行时的类加载,会经历几个关键步骤,我们先简单过一遍大体的过程。再介绍今天这篇博客的重点内容——类加载机制

本篇博客配图大部分源自:

【JVM】Java类加载机制这块算是玩明白了_哔哩哔哩_bilibili

.java文件从编译到运行

image-20240510201707196

1. 编写源代码(.java文件)

Java开发者使用文本编辑器或集成开发环境(IDE)编写源代码。源代码是Java语言编写的程序,包含了类定义、方法定义、变量声明等。

简单来说就是我们写在编译器里面的东西.jpg

2. 编译源代码

编写完源代码后,需要通过Java编译器将源代码编译成字节码。这个过程由javac命令触发:

javac MyClass.java

编译过程涉及以下步骤:

  • 词法分析:编译器读取源代码文件,将其分解成一系列的词法单元(tokens),如关键字、标识符、符号等。
  • 语法分析:编译器检查词法单元的排列顺序是否符合Java语言的语法规则。
  • 语义分析:编译器检查代码的语义,如类型检查、变量作用域等。
  • 生成字节码:编译器将经过分析的源代码转换成Java虚拟机的字节码指令。

简单来说就是将人类可读的Java源代码转换成JVM可执行的字节码

3. 生成字节码(.class文件)

编译过程结束后,会生成一个或多个.class文件,每个.class文件对应源代码中的一个公共类(public class)。字节码是JVM能够理解的中间代码,包含了Java源代码的执行逻辑,但不包含任何特定于硬件平台的指令。(这也是Java跨平台的基础机制之一)

4. 运行Java程序

生成.class文件后,可以使用java命令来运行程序:

java MyClass

运行过程涉及以下步骤:

  • 加载:JVM的类加载器负责加载.class文件到JVM中,创建java.lang.Class对象。
  • 链接:链接过程包括验证字节码的合法性、为静态变量分配内存并设置默认值、将符号引用转换为直接引用。
  • 初始化:执行类构造器<clinit>()方法,初始化静态变量和静态代码块。
  • 执行:JVM开始执行程序的main方法,程序按照编写的逻辑运行。

5. 类加载阶段发生的时机

了解过java的编译运行流程,仍有一点不清楚,类加载阶段究竟发生在什么时候?是在进入main方法之前?还是在使用类前?接下来我们罗列几个常见的类加载机制出现的时机:

  1. 首次使用类时:在程序运行期间,当某个类被首次主动使用时,JVM会开始这个类的加载阶段。主动使用的情况包括但不限于创建类的实例、访问某个类的静态变量、调用类的静态方法等。
  2. 通过类加载器显式加载:当通过Java类加载器(如java.lang.ClassLoader的子类)显式加载一个类时,也会触发该类的加载阶段。
  3. 由其他类引用:当一个类在运行时使用了另一个类,JVM可能需要加载并初始化那个被使用的类。
  4. 初始化某个类的子类:如果一个类的子类被加载,其父类还未被加载,JVM会先加载父类。
  5. 调用类的静态方法:在调用一个类中的静态方法时,该类会被加载。

image-20240512125943184

示例如上,我们编写了一个用于测试的DemoTest类,一个TestHaHa类,在main中调用TestHaHa静态方法,即使用了TestHaHa类,此时触发类加载机制,加载了TestHaHa中的静态代码块。

其余示例由于篇幅原因,不在本篇博客中作展示。

类从加载到使用

下图展示了Java类生命周期的主要阶段,包括编译、加载、连接、初始化、使用和卸载。,由于类加载只包括加载、链接、初始化三个过程,故而本篇博客暂时不会提及最终的卸载环节。

想了想还是把简单的概述放在这里吧~

卸载:当类不再被使用时,由JVM的垃圾回收器卸载。

image-20240510201742436

1. 加载

加载是一个读取Class文件,将其转化为某种静态数据结构存储在方法区内,并在堆中生成一个便于用户调用的java.lang.Class类型的对象的过程。

  1. 通过类的全限定名查找类:JVM通过类加载器查找.class文件或提供.class文件的网络资源。

  2. 将.class文件的二进制数据读入JVM:这些数据被存储在方法区内。

  3. 在堆区创建java.lang.Class对象:每个类在JVM中都有唯一对应的Class对象,用于表示类在JVM的状态。

注1:此处的Class文件并不一定指的是本地文件,而是泛指各种来源的二进制流(网络、数据库、及时生成的Class文件)

注2:全限定名:

全限定名通常由以下两部分组成:

  1. 包名(Package Name):类或接口所属的包的名称,用句点(.)分隔。
  2. 类名或成员名(Class or Member Name):类的简单名称或接口的简单名称,以及可能的方法名和字段名。

假设有一个名为MyClass的类,它位于名为com.example的包中,那么它的全限定名将是:

com.example.MyClass

如果MyClass类中有一个名为myMethod的方法,这个方法的全限定名将是:

com.example.MyClass.myMethod

2. 验证

  1. 首先对文件格式进行验证(发生于加载阶段)
  2. 而后对元数据和字节码进行验证(即对Class静态结构进行语法和语义上的分析,保证其不会产生危害虚拟机的行为)
  3. 符号引用进行验证(在解析阶段进行)

image-20240510203535267

3. 准备

准备阶段是在字节码验证通过之后,虚拟机会认为该Class是安全的,此时将会进入准备阶段。

准备阶段做的处理其实不复杂,就是为类分配静态变量的内存,并设置默认初始值。例如,对于基本数据类型,int会被初始化为0,对象引用会被初始化为null。

这里仅仅是静态变量,而不是成员变量。

在准备阶段的介绍中,我们简单了解方法区。

在JDK 8之前,方法区通常与永久代(Permanent Generation,PermGen)联系在一起。在JDK 8及以后弃用了永久代这种实现方式,采用**元空间(Metaspace)**这种直接内存来取代。

永久代与元空间的区别:

永久代是堆内存中的一部分,用于存储类元数据。但是,由于永久代的大小是固定的,这可能导致内存溢出问题。

元空间位于本地内存(Native Memory),而不是堆内存。这意味着元空间的大小只受限于本地内存的大小,而不是JVM堆内存的大小。

有人说,JDK 8以后采用了元空间来替代方法区,这种说法是完全错误的。

因为方法区是抽象概念,元空间是实现方式。

在JDK 8之前,类的元信息、常量池、静态变量等都存储在永久代这种具体实现中,而在JDK 8及以后,常量池、静态变量等被移除方法区,从而转移到了中,元信息这些依然保留在方法区内,但是具体的存储方式改成了元空间。

元信息包括:

  1. 类信息:包括类的名称、访问修饰符、继承关系、接口实现等。
  2. 字段信息:包括字段的名称、类型、访问修饰符等。
  3. 方法信息:包括方法的名称、返回类型、参数列表、异常表、访问修饰符等。
  4. 字节码指令:JVM执行的指令序列,用于实现类中定义的方法的具体行为。
  5. 类加载器信息:标识用于加载类的类加载器。

image-20240510203922435

4. 解析

解析阶段是在准备阶段完成之后,主要做的一件事情就是将符号引用替换为直接引用

注:事实上,解析阶段还有许多其他任务,例如:

类和接口的解析、自读但解析、类方法解析、实例方法解析,延迟解析(将某些引用推迟到运行时再进行解析)

回到刚刚说的将符号引用替换为直接引用。

那么什么是符号引用,什么是直接引用呢?

我们首先假设类A与类B

符号引用(Symbolic Reference)

符号引用是编译时使用的一种引用形式,它通过一系列描述性的符号来引用目标。在Java类编译成.class文件后,如果类A中引用了类B,此时A中的引用会以符号引用的形式存在。这个符号引用通常是一个字符串,它代表了B的全限定名,如"com.example.B"

直接引用(Direct Reference)

直接引用是类加载过程中解析阶段的一个输出,它是一个具体的指针,指向目标对象或目标类的内存地址。一旦类B被加载和链接到JVM中,JVM就会将类A中的符号引用替换为直接引用,这个直接引用指向类B的java.lang.Class对象或方法的具体内存地址。

当类A在运行时需要引用类B时,JVM会通过以下步骤来解析这个引用:

  1. 类加载:如果类B尚未被加载,JVM将触发类B的加载过程。
  2. 解析:JVM解析类A中的符号引用,找到类B的Class对象。
  3. 替换:JVM将符号引用替换为直接引用,这个直接引用指向类B的确切位置。

我在这篇博客中提到过动态链接的概念:

【JVM】从i++到JVM栈帧-CSDN博客

动态链接

每一个栈帧内部都包含一个指向运行时常量池中该栈帧所属方法的引用。包含这个引用的目的就是为了支持当前方法的代码能够实现动态链接。

在Java源文件被编译到字节码文件中时,所有的变量和方法引用都作为**符号引用(Symbolic Reference)**保存在class文件的常量池里。

比如:描述一个方法调用了另外的其他方法时,就是通过常量池中指向方法的符号引用来表示的,那么动态链接的作用就是为了将这些符号引用转换为调用方法的直接引用

为什么这里也有将符号引用转换为直接引用的操作呢?这里的操作与类加载中的操作有什么区别

静态解析:如果调用的目标在编译时就已经明确,如调用一个具体类的方法,那么解析发生在编译后的链接阶段。

动态解析:如果调用的目标在编译时不明确,如调用一个抽象类或接口的方法,且该方法在运行时可能有不同的实现,那么解析将推迟到运行时,这称为动态绑定

当一个动态调用发生时,JVM会执行以下操作:

  1. 运行时类型检查:JVM检查对象的实际类型。
  2. 方法选择:JVM根据对象的实际类型选择正确的方法版本。
  3. 直接引用替换:JVM使用选定方法的实际内存地址来替换原来的符号引用。

当解析部分完成,外部链接的Java类已经成功地引入到了程序当中。

image-20240510204038490

5. 初始化

在Java的类加载机制中,初始化阶段标志着类加载过程的完成。在这一阶段,JVM会识别并执行类级别上的初始化操作,这些操作与new一个对象不同。

初始化动作包括以下几个方面:

  1. 类变量的赋值:在类级别上声明的变量(即静态变量)将根据代码中的指令被赋予具体的初始值。
  2. 静态代码块的执行:类中定义的静态代码块(以static { ... }括起来的部分)包含了一些在类加载时需要执行的逻辑,这些逻辑会在初始化阶段按代码中出现的顺序执行。

值得注意的是,这些类级别的初始化调用对象的构造函数是两个不同的概念。对象的实例化涉及到以下步骤:

  1. 使用new关键字:当Java代码中显式使用new关键字创建一个对象时,会触发对象的构造过程。
  2. 构造函数的调用:对象实例化后,JVM会调用与对象相对应的构造函数(用<init>()表示),以完成对象初始化。

类初始化和对象初始化是两个不同的阶段,分别对应于类和对象的不同生命周期。类初始化是一次性的,发生在类首次加载到JVM时;而对象初始化则在每次创建新对象时发生为每个对象单独进行

结语

本篇博客内容参考:

【JVM】Java类加载机制这块算是玩明白了_哔哩哔哩_bilibili

接下来还会继续介绍JVM的类加载器和双亲委派机制,笔者争取争取都给学一下。

已经没有啦!不用再往下翻啦!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

几个字符串函数的使用和模拟实现(2)

strcop的使用和模拟实现 strcpy函数的使用事项&#xff1a; 源字符串时不需要修改的&#xff0c;在定义前加上const 源字符串被拷贝到目标字符串上时终止字符\0也被拷贝进去 目标数组的大小要相对于源数组的大小足够大&#xff0c;并且不应该在内存中重叠 函数的返回值是一个字…

【Unity】Unity项目转抖音小游戏(二)云数据库和云函数

业务需求&#xff0c;开始接触一下抖音小游戏相关的内容&#xff0c;开发过程中记录一下流程。 抖音云官方文档&#xff1a;https://developer.open-douyin.com/docs/resource/zh-CN/developer/tools/cloud/develop-guide/cloud-function-debug 1.开通抖音云环境 抖音云地址&a…

软件体系结构风格

目录 一、定义 二、.经典软件体系结构风格&#xff1a; 1.管道和过滤器 2.数据抽象和面向对象系统 3.基于事件系统&#xff08;隐式调用&#xff09; 4.分层系统 5.仓库 6.C2风格 7.C/S 8.三层C/S 9.B/S 题&#xff1a; 一、定义 软件体系机构风格是描述某一特定应用…

C#泛型委托

在C#中&#xff0c;delegate 关键字用于声明委托&#xff08;delegates&#xff09;&#xff0c;委托是一种类型安全的函数指针&#xff0c;允许你传递方法作为参数或从方法返回方法。有时我们需要将一个函数作为另一个函数的参数&#xff0c;这时就要用到委托&#xff08;Dele…

java项目之车辆管理系统(springboot+vue+mysql)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的车辆管理系统。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 项目简介&#xff1a; 车辆管理系统的主要使用者分…

Deckset for Mac:让演示文稿制作更轻松

还在为繁琐的演示文稿制作而烦恼吗&#xff1f;Deckset for Mac来帮您解决&#xff01;它支持Markdown语言&#xff0c;让您只需专注于内容的创作&#xff0c;无需在排版和设计上耗费过多精力。丰富的主题和布局选项&#xff0c;让您能够轻松打造出专业级的演示文稿。快来体验D…

云计算第十二课

安装虚拟机 第一步新建虚拟机 选择自定义安装 下一步 选择稍后安装操作系统 选择系统类型和版本 选择虚拟机文件路径&#xff08;建议每台虚拟机单独存放并且路径不要有中文&#xff09;点击下一步 选择bios下一步 选择虚拟机处理器内核数量 默认硬盘或者自行调大硬盘 选择虚…

软件测试的分类

1.用户分类 2.查看代码分类 3.阶段分类

云计算十三课

centos安装 点击左上角文件 点击新建虚拟机 点击下一步 点击稍后安装操作系统&#xff0c;下一步 选择Linux&#xff08;l&#xff09;下一步 设置虚拟机名称 点击浏览选择安装位置 新建文件夹设置名称不能为中文&#xff0c;点击确定 点击下一步 设置磁盘大小点击下一步…

4.1 编写程序,从键盘接收一个小写字母,然后找出他的前导字符和后续字符,再按顺序显示这三个字符

方法一&#xff1a; 运行效果&#xff1a; 输入B&#xff0c;输出显示ABC&#xff1b;输入A&#xff0c;输出显示AB 思路&#xff1a; 1、通过键盘输入接收一个字母。 2、将输入的字母减去1&#xff0c;得到前导字符&#xff0c;然后输出。 3、将输入的字母加上1&#xff0c;得…

【python量化交易】qteasy使用教程07——创建更加复杂的自定义交易策略

创建更加复杂的自定义交易策略 使用交易策略类&#xff0c;创建更复杂的自定义策略开始前的准备工作本节的目标继承Strategy类&#xff0c;创建一个复杂的多因子选股策略策略和回测参数配置&#xff0c;并开始回测 本节回顾 使用交易策略类&#xff0c;创建更复杂的自定义策略 …

(四十)第 6 章 树和二叉树(树的双亲表存储)

1. 背景说明 2. 示例代码 1) errorRecord.h // 记录错误宏定义头文件#ifndef ERROR_RECORD_H #define ERROR_RECORD_H#include <stdio.h> #include <string.h> #include <stdint.h>// 从文件路径中提取文件名 #define FILE_NAME(X) strrchr(X, \\) ? strrch…

基于yolov5+streamlit目标检测演示系统设计

YOLOv5与Streamlit&#xff1a;智能目标检测可视化展示介绍 随着人工智能技术的飞速发展&#xff0c;目标检测技术已成为推动智能化社会进步的关键技术之一。在众多目标检测算法中&#xff0c;YOLOv5以其卓越的性能和实时性&#xff0c;成为了业界的佼佼者。与此同时&#xff…

代码随想录阅读笔记-动态规划【爬楼梯】

题目 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢&#xff1f; 注意&#xff1a;给定 n 是一个正整数。 示例 1&#xff1a; 输入&#xff1a; 2输出&#xff1a; 2解释&#xff1a; 有两种方法可以爬到楼…

[AutoSar]BSW_Diagnostic_002 DCM模块介绍

目录 关键词平台说明背景一、DCM所处架构位置二、DCM 与其他模块的交互三、DCM 的功能四、DCM的内部子模块4.1 Diagnostic Session Layer (DSL)4.1 DSL 与其他模块的交互 4.2 Diagnostic Service Dispatcher (DSD)4.3 Diagnostic Service Processing (DSP)4.4 小结 关键词 嵌入…

vue3土味情话pinia可以持久保存再次修改App样式

我是不是你最疼爱的人-失去爱的城市 <template><div class"talk"><button click"getLoveTalk">土味情话</button><ul><li v-for"talk in talkStore.talkList" :key"talk.id">{{ talk.title }}<…

计算机服务器中了360后缀勒索病毒怎么解密,360后缀勒索病毒恢复

计算机网络技术的不断发展与应用&#xff0c;为企业的生产运营提供了极大便利&#xff0c;大大提高了企业的办公效率&#xff0c;为企业的生产运营注入了新的动力&#xff0c;但网络是一把双刃剑&#xff0c;在为企业提供便利的同时&#xff0c;也为企业的数据安全带来严重威胁…

macos使用yarn创建vite时出现Usage Error: The nearest package directory问题

步骤是macos上使用了yarn create vite在window上是直接可以使用了yarn但是在macos上就出现报错 我们仔细看&#xff0c;它说的If /Users/chentianyu isnt intended to be a project, remove any yarn.lock and/or package.json file there.说是要我们清除yarn.lock和package.js…

深圳晶彩智能ESP32-1732S019实时观看GPIO的状态

深圳晶彩智能ESP32-1732S019介绍 ESP32-1732S019开发板是基于ESP32-S3-WROOM-1模块作为主控&#xff0c;双核MCU ,集成WI-FI和蓝牙功能&#xff0c;主控频率可达240MHz , 512KB SRAM , 384KB ROM&#xff0c;8M PSRAM&#xff0c;16MB Flash&#xff0c;显示分辨率为170*320 I…

职校智慧校园现状及问题分析

各大中职院校及高职院校是校园信息化的先行者和开拓者&#xff0c;很早就开始注重信息化基础设施建设和信息化人文素养的提升。在过去几年里&#xff0c;随着国家大力发展与扶植职校教育&#xff0c;学校投入相当的经费进行了校园信息通信网络、计算机等基础硬件设备建设&#…