【JVM】类加载

1. 类加载过程

Java虚拟机(JVM)的 类加载 过程是将字节码文件(.class文件)从存储设备加载到内存,并为其创建相应的类对象的过程。类加载是Java程序运行的基础,保证了程序的动态性和安全性。JVM的类加载过程主要分为五个阶段:加载(Loading)连接(Linking)初始化(Initialization)使用(Using)卸载(Unloading)。其中,连接阶段又分为验证准备解析三个子阶段。

1.1 加载(Loading)

在这个阶段,JVM通过类加载器将类的字节码从文件系统或网络等资源中加载到内存,并创建一个java.lang.Class对象来表示这个类。加载阶段包括以下几步:

  • 通过类的全限定名查找并读取类的字节码文件(通常是.class文件)。
  • 将字节码数据加载到JVM的内存中,并在方法区中生成类的运行时数据结构。
  • 在堆中生成Class对象,作为类的字节码数据的访问入口。

注意:Java的类是按需加载的,只有在程序第一次使用某个类时,才会触发类的加载过程。

1.2 连接

连接阶段的目的是确保类可以被正确使用,它分为三个子阶段:验证准备解析

验证(Verification)

图引用:[1]

验证是为了确保被加载的类的字节码是符合JVM规范的,并且不会破坏JVM的安全性。主要包括:

  • 文件格式验证:检查.class文件的格式是否符合规范。
  • 元数据验证:验证类中的元数据(如类的字段、方法)是否合法。
  • 字节码验证:确保类的字节码操作符号是合法的,例如跳转指令不会跳到无效位置,数据类型操作正确等。
  • 符号引用验证:检查解析时,符号引用是否能够解析到实际的类、字段或方法。
准备(Preparation)

准备阶段是为类的静态变量分配内存,并将其初始化为默认值。注意,此时的初始化并不是为静态变量赋值,而只是分配内存和设置默认值(例如,int类型的默认值是0boolean的默认值是false,引用类型的默认值是null)。

public static int a = 10;

 在准备阶段,a的值为默认的0,而不是10。赋值操作将在初始化阶段进行。

解析(Resolution)

解析阶段是将类的符号引用转换为直接引用。符号引用是编译时期的一种表示方式,而直接引用是运行时的真实地址或偏移量。解析过程主要包括:

  • 类或接口解析:将符号引用的类或接口解析为内存中的实际类或接口。
  • 字段解析:将符号引用的字段解析为具体类的字段。
  • 方法解析:将符号引用的方法解析为实际的方法地址。
  • 接口方法解析:解析接口中的方法引用。

《深入理解 Java 虚拟机》7.34 节第三版对符号引用和直接引用的解释如下[1]:

解析过程可以在连接的解析阶段完成,也可以在类使用时延迟进行(例如动态绑定)。

1.3 初始化(Initialization)

初始化阶段是类加载过程中唯一一个会执行代码的阶段。在这个阶段,JVM执行类的静态代码块静态变量的初始化赋值操作。

初始化阶段,Java 虚拟机真正开始执行类中编写的Java 程序代码,将主导权移交给应用程序。初始化阶段就是执行类构造器方法的过程。

例如:

public static int a = 10;
static {
    a = 20;
}

在初始化阶段,a将被赋值为20

1.4. 使用(Using)

在类初始化之后,该类可以被程序使用。程序可以通过调用类的静态方法、访问静态字段、创建类实例等方式使用类。

1.5. 卸载(Unloading)

类的卸载是指当类不再被使用时,JVM将其从内存中移除。类的卸载通常由垃圾回收器完成,当没有任何类加载器引用该类的Class对象,并且类的实例也不再存在时,类才会被卸载。

2. 双亲委派模型

2.1 类加载器

