JVM类的加载相关的问题

news2024/12/24 9:40:28

JVM类的加载相关的介绍

学习类的加载的加载过程对深入理解JVM有十分重要的作用,下面就跟我一起学习JVM类的加载过程吧!


文章目录

  • JVM类的加载相关的介绍
  • 一、类的加载过程
  • 二、双亲委派机制
    • 1、类加载器的种类
    • 2、为什么JVM要分成不同的类的加载器
    • 3、类的加载过程
    • 4、为什么要通过双亲委派机制
    • 5、从代码层次分析双亲委派机制
  • 三、自定义类加载器
  • 四、打破双亲委派机制
    • 1.tomcat打破双亲委派机制
    • 2.SPI 打破双气双亲委派机制


一、类的加载过程

JVM的类的加载过程分为五个阶段:加载、验证、准备、解析、初始化
加载
  加载阶段就是将编译好的的class文件通过字节流的方式从硬盘或者通过网络加载到JVM虚拟机当中来。(我们平时在Idea中书写的代码就是放在磁盘中的,也可以通过网络加载到虚拟机)

验证
  验证阶段主要是验证class文件的格式或者内容是否符合java规范和虚拟机规范,比如最常见的字节码文件中的cafe baby,以及通过class文件是u2或者u4的的方式解析字节码中各部分的内容是否符合规范。

准备
  准备阶段就是给静态变量(static修饰的变量)赋默认值的过程,int类型的赋值为0,对象类型赋值为null,这都是默认值,需要注意的是常量,也就是被final修饰的static,在类编译的时候就已经赋值完成了。

解析
 解释阶段做的事情用一句话来说就是将符号引用转变为直接引用(句柄)
 句柄:如下如所示虚拟机有两种引种对象数据的方式,一种是通过句柄池的方式,一种是通过直接指针的方式,句柄池的方式就是在引用和真实地址之间多了一层句柄,这样的好处就是当引用的真实对象发生改变时,栈中存放的值不需要改动,但是多了一层就会增加系统的复杂性,所以hotsport虚拟机用的是直接地址指针的方式(如下图)。
直接引用:数据在内存中的真实地址
符号引用: 包括一些方法名和字面量等,这些符号引用在加载过程中有的可以确定下来的,比如static修饰的main方法,这些可以确定下来的引用在解析过程中就会转变为指向真实地址的指针,还有一些是只有在实际运行期间才可以确定下来的,比如多态,接口等,这些就需要到真正调用的时候才转为直接地址引用。(这也就是静态连接和动态连接的区别)

在这里插入图片描述

初始化
类的初始化是将给静态变量赋值(这时候的赋值才是赋代码中书写的值)静态代码块收集在一起进行执行的过程(其实就是执行字节码中的clinit方法,注意这个方法不是类的构造方法)执行顺序根据静态变量和静态代码块在类中的顺序决定。代码如下

public class Hello {
	//准备阶段给a赋值为0.初始化的时候给a赋值为3
    public static int a=3;
    //根据顺序先执行a=3,在实行静态方法
    static {
        String b="b";
    }
    public static void main(String[] args) {

    }

}

注意:类的初始化,实例化对象


二、双亲委派机制

双亲委派机制是我们学习类的加载和面试过程中逃不掉的问题,接下来我会通过介绍类加载器的种类,jvm虚拟机使通过什么方式调用这些类加载器完成类的加载的,双亲委派机制是什么,打破双亲委派机制的例子有哪些。

1、类加载器的种类

类的加载过程,同通过类加载器实现的。类的加载器主要有如下几种:
引导类加载器:c++代码实现,负责接在java的核心类库,比如rt.jar等
扩展类加载器:加载java的一些扩展类,负责加载jre下ext扩展目录下的jar报
应用类加载器:负责加载classpath下的类,也就是我们自己写的类
自定义类加载器:我们自己写的写的类加载器,负责加载自定义路径下的类

