java八股文面试[JVM]——类加载器

在这里插入图片描述

一、类加载器的概念
类加载器是Java虚拟机用于加载类文件的一种机制。在Java中,每个类都由类加载器加载,并在运行时被创建为一个Class对象。类加载器负责从文件系统、网络或其他来源中加载类的字节码,并将其转换为可执行的Java对象。类加载器还负责解析类的依赖关系,即加载所需的其他类。

Java虚拟机定义了三个主要的类加载器:

启动类加载器(Bootstrap Class Loader):也称为根类加载器,它负责加载Java虚拟机的核心类库,如java.lang.Object等。启动类加载器是虚拟机实现的一部分,它通常是由本地代码实现的,不是Java类。

扩展类加载器(Extension Class Loader):它是用来加载Java扩展类库的类加载器。扩展类库包括javax和java.util等包,它们位于jre/lib/ext目录下。

应用程序类加载器(Application Class Loader):也称为系统类加载器,它负责加载应用程序的类。它会搜索应用程序的类路径(包括用户定义的类路径和系统类路径),并加载类文件。

除了这三个主要的类加载器,Java还支持自定义类加载器,开发人员可以根据需要实现自己的类加载器。

二、类加载器的工作原理
类加载器的工作可以简化为三个步骤:

加载(Loading):根据类的全限定名(包括包路径和类名),定位并读取类文件的字节码。

链接(Linking):将类的字节码转换为可以在虚拟机中运行的格式。链接过程包括三个阶段:

        验证(Verification):验证字节码的正确性和安全性,确保它符合Java虚拟机的规范。

        准备(Preparation):为类的静态变量分配内存,并设置默认的初始值。

        解析(Resolution):将类的符号引用(比如方法和字段的引用)解析为直接引用(内存地址)。

初始化(Initialization):执行类的初始化代码,包括静态变量的赋值和静态块的执行。

类加载器采用了双亲委派模型(Parent Delegation Model)来加载类。即当一个类加载器需要加载类时,它会首先委派给其父类加载器加载。如果父类加载器无法加载,才由该类加载器自己去加载。这种层级关系使得类加载器能够实现类的共享和隔离,提高了代码的安全性和可靠性。

1 加载阶段

指定是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对象(JVM规范并未说明Class对象位于哪里,Hotspot虚拟机将其放在方法区中),用来封装类在方法区内的数据结构,类的加载的最终产品是位于堆区中Class对象。Class对象封装了类在方法区的数据结构,并且向Java程序提供了访问了方法区内的数据结构的接口。

Class对象位置(HotSpot虚拟机)
1.在jdk1.7是在方法区中或永久代中。
2.在jdk1.8是在方法区中或元空间中。
jdk1.8中移除了永久代,转而用元空间来实现方法区。

  • 方法区其实只是一个虚拟的概念,方法区具体的实现其实是永久代元空间,1.7是永久代,1.8是元空间。
  • 元空间和永久代最大的区别就是:JDK7的永久代放在堆中并且独立与堆,JDK8的元空间完全剥离虚拟机,存在于直接内存

在这里插入图片描述

编写一个新的Java类时,JVM会帮我们编译成class对象,存放在同名的.class文件中。在运行时,当需要生成这个类的对象时,JVM会帮我们检查该类是否已经加载到内存中,若是没有加载,则把.class文件加载到内存中,若是已经加载,则根据class文件生成实例对象。

怎么理解Class对象和new出来的对象的关系?
可以把Class对象看成一个模板,每个new出来的对象都是按照
Class对象这个模板参照出来的,为啥可以参照呢?因为Class对象提供了访问方法区内数据结构的入口。

总结:
加载阶段简单来说就是:
.class文件(二进制数据)-——>读取到内存中-——>数据放到方法区-——>堆中创建对应的Class对象-——>并提供访问方法区的接口

相对于类加载的其他阶段而言,加载阶段(准确来说,是加载阶段获取类的二进制字节流的动作)是可控性最高的阶段。因为开发人员既可以使用系统提供的类加载器来完成加载,也可以定义自己的类加载来完成加载。