Java的类加载器分为三大类,分别是:

  1. 引导类加载器(Bootstrap ClassLoader)

    • 负责加载Java核心库(如rt.jar中的类),例如java.lang.Stringjava.util.List等。这是JVM自身实现的类加载器,位于JVM的本地代码中,开发者无法直接操作引导类加载器。
    • 引导类加载器从JRE安装目录下的lib目录或-Xbootclasspath指定的路径中加载类。
  2. 扩展类加载器(Extension ClassLoader)

    • 加载JVM扩展库,一般是加载位于JRE/lib/ext目录下的类或由java.ext.dirs系统属性指定的目录下的类。开发者可以访问扩展类加载器。
    • 该加载器是由sun.misc.Launcher$ExtClassLoader实现的。
  3. 应用程序类加载器(Application ClassLoader)

    • 加载应用程序类路径(CLASSPATH)中的类,负责加载用户编写的代码。开发者也可以自定义类加载器来代替应用程序类加载器。
    • 该加载器是由sun.misc.Launcher$AppClassLoader实现的。它是我们编写的Java应用程序最常使用的类加载器。

除了 BootstrapClassLoader 是 JVM 自身的一部分之外,其他所有的类加载器都是在 JVM 外部实现的,并且全都继承自 ClassLoader抽象类。这样做的好处是用户可以自定义类加载器,以便让应用程序自己决定如何去获取所需的类。

每个 ClassLoader 可以通过getParent()获取其父 ClassLoader,如果获取到 ClassLoadernull的话,那么该类是通过 BootstrapClassLoader 加载的

2.2 双亲委派模型