2、为什么JVM要分成不同的类的加载器

介绍为什么要搞这么多类加载器之前,就需要先说明JVM是如何使用这些类加载器的(也就是双亲委派机制)。双亲委派机制就是(引导类加载器->扩展器类加载器->应用类加载器->自定义类加载器 逐层上层是下层类加载器的父亲,注意这里不同于extends的继承关系,是类加载器中有一个parent属性,指向它的父亲是谁)

在这里插入图片描述

在这里插入图片描述

3、类的加载过程

抛开自定义加载器先不谈,当我们自己编的类加载的时候,会先去应用类加载器中查找该类是否已经加载了(如果加载了直接返回),如果没有则向上委托扩展类加载器看它是否已经加载(加载了直接返回),如果没有则继续向上委托(加载了直接返回),若没有加载并且是需要加载核心JAVA的核心类库,则加载如果不是核心类库,则通知下层去加载,如果是JAVA的扩展包的类就通知扩展类加载器加载,如果是用户自定义的类就再由扩展类加载器继续向下通知应用程序加载器加载。这就是JVM的双亲委派机制。

4、为什么要通过双亲委派机制

JVM为什么要通过这种麻烦的方式,来实现类的加载的,原因如下:

  • 沙箱机制:JAVA不希望用户或者黑客随便更改核心类库,所以再加载核心类库和扩展包的时候,JAVA要保证加载的是自己核心类库的东西,而不是别人更改过的类。,就是为了安全
  • 避免了类的重复加载

5、从代码层次分析双亲委派机制

JAVA的双亲委派的机制的主要概念:

  • 引导类加载器由JAVA虚拟机启动以后,调用底层C++代码加载
  • 扩展类加载器和应用类加载器由一个被称为启动器的加载,该类就是Launcher, 扩展类加载器和应用类加载器的上下层父子关系由parent属性确立
  • Launcher类是由引导类加载器加载的

类的关系图

AppClassLoader和ExtClassLoader都是Launcher(启动器)的内部类,由Launcher调用构造方法的时候,创建AppClassLodader和ExtClassLoder
在这里插入图片描述
                      图一

图一
                     图二

AppClassLoader和ExtClassLoder都是继承自URLClassLoader

在这里插入图片描述在这里插入图片描述

URLClassLoader最终继承自ClassLoader

在这里插入图片描述
                     图三
                     
类的父子关系整理清楚了,下面看Launcher是怎么创建AppcClassLloader和ExtClassLoader的?
可以看到,在创建AppClassLoader时,将创建好的ExtClassLoader传给了AppClassLoader的get**方法。这个方法最终直接会调用到它的构造方法,我们直接看它的构造放的代码。
在这里插入图片描述
                     图四

我们看下图AppClassLoader的构造方法,会调用super,也就是父类的构造方法,根据图三的类的关系图,会最终调用到ClassLoader类的构造方法,我们继续进入到它的构造方法中看它干了什么。
在这里插入图片描述
                     图五

好的我们看到了parent属性,也就是将最初传入的ExtClassLoader的实例设置为APPClassLoader的父亲。
tuliu在这里插入图片描述
                    图六

总结:引导类加载器加载了启动器Launcher,Launcher创建了AppClassLoader和ExtClasslLoader,并好父子关系(通过parent)


你以为这就完了吗?,不可能,绝对不可能!双亲委派的代码还没说呢,加载类之前向上委托那一系列的操作,在哪能还没说呢,怎么可能就这么结束了呢!!!