加载阶段完成后,虚拟机外部的二进制字节流就按照虚拟机所需的格式存储在方法区之中,而且在Java堆中也创建一个java.lang.Class类的对象。这样便可以通过该对象访问方法区中的这些数据。

加载.class文件的方式:
类的加载由类加载器完成,类的加载器通常由JVM提供,这些类加载器也是前面所有程序运行的基础。JVM提供的这些类加载器通常被称为系统类加载器,除此之外,开发者可以通过继承ClassLoader基类来创建自己的类加载器。通过使用不同的类加载器,可以从不同来源加载类的二进制数据。二进制数据通常有以下几种来源

  1. 从本地系统中直接加载。
  2. 通过网络下载.class文件。
  3. 从zip,jar包等归档文件中加载.class文件。
  4. 从专用数据库中提取.class文件。
  5. 将java源代码编译为.class文件。
2 验证

验证:确保被加载的类的正确性
关于验证大可不必深入但是了解类加载机制必须要知道有这么一个过程,以及知道验证就是为了确保class文件文件的字节流中包含的信息符合当前虚拟机的要求即可。
下面关于验证的内容作为了解即可。

验证是连接阶段的第一阶段,这一阶段的目的是确保class文件的字节流包含的信息符合当前虚拟机的要求,并且不会危害当前虚拟机自身的安全,验证阶段大致会完成四个阶段的检验操作。

  • 文件格式验证:验证字节流是否符合Class文件格式的规范,例如:是否以0xCAFEBABE开头,主次版本号是否在当前虚拟机的处理范围之内,常量池中的常量是否有不被支持的类型。
  • 元数据验证:对字节码描述的信息进行语义分析(注意,对比Javac编译阶段的语义分析),以保证其描述的信息符合Java语言规范的要求,例如这个类是否有父类,除了java.lang.Object这个类以外。
  • 字节码验证:通过程序流和数据流分析,确保程序语言是合法的,符合逻辑的。
  • 符合引用验证:确保解析操作能正确执行。
    验证阶段是非常重要的,但不是必须的,他对程序运行期间没有任何影响,如果所引用的类经过反复验证,那么可以考虑采用-Xverifynone参数来关闭大部分的类验证措施,来缩短虚拟机加载类的时间。