如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最 终都应该传送到最顶层的启动类加载器中,只有当父加载器反馈自己无 法完成这个加载请求(它的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去完成加载。

双亲委派模型的实现代码非常简单,逻辑非常清晰,都集中在 java.lang.ClassLoaderloadClass() 中,相关代码如下所示。 

protected Class<?> loadClass(String name, boolean resolve)
    throws ClassNotFoundException
{
    synchronized (getClassLoadingLock(name)) {
        //首先,检查该类是否已经加载过
        Class c = findLoadedClass(name);
        if (c == null) {
            //如果 c 为 null,则说明该类没有被加载过
            long t0 = System.nanoTime();
            try {
                if (parent != null) {
                    //当父类的加载器不为空,则通过父类的loadClass来加载该类
                    c = parent.loadClass(name, false);
                } else {
                    //当父类的加载器为空,则调用启动类加载器来加载该类
                    c = findBootstrapClassOrNull(name);
                }
            } catch (ClassNotFoundException e) {
                //非空父类的类加载器无法找到相应的类,则抛出异常
            }

            if (c == null) {
                //当父类加载器无法加载时,则调用findClass方法来加载该类
                //用户可通过覆写该方法,来自定义类加载器
                long t1 = System.nanoTime();
                c = findClass(name);

                //用于统计类加载器相关的信息
                sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                sun.misc.PerfCounter.getFindClasses().increment();
            }
        }
        if (resolve) {
            //对类进行link操作
            resolveClass(c);
        }
        return c;
    }
}

 双亲委派模型的优点

1. 避免类的重复加载:同一个类只会由一个类加载器加载一次,避免了类的重复加载,保证了类加载的唯一性。

2.保证Java核心类的安全:如果允许自定义类加载器加载和替换Java核心类库中的类,例如java.lang.String,开发者可以定义一个具有相同全限定名(包名和类名)的自定义类。这样,当系统中调用String类时,可能会不小心使用开发者自定义的String类,导致系统行为出现不一致,甚至引发严重的安全问题。

为了防止这种情况,Java采用双亲委派机制。根据双亲委派模型,所有类加载器在尝试加载类时,首先会委派给父加载器,最终到达最顶层的引导类加载器。引导类加载器专门负责加载JDK中的核心类库,并确保这些核心类库的加载不可被覆盖。

3. 破坏双亲委派模型

自定义加载器的话,需要继承 ClassLoader

如果我们不想打破双亲委派模型,就重写 ClassLoader 类中的 findClass() 方法即可,无法被父类加载器加载的类最终会通过这个方法被加载。

但是,如果想打破双亲委派模型则需要重写 loadClass() 方法。这是因为:类加载器在进行类加载的时候,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成(调用父加载器 loadClass()方法来加载类)。

例如,Tomcat中的类加载器架构打破了双亲委派模型,每个Web应用都有自己的类加载器,并且每个应用之间的类加载是相互隔离的。


引用:

[1]JavaGuide(javaguide.cn):https://javaguide.cn/java/jvm/class-loading-process.html

[2]《深入理解 Java 虚拟机》

[3]Chapter 5. Loading, Linking, and Initializing:
Chapter 5. Loading, Linking, and Initializing

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

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

相关文章

Unity 粒子系统参数说明

一、Particle System 1. Duration&#xff08;持续时间&#xff09; 粒子系统运行一次所需的时间。它决定粒子系统持续播放的时间长度。 2. Looping&#xff08;循环播放&#xff09; 如果启用&#xff0c;粒子系统将在播放完一次后自动重新开始播放&#xff0c;直到你停止它…

pgrouting实战应用

1&#xff09;下载地区地区数据&#xff08;下载数据是XYZM 四位数据&#xff09; 2&#xff09;下载裁剪行政区数据 3&#xff09;使用arcgis pro添加路网数据和行政区数据 4&#xff09;裁剪数据&#xff0c;仅历下行政区路网 5&#xff09;arcgis pro要素转线&#xff0…

【数据结构】顺序表和链表经典题目

系列文章目录 单链表 动态顺序表实现通讯录 顺序表 文章目录 系列文章目录前言一、顺序表经典例题1. 移除元素2. 合并两个有序数组 二、链表经典例题1. 移除链表元素2. 反转链表3. 合并两个有序链表4. 链表的中间节点5. 环形链表的约瑟夫问题 总结 前言 我们通过前面对顺序表…

ArrayList 源码解析

ArrayList是Java集合框架中的一个动态数组实现&#xff0c;提供了可变大小的数组功能。它继承自AbstractList并实现了List接口&#xff0c;是顺序容器&#xff0c;即元素存放的数据与放进去的顺序相同&#xff0c;允许放入null元素&#xff0c;底层通过数组实现。除该类未实现同…

HTB-Vaccine(suid提权、sqlmap、john2zip)

前言 各位师傅大家好&#xff0c;我是qmx_07&#xff0c;今天来为大家讲解Vaccine靶机 渗透过程 信息搜集 服务器开放了 21FTP服务、22SSH服务、80HTTP服务 通过匿名登录FTP服务器 通过匿名登录到服务器&#xff0c;发现backup.zip文件&#xff0c;可能存在账号密码 发现b…

2024.9.16 day 1 pytorch安装及环境配置

一、配置pytorch环境&#xff0c;安装pytorch 1.查看python版本 python --version 2.在anaconda命令中创建pytorch环境 conda create -n pytorch python3.12(python版本&#xff09; 3.pytorch安装 pytorch首页 PyTorchhttps://pytorch.org/ os为windows推荐package选择…

算法练习题27——疫情下的电影院(模拟)

其实思路还好 就是输入有点难搞 Java import java.util.ArrayList; import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner scanner new Scanner(System.in);String input scanner.nextLine();// 去掉输入字符串的方括号if (input.…

react 安装使用 antd+国际化+定制化主题+样式兼容

安装antd 现在从 yarn 或 npm 或 pnpm 安装并引入 antd。 yarn add antd修改 src/App.js&#xff0c;引入 antd 的按钮组件。 import React from react; import { Button } from antd;const App: React.FC () > (<div className"App"><Button type&q…

USB摄像头视频流转RTSP流

一、VLC查看USB摄像头视频流原理&#xff1a; USB摄像头的工作原理与VLC播放其他视频文件类似&#xff0c;主要区别在于视频流的来源是实时捕获的&#xff0c;而不是预先录制的文件。如果使用VLC将USB摄像头的视频流作为RTSP服务器广播&#xff0c;需要进一步配置 二、VLC查看…

[机器学习]决策树

1 决策树简介 2 信息熵 3 ID3决策树 3.1 决策树构建流程 3.2 决策树案例 4 C4.5决策树 5 CART决策树&#xff08;分类&回归&#xff09; 6 泰坦尼克号生存预测案例 import pandas as pd from sklearn.model_selection import train_test_split from sklearn.tree import …

扣子智能体实战-汽车客服对话机器人(核心知识:知识库和卡片)

这一节的主要内容是通过创建一个汽车客户对话机器人学习扣子平台知识库和卡片的使用。 机器人参考&#xff1a; 企业汽车客服 资深汽车销售 一&#xff0c;汽车销售机器人需求简介 汽车销售是一个需要 7*24h在线的客服咨询岗位&#xff0c;专业性强&#xff0c;但流动性非…

【数据结构】排序算法---直接插入排序

文章目录 1. 定义2. 算法步骤3. 动图演示4. 性质5. 算法分析6. 代码实现C语言PythonJavaCGo 7. 折半插入排序代码实现——C 结语 1. 定义 直接插入排序是一种简单直观的排序算法。它的工作原理为将待排列元素划分为「已排序」和「未排序」两部分&#xff0c;每次从「未排序的」…

自定义EPICS在LabVIEW中的测试

继续上一篇&#xff1a;LabVIEW中EPICS客户端/服务端的测试 变量定义 You can use CaLabSoftIOC.vi to create new EPICS variables and start them. CA Lab - LabVIEW (Realtime) EPICS INPUT: PV set Cluster-array of names, data types and field definitions to crea…

【Go】Go语言介绍与开发环境搭建

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

【Elasticsearch系列六】系统命令API

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

二叉树OJ题——二叉树的前序遍历

文章目录 一、题目链接二、解题思路三、解题代码 一、题目链接 二叉树的前序遍历 二叉树前序遍历后需要返回一个 list 。 二、解题思路 三、解题代码

Pytorch详解-Pytorch核心模块

Pytorch核心模块 一、Pytorch模块结构_pycache__Cincludelibautogradnnoptimutils 二、Lib\site-packages\torchvisiondatasetsmodelsopstransforms 三、核心数据结构——Tensor&#xff08;张量&#xff09;在深度学习中&#xff0c;时间序列数据为什么是三维张量&#xff1f;…

Node.js运行环境搭建

【图书介绍】《Node.jsMongoDBVue.js全栈开发实战》-CSDN博客 《Node.jsMongoDBVue.js全栈开发实战&#xff08;Web前端技术丛书&#xff09;》(邹琼俊)【摘要 书评 试读】- 京东图书 (jd.com) 本节介绍如何搭建Node.js运行环境。 1.2.1 Node.js运行环境安装 进入Node.js官…

苍穹外卖Day01

文章目录 目录 文章目录 前端环境搭建 后端环境搭建 后端-数据库环境搭建 前后端联调 前端环境搭建 打开文件夹&#xff08;确保nginx在英文目录下&#xff09;双击ngnix.exe启动nginx服务&#xff0c;访问端口号80在地址栏输入localhost打开界面 后端环境搭建 熟悉项目…

行业分析---自动驾驶行业的发展

1 背景 进入21世纪以来&#xff0c;自动驾驶行业有着飞速的发展&#xff0c;L2级别的自动驾驶技术也逐渐落地量产到寻常百姓家。不管是起步比较早的特斯拉&#xff0c;还是2015年以后国内的公司&#xff0c;都在逐渐发展自动驾驶技术&#xff0c;并量产给用户使用。 自动驾驶最…