我们来看类是怎么加载的,我们都知道,加载一个类的时候我们会调用ClassLoader的loaderClass方法,那我们就看看父子关系建立好以后,是怎么实现向上委托的?
在这里插入图片描述
                     图七

 public Class<?> loadClass(String name) throws ClassNotFoundException {
 		//调用了重载的loadClass方法,多传入一个flase
        return loadClass(name, false);
    }
 protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
    //加锁是为了保证加载类class文件时,必须是安全的,不会因为并发加载多个类进来
        synchronized (getClassLoadingLock(name)) {
            // 先根据类的全限定名查找类是否已经加载过了,加载过了c!=null直接返回。不走下面的代码
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                /**
                * 注意:这里就是双亲委派的关键代码
     			*代码走到这里会让他的parent去递归调用本方法,直到parent==null时(ExtClassload),这里就符合双亲委派机制,向上委托
     			*/
     				//AppClassLoader执行这行代码
                    if (parent != null) {
                        c = parent.loadClass(name, false);
                        //扩展类加载器执行这行代码
                    } else {
                    //因为ExtClassLoader的partent==null执行这行代码,调用引导类加载器,加载java核心类库的代码返回c==null 代码继续向下执行
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }

                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    //因为之前是递归调用的,第一次是ExtClassLoader调用这个方法,去加载java扩展包的类
                    //第二次是AppClassLoader调用这个方法,AppCLassLoader是加载classpath路径下我们自己自己写的类,具体实现的方法在URLClassLoader,我们去看URLClassLoader是怎么实现的,如图八
                    c = findClass(name);

                    // this is the defining class loader; record the stats
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }

在这里插入图片描述
                     图八

加载class流文件
在这里插入图片描述
                       图九

总结

  • 引导类加载器由底层C++代码创建
  • 引导类加载器加载启动器Launcher
  • Launcher的构造方法创建ExtClassLoade和AppClassLoader,并通过parent属性确认父子父子关系
  • ClassLoader的loadClass方式实现了双亲委派机制
  • findClass方法调用defindClass实现classpath下的class文件的加载

在这里插入图片描述
                       图十


三、自定义类加载器

public class MyClassLoaderTest {
    static class MyClassLoader extends ClassLoader {
        private String classPath;

        public MyClassLoader(String classPath) {
            this.classPath = classPath;
        }

        private byte[] loadByte(String name) throws Exception {
            name = name.replaceAll("\\.", "/");
            FileInputStream fis = new FileInputStream(classPath + "/" + name + ".class");
            int len = fis.available();
            byte[] data = new byte[len];
            fis.read(data);
            fis.close();
            return data;
        }

        protected Class<?> findClass(String name) throws ClassNotFoundException {
            try {
                byte[] data = loadByte(name);
                //defineClass将一个字节数组转为Class对象,这个字节数组是class文件读取后最终的字节数组
                return defineClass(name, data, 0, data.length);
            } catch (Exception e) {
                e.printStackTrace();
                throw new ClassNotFoundException();
            }
        }

    }

    public static void main(String args[]) throws Exception {
        //初始化自定义类加载器,会先初始化父类ClassLoader,其中会把自定义类加载器的父加载器设置为应用程序类加载器AppClassLoader
        MyClassLoader classLoader = new MyClassLoader("D:/test");
        //D盘创建 test/com/test/jvm 几级目录,将User类的复制类User1.class丢入该目录
        Class clazz = classLoader.loadClass("com.test.jvm.User1");
        Object obj = clazz.newInstance();
        Method method = clazz.getDeclaredMethod("sout", null);
        method.invoke(obj, null);
        System.out.println(clazz.getClassLoader().getClass().getName());
    }
}



四、打破双亲委派机制

1.tomcat打破双亲委派机制

2.SPI 打破双气双亲委派机制

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

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

相关文章

AIGC风暴席卷出版业,探索人工智能与创造力的融合

随着时代的不断发展&#xff0c;人工智能技术正逐渐深入并改变着各行各业&#xff0c;其中包括传统的出版行业。最近独家连载于微信读书的刑侦小说《马一鸣从警记》的成功&#xff0c;以及其中使用到的AI工具在策划、编校、推广和封面设计上的辅助&#xff0c;不仅为出版行业带…

windows下的mysql安装部署和配置环境