3 准备(重点

当完成字节码文件的校验之后,JVM便会开始为类变量分配内存并初始化,准备阶段是正式为类变量分配内存并设置类变量初始值的阶段,这些内存都在方法区分配

这里有两个需要注意的关键点,即内存分配的对象以及初始化的类型

内存分配的对象:要明白首先要知道Java中的变量类变量类成员变量两种类型。类变量指的是被static修饰的变量,而其他所有类型的变量都属于类成员变量。在准备阶段,JVM只会给类变量分配内存,而不会为类成员变量分配内存。类成员变量的内存分配需要等到初始化阶段才开始。

举个例子,例如下面的代码在准备阶段,只会给LeiBianLiang 属性分配内存,而不会为ChenYuanBL 属性分配内存。

public static int LeiBianLiang = 666;
public String ChenYuanBL = "jvm";

**初始化的类型:**在准备阶段,JVM会为类变量分配内存,并为其初始化(JVM只会给类变量分配内存,而不会为类成员变量分配内存,类成员变量自然这个时候也不能初始化)。但是这里的初始化指的是为变量赋予Java语言中该类型的默认值。而不是用户代码里初始化的值
例如下面的代码在准备阶段之后,LeiBianLiang的值将是0,而不是666

public static int LeiBianLiang = 666;

注意:
但如果一个变量是常量(被static final修饰的)的化,那么准备阶段,属性便会被赋予用户希望的值。例如下面的代码在准备阶段之后,ChangLiang 的值将是666,而不是0

public static final int ChangLiang = 666;

之所以被static final修饰的变量会直接赋值,而static修饰的变量会被赋予java语言类型的默认值,其实我们稍微思考一下就会明白。
两个语句的区别在于一个有final关键字修饰,另外一个没有,而final关键字在Java中代表不可改变的。意思就是说我们如果一旦对ChangLiang 赋值就不会再改变,既然一旦赋值就不会再改变,那么就必须一开始就要赋予用户希望的值。因此被final修饰的类变量再准备阶段就会被赋予想要的值,而没有被final修饰的类变量,其可能在初始化阶段或者运行阶段发生变化,所以就没有必要在准备阶段对它赋予用户想要的值。

4 解析

当通过准备阶段之后,就进入了解析阶段,解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程,解析动作主要针对类或接口,字段,类方法,接口方法,方法类型,方法句柄和调用点限定符7类符号引用进行,符号引用就是一组符号来描述目标,可以是任何的字面量。

直接引用就是直接指向目标的指针,相对偏移或一个间接定位到目标的句柄。

5 初始化(重点

到了初始化阶段,用户定义的代码才真正开始执行

Java程序对类的使用方式分为两种,主动使用被动使用。一般来说,只有当对类的首次主动使用才会导致类的初始化。所以主动使用又叫做类加载过程中初始化开始的时间。
那什么是主动使用呢?包括以下六种方式

1.创建类的实例,也就是使用new关键字创建对象。
2.调用类的静态方法。
3.访问某个类或者接口的静态变量,或者对该静态变量赋值。被final和staic修饰的变量除外(是在编译器把结果放入常量池的静态字段)。
4.反射(例如Class.forName()和对象.class)。
5.初始化某个类的子类,则其父类也会被初始化。
6.Java虚拟机启动时被标明为启动类的类(JavaTest),还有就是Main方法的类会首先被初始化。

最后注意一点对于静态字段,只有直接定义这个字段的类才会被初始化(执行静态代码块),这句话在继承,多态中最为明显。

6 使用

当JVM完成初始化阶段之后,JVM便开始从入口方法开始执行用户的程序代码,使用阶段大家了解一下即可。

7 卸载

当用户程序代码执行完毕后,JVM便开始销毁创建的Class对象,最后负责运行的JVM也退出内存。

8 结束生命周期

在如下几种情况下,Java虚拟机将结束生命周期

  1. 执行了System.exit()方法。
  2. 程序正常执行结束。
  3. 程序在执行过程中遇到异常或者错误而异常终止。
  4. 由于操作系统出现错误导致Java虚拟机进程终止。

三、类加载器的使用示例

下面是一个简单的示例代码,演示了如何使用Java类加载器加载和使用类:

public class ClassLoaderExample {
    public static void main(String[] args) throws ClassNotFoundException {
        // 使用系统类加载器加载并实例化一个类
        ClassLoader classLoader = ClassLoader.getSystemClassLoader();
        Class<?> clazz = classLoader.loadClass("com.example.MyClass");
        MyClass myObject = (MyClass) clazz.newInstance();
        
        // 调用加载的类的方法
        myObject.sayHello();
    }
}

class MyClass {
    public void sayHello() {
        System.out.println("Hello, World!");
    }
}

在上述示例中,我们使用系统类加载器加载并实例化了一个名为com.example.MyClass的类。然后,我们调用了该类的sayHello方法,输出了"Hello, World!"。

四、自定义类加载器
Java允许开发人员自定义类加载器,以满足特定的加载需求。自定义类加载器必须继承java.lang.ClassLoader类,并重写findClass方法。在findClass方法中,开发人员可以根据自己的规则和逻辑来加载类的字节码。

下面是一个简单的自定义类加载器的示例代码:
 

public class CustomClassLoader extends ClassLoader {
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        // 根据名称加载类的字节码
        byte[] byteCode = loadClassByteCode(name);
        // 调用defineClass方法将字节码转换为Class对象
        return defineClass(name, byteCode, 0, byteCode.length);
    }
    
    private byte[] loadClassByteCode(String name) {
        // 实现加载类字节码的逻辑
        // ...
    }
}

在上述示例中,我们自定义了一个类加载器CustomClassLoader,并重写了findClass方法。在findClass方法中,我们可以根据类的名称加载类的字节码,并通过defineClass方法将字节码转换为Class对象。开发人员可以根据具体需求实现自己的加载逻辑

总结
Java类加载器是Java虚拟机的一部分,负责加载类的字节码并转换为可执行的Java对象。类加载器采用双亲委派模型来加载类,具备层级关系共享隔离的特性。

Java提供了启动类加载器、扩展类加载器和应用程序类加载器作为主要的类加载器,开发人员也可以自定义类加载器来满足特定需求。
 

知识来源:

【基础】java类加载器有哪些_哔哩哔哩_bilibili

【2023年面试】Java类加载器有哪些_哔哩哔哩_bilibili

【Java 基础篇】Java类加载器详解_繁依Fanyi的博客-CSDN博客

 深入理解Java类加载器(ClassLoader)

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

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

相关文章

vscode调试PHP代码

目录 准备工作ssh的连接以及配置调试 准备工作 1.首先你需要下载一个vscode 2.下载模块 你需要在VScode中去下载我们所需的两个模块PHP Debug以及remote -ssh 3.安装对应版本的xdebug 需要在xdebug的官方去进行分析&#xff0c;选择适合你自己版本的xdebug 去往官方&#x…

springCloud整合Zookeeper的时候调用找不到服务

SpringCloud整合Zookeeper的时候调用找不到服务 首先&#xff0c;我们在注册中心注册了这个服务&#xff1a; 然后我们使用RestTemplate 调用的时候发现失败了&#xff1a;找不到这个服务&#xff1a; 找了很多资料发现这个必须要加上负载才行 BeanLoadBalanced //负载publi…

国产化-银河麒麟V10系统及docker的安装

一、最近在研究国产化操作系统&#xff0c;“银河麒麟V10”&#xff0c; 在我电脑本机vmware 15的虚拟机中进行安装测试&#xff1b; 1.点击这里提交产品试用申请&#xff0c;不过只需要随便输入&#xff0c;手机号验证码验证后方可跳转至下载地址产品试用申请国产操作系统、银…

《中国区块链发展报告(2023)》发布 和数集团推动区块链发展

北京区块链技术应用协会与社会科学文献出版社日前在京共同发布《区块链蓝皮书&#xff1a;中国区块链发展报告&#xff08;2023&#xff09;》。蓝皮书归纳梳理了2022年区块链产业发展现状及趋势&#xff0c;并结合行业热点Web3.0、AIGC&#xff0c;探讨我国区块链发展的热点话…

智能客服系统:解决企业服务、管理难题的新选择

在数字化时代&#xff0c;智能客服系统是企业服务、管理的新选择。智能客服系统可以通过自然语言处理、人工智能等技术实现与顾客的智能对话&#xff0c;提升企业客服效率和服务质量。同时&#xff0c;智能客服系统也可以为企业提供实时数据分析和监管&#xff0c;进一步优化管…

专访 Hyper Oracle:可编程的 zkOracle 打造未来世界的超算

许多 Web3 应用在实现的过程中&#xff0c;常常会遇到基础设施方面的限制&#xff0c;包括去中心化自动化、预言机、链上信息搜索等问题。绝大部分区块链的中间件网络都是依赖于节点质押来保证节点执行的诚实性&#xff0c;这样的模式会产生诸多衍生问题&#xff0c;例如安全性…

【Java基础】Java注解与反射

文章目录 ⭐️写在前面的话⭐️1、什么是注解&#xff1f;注解的分类常用的Java注解 2、元注解TargetRetentionDocumentedInherited 3、自定义注解Override注解的基本格式 4、什么是反射&#xff1f;什么时候需要用到反射&#xff1f;反射的应用场合 5、反射的原理6、反射机制的…

CSS中如何实现元素之间的间距(Margin)合并效果?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 外边距合并的示例&#xff1a;⭐ 如何控制外边距合并&#xff1a;⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前端之旅 欢迎来到前端入门之旅&#xff…

跳跃游戏【贪心算法】

跳跃游戏 给你一个非负整数数组 nums &#xff0c;你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。 判断你是否能够到达最后一个下标&#xff0c;如果可以&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。在这里插入图片…

美创科技“签”手柠檬文才学堂,共推高校数据安全建设

近日&#xff0c;由柠檬文才学堂联合中国教育在线、东北财经大学网络教育学院共同主办的“三教统筹下高校继续教育数字化转型研讨”顺利召开。 国内高等院校&#xff08;高职院校&#xff09;继续教育分管领导&#xff0c;继续教育学院领导及继续教育信息化、教学教务管理、课程…

『PyQt5-基础篇』| 05 Qt Designer保存的.ui文件如何生成.py文件?

05 Qt Designer保存的.ui文件如何生成.py文件? 1 使用Qt Designer设计一个简单的界面2 UI文件转PY文件2.1 方法一:直接使用命令2.2 方法二:直接调用PyUIC5工具3 运行转换后的py文件.ui文件是用Qt Designer设计的界面保存后的文件;保存后我们需要把这个文件转换成.py 文件,…

JavaSE 集合框架及背后的数据结构

目录 1 介绍2 学习的意义2.1 Java 集合框架的优点及作用2.2 笔试及面试题 3 接口 interfaces3.1 基本关系说明3.2 Collection 常用方法说明3.3 Collection 示例3.4 Map 常用方法说明3.5 Map 示例 4 实现 classes5 Java数据结构知识体系5.1 目标5.2 知识点 1 介绍 集合&#xf…

使用Rust开发命令行工具

生成二进制文件&#xff0c;将其扔到环境变量的path下即可~ 用rust打造实时天气命令行工具[1] 找到合适的API 使用该api[2] 如请求 api.openweathermap.org/data/2.5/weather?qBeijing&appidyour_key: { "coord": { "lon": 116.3972, "lat&quo…

【学习笔记】求解线性方程组的G-S迭代法

求解线性方程组的G-S迭代法 // 运行不成功啊function [x,k,index] Gau_Seid(A,b,ep,it_max) % 求解线性方程组的G-S迭代法&#xff0c;其中 % A为方程组的系数矩阵 % b为方程组的右端项 % ep为精度要求&#xff0c;省缺为1e-5 % it_max为最大迭代次数&#xff0c;省缺为100 % …

基于Android的课程教学互动系统 微信小程序uniapp

教学互动是学校针对学生必不可少的一个部分。在学校发展的整个过程中&#xff0c;教学互动担负着最重要的角色。为满足如今日益复杂的管理需求&#xff0c;各类教学互动程序也在不断改进。本课题所设计的springboot基于Android的教学互动系统&#xff0c;使用SpringBoot框架&am…

threejs纹理加载三(视频加载)

threejs中除了能把图片作为纹理进行几何体贴图以外&#xff0c;还可以把视频作为纹理进行贴图设置。纹理的类型有很多&#xff0c;我们可以用不同的加载器来加载&#xff0c;而对于视频作为纹理&#xff0c;我们需要用到今天的主角&#xff1a;VideoTexture。我们先看效果&…

虚幻官方项目《CropOut》技术解析 之 在实战中理解Enhanced Input系统

文章目录 概要Enhanced Input系统基础回顾旧版输入系统定义物理按键和Action/Axis的映射输入事件 Enhanced Input系统统一的ActionInput Mapping Context输入事件 《Crop Out》《Crop Out》中基于Enhanced Input的输入控制系统Input Mapping Context分层管理输入修改器(Input M…

数据库相关知识2

数据库知识2 关系完整性 数据完整性 指的是数据库中的数据的准确性和可靠性 实体完整性约束&#xff1a; 目的&#xff1a; 在表中至少有一个唯一的 标识&#xff0c;主属性字段中&#xff0c;不为空&#xff0c;不重复 主键约束&#xff1a;唯一 不重复 不为空 primary k…

系统架构设计高级技能 · Web架构

现在的一切都是为将来的梦想编织翅膀&#xff0c;让梦想在现实中展翅高飞。 Now everything is for the future of dream weaving wings, let the dream fly in reality. 点击进入系列文章目录 系统架构设计高级技能 Web架构 一、Web架构介绍1.1 Web架构涉及技术1.2 单台服务…

docker-compose管理创建LNMP服务并运行Wordpress网站平台

文章目录 一&#xff0e;项目环境1. 环境描述2.项目需求 二&#xff0e;部署过程1.安装Docker2.安装Docker加速器3.Docker-Compose安装部署4.准备依赖文件、配置nginx5.配置mysql6.配置php7.编写docker-compose.yml8.验证 三.容器快照&#xff0c;然后将Docker镜像打包成tar包备…