目录 一、下载&#xff08;mysql下载链接&#xff09; 1.进入官网选择下载 2.拉到最底下&#xff0c;选择社区版下载 3.选择mysql community server 4.选择数据库版本&#xff0c;以及系统和安装方式&#xff08;这里我选的msi&#xff09; 5.直接点击no thanks,just sta…

Unity 安卓(Android)端AVProVideo插件播放不了视频,屏幕一闪一闪的

编辑器运行没有问题&#xff0c;但是安卓就有问题&#xff0c;在平板上运行就会报错&#xff1a; vulkan graphics API is notsupported 说不支持Vulkan图形API,解决方法&#xff1a;把Vulkan删除掉

NPM 常用命令(二)

目录 1、npm bugs 1.1 配置 browser registry 2、npm cache 2.1 概要 2.2 详情 2.3 关于缓存设计的说明 2.4 配置 cache 3、 npm ci 3.1 描述 3.2 配置 install-strategy legacy-bundling global-style omit strict-peer-deps foreground-scripts ignore-s…

文心一言 VS 讯飞星火 VS chatgpt (86)-- 算法导论8.2 3题

三、用go语言&#xff0c;假设我们在 COUNTING-SORT的第 10行循环的开始部分&#xff0c;将代码改写为: 10 for j 1 to A.length 试证明该算法仍然是正确的。它还稳定吗? 文心一言&#xff1a; 首先&#xff0c;让我们先明确 COUNTING-SORT 算法的基本思想。COUNTING-SOR…

Java学习笔记之----I/O(输入/输出)二

【今日】 孩儿立志出乡关&#xff0c;学不成名誓不还。 文件输入/输出流 程序运行期间&#xff0c;大部分数据都在内存中进行操作&#xff0c;当程序结束或关闭时&#xff0c;这些数据将消失。如果需要将数据永久保存&#xff0c;可使用文件输入/输出流与指定的文件建立连接&a…

kubernetes/cluster/addons/fluentd-elasticsearch

#发文福利# 一、前言 kubernetes 1.23搭建EFK所用到的yaml文件&#xff0c;本帖均来自kubernetes官方&#xff0c;且没做修改。 https://github.com/kubernetes/kubernetes/tree/release-1.23/cluster/addons/fluentd-elasticsearch 二、EFK 原版yaml 1、create-logging-na…

【Unity3D】UI Toolkit元素

1 前言 UI Toolkit简介 中介绍了 UI Builder、样式属性、UQuery、Debugger&#xff0c;UI Toolkit容器 中介绍了 VisualElement、ScrollView、ListView、GroupBox 等容器&#xff0c;本文将介绍 UI Toolkit 中的元素&#xff0c;主要包含 Label、Button、TextField、Toggle、Ra…

Nerd Font 字体安装

一、Nerd Font 是什么&#xff1f; Nerd Font 是为开发人员准备的&#xff0c;具有大量字形&#xff08;图标&#xff09;的字体。特别是从流行的“标志性字体”中添加大量额外的字形&#xff0c;如Font Awesome、Devicons、Octicons等。 二、安装步骤 1.克隆仓库 git clon…

【上海迪士尼度假区】技术解决方案

开源平台地址Giteehttps://gitee.com/issavior/disney 技术解决方案 1. 背景2. 技术架构3. 业务架构3.1 架构图3.2 说明 4. 技术能力4.1 自研中间件4.2 定制化中间件 5. 领域模型6. 数据模型7. 交易链路8. 状态机8. 接口文档 1. 背景 上海迪士尼度假区已运营近10年&#xff0c…

【微服务部署】四、Jenkins一键打包部署NodeJS(Vue)前端项目步骤详解

本文介绍使用Jenkins一键将NodeJS&#xff08;Vue&#xff09;前端项目打包并上传到生产环境服务器&#xff0c;这里使用的是直接打包静态页面&#xff0c;发送到远程服务器Nginx配置目录的方式&#xff0c;首先确保服务器环境配置好&#xff0c;安装Nginx&#xff0c;运行目录…

微软拼音输入法配置小鹤双拼

实现微软拼音能够使用小鹤双拼&#xff0c;再也不用下载搜狗输入法了 winR输入regedit&#xff0c;打开注册表 计算机\HKEY_CURRENT_USER\Software\Microsoft\InputMethod\Settings\CHS新建字符串值&#xff1a; 名字为&#xff1a;UserDefinedDoublePinyinScheme0值为 &#…

加杠杆平台怎么样_哪个平台可以给股票加杠杆?配先查

加杠杆是一种投资策略&#xff0c;以小额的资金进行更大规模的投资交易。这种策略通常在金融市场中使用&#xff0c;包括股票市场。然而&#xff0c;加杠杆投资并非适合所有人&#xff0c;因为它带有更高的风险和潜在的损失。那么哪个平台可以给股票加杠杆&#xff1f; 通过配先…

手写Mybatis:第12章-完善ORM框架,增删改查操作

文章目录 一、目标&#xff1a;完善增删改查二、设计&#xff1a;完善增删改查三、实现&#xff1a;完善增删改查3.1 工程结构3.2 完善增删改查类图3.3 扩展解析元素3.4 新增执行方法3.4.1 执行器接口添加update3.4.2 执行器抽象基类3.4.3 简单执行器 3.5 语句处理器实现3.5.1 …

CS420 课程笔记 P7 - 虚拟内存 多级指针寻址

文章目录 IntroPointersMemory leaksPointer pathPointer scanningExample! Intro 上节课我们学习了静态地址&#xff0c;这节课我们将着手关注动态地址&#xff0c;我们需要了解一个叫做指针的东西 Pointers 简单地说&#xff0c;指针是对象之间的单向连接 Pointers are co…

【 BlockChain 】零知识证明

【 BlockChain 】零知识证明 一、零知识证明起源 “零知识”的概念最早在80年代由麻省理工学院的研究人员 Shafi Goldwasser&#xff0c;Silvio Micali 和 Charles Rackoff 所提出。当时这些人正在研究与交互证明系统相关的问题——即一种理论系统&#xff0c;使得甲方&#…

QT6删除旧的编译文件,编译会出错,可以选择重新选择编译

QT6工程文件和编译文件的路径关系 下面是显示的工程情况 可能编译文件全部被删除&#xff0c;或者部分缺失 编译问题出现后的处理办法 删去编译工程时添加的内容&#xff0c;其中主要修改CMakeLists.txt中添加的内容&#xff0c;例如添加的路径或resource内容&#xff0c;可…

两个非递减顺序表合并成一个非递减顺序表

两个非递减顺序表合并成一个非递减顺序表 引入 以下这个例题的描述是关于合并两个有序的数组&#xff0c;然后合并之后同样也是一个非递减的顺序排列 但是我名这里讲的不是顺序表&#xff0c;而是封装成一个顺序表&#xff0c;但是我们这里的顺序表其实底层同样是一个数组&a…

复盘|攻防实战中面对“谍影重重”,如何出奇制胜?

随着“数字孪生”概念的深入人心&#xff0c;数字化转型的进展也是一日千里。与此同时&#xff0c;在网络世界中的攻防对抗热度有增无减&#xff0c;甚至连最顶级的安全团队一不小心也会中招。在攻防演练中发生过好几次这种情况&#xff1a;腾讯安全一线的分析人员在前方为攻击…

ATSHA204A国产替代新选择:LCSHA204

ATSHA204A是一款用于固件版权保护&#xff0c;防抄板仿克隆的加密芯片。包含多达16个密钥存储区用于安全存储密钥。同时提供OTP区用于存储用户固定信息。因推出时间早以及芯片本身的安全性在中国市场的使用量非常大。 受当前全球经济和大环境的影响造成该芯片供货严重不足&…