java基础——类加载机制

news2024/11/15 19:58:05

类加载机制

  • 一、背景知识补充
  • 二、类加载过程/机制
    • 1、浅层理解
    • 2、大致步骤
    • 3、具体步骤
      • (3.1)装载loading:查找和导入相应的class文件
      • (3.2)链接linking:把类的二进制数据合并到JRE中
      • (3.3)初始化initializing:对类的静态变量,静态代码块执行初始化操作,赋初始值
  • 三、类的初始化
    • 1、类的初始化步骤
    • 2、原因
  • 四、类加载器
    • 1、双亲委派机制定义
    • 2、双亲委派机制存在的意义
    • 3、类加载器分类
    • 4、四种类加载器间关系
    • 5、源码解读
    • 6、Tomcat的加载机制

一、背景知识补充

1、java执行图序
在这里插入图片描述
2、JVM内存模型
在这里插入图片描述

3、类变量
类变量,也叫静态变量,用static标识

二、类加载过程/机制

1、浅层理解

类加载:java虚拟机将Class字节码文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的java类型

2、大致步骤

1.找到对应的class文件,解析并校验文件内容是否符合
2.校验通过之后load进jvm虚拟机内存中为当前的java类创建一个相应的Class对象并初始化

3、具体步骤

类装载器把一个类装入jvm中,要经过以下三大步

(3.1)装载loading:查找和导入相应的class文件

1.1 通过一个类的全限定名来获取定义此类的二进制字节流
1.2 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构
1.3 在java堆中生成一个代表这个类的java.lang.Class对象,通过该对象访问方法区中的这些数据

(3.2)链接linking:把类的二进制数据合并到JRE中

2.1 校验-verification 检查载入class文件数据的正确性

  1. 文件格式验证 CA、FE、BA、BE;
  2. 元数据验证;
  3. 字节码验证;
  4. 符号引用验证。

2.2 准备-preparation 给类的静态变量分配存储空间 赋默认值【零值】

  1. 只包含static 修饰的类变量,会分配在方法区
  2. 不包含使用final修饰的static,因为final在编译的时候分配。

原因:常量是一种特殊的变量,**在编译器把他们当做值(value) 【直接初始化了变量名和对应的值在字节码中】,而不是域(field)来对待。**若代码中用到了常变量,编译器并不会生成字节码来从对象中载入域的值,而是直接把这个值插入到字节码中。这是一种有用的优化,但是如果需要改变final域的值,那么每一块用到这个域的代码都需要重新编译。

2.3 解析-resolution:虚拟机将常量池内的符号引用替换为直接引用的过程

  1. 符号引用(Symbolic Reference) : 以一组符号来描述所引用的目标类。
  2. 直接引用(Direct Reference): 可以是直接指向目标的指针、相对偏移量或是一个能间接定位到目标的句柄。
    【直接引用与虚拟机实现的内存布局相关,同一个符号引用在不同的虚拟机实例上翻译出来的直接引用一般不相同,若存在了直接引用,则引用的目标必定已经在内存中存在的。】

1). 类或者接口的解析:判断所要转化成的直接引用是对数组类型,还是普通的对象类型的引用,从而进行不同的解析
2). 字段解析:对字段进行解析时,会先在本类中查找是否包含有简单名称和字段描述符都与目标相匹配的字段,若果有,则查找结束;如果没有,则会按照继承关系从上往下递归搜索该类所实现的各个接口和它们的父接口,还没有,则按照继承关系从上往下递归搜索其父类,直至查找结束
3). 类方法解析: 对类方法的解析与对字段解析的搜索步骤差不多,只是多了判断该方法所处的是类还是接口的步骤,且对类方法的匹配搜索,是先搜索父类,再搜索接口
4). 接口方法解析:与类方法解析步骤类似,只是接口不会有父类,因此只递归向上搜索父接口就行

(3.3)初始化initializing:对类的静态变量,静态代码块执行初始化操作,赋初始值

定义:初始化阶段就是开始执行类中定义的java程序代码、执行类构造器方法()。
具体工作:JVM负责对类进行初始化,主要对类变量static赋正确的初始值,在java中对类变量进行初始化的两种方式:

  1. 声明变量时指定初始值
  2. 使用静态代码块为类变量指定初始值

三、类的初始化

1、类的初始化步骤

1 初始化父类的静态成员
2 初始化父类的静态代码块

3 初始化子类的静态成员
4 初始化子类的静态代码块
5 初始化父类的非静态成员
6 初始化父类的非静态代码块
7 初始化父类的构造方法

8 初始化子类的非静态成员
9 初始化子类的非静态代码块
10 初始化子类的构造方法

2、原因

1.类构造器方法是由编译器自动收集类中的所有类变量的赋值动作和静态语句块(static{}块) 中的语句合并产生的。

  • 编译器收集的顺序由语句在源文件中出现的顺序所决定

2.类构造器方法与类的构造函数不同,它不需要显示地调用父类构造器。虚拟机会保证在子类的类构造器方法执行之前,父类的类构造器方法已经执行完毕,因此在虚拟机中第一个执行的类构造器方法的类是java.lang.Object

3.接口中可能会有变量的赋值操作,因此接口也会生成类构造器方法,但是接口与类不同,执行接口的类构造器方法不需要先执行父接口的类构造器方法。只有当父接口中定义的变量被使用时,父接口才会被初始化。另:接口实现类在初始化时也不会执行接口的类构造器方法

4.虚拟机会保证一个类的类构造器方法在多线程环境中被正确地加锁和同步。若有多个线程同时初始化一个类,那么只会有一个线程去执行这个类的类构造器方法,其他线程都需要阻塞等待,直到活动线程执行类构造器方法完毕
思考:如果这个线程已经执行完该类构造器方法构建类完成,还需要其他线程再调用该类构造器方法吗?

四、类加载器

1、双亲委派机制定义

  • 双亲委派机制定义:当一个类加载器收到了类加载的请求的时候,他不会直接去加载指定的类,而是把这个请求委托给自己的父加载器去加载。只有父加载器无法加载这个类的时候,才会由当前这个加载器来负责类的加载。

2、双亲委派机制存在的意义

  1. 通过委派的方式,避免类的重复加载。当父加载器已经加载过某一个类时,子加载器就不会再重新加载这个类。
  2. 通过双亲委派的方式,保证安全性。因为Bootstrap ClassLoader在加载的时候,只会加载JAVA_HOME中的jar包里面的类,如java.lang.Integer,那么这个类是不会被随意替换的(除非有人跑到你的机器上, 破坏你的JDK)。因此,可以避免有人自定义一个有破坏功能的java.lang.Integer被加载,有效的防止核心Java API被篡改。

3、类加载器分类

  1. BootStrap ClassLoader:由C++语言实现启动,加载Java核心类库 /lib/rt.jar。属于虚拟机自身的一部分。
  2. Extension ClassLoader:扩展类加载器(抽象类) jre/lib/ext.jar
  3. Application ClassLoader:应用类加载器,加载classpath下的所有类。独立于jvm外部,继承自抽象类java.lang.ClassLoader。
  4. Custom ClassLoader:自定义类加载器,可加载指定路径的class文件。独立于jvm外部,继承自抽象类java.lang.ClassLoader。

4、四种类加载器间关系

类加载时,虽然用户自定义类不会由bootstrap classloader或是extension classloader加载(由类加载器的加载范围决定),但是代码实现还是会一直委托到bootstrap classloader, 上层无法加载,再由下层是否可以加载,如果都无法加载,就会触发findclass,抛出classNotFoundException.
在这里插入图片描述

5、源码解读

简单总结实现流程:如果一个类加载器收到类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委托给父类加载器去完成,每一个层次的加载器都是如此,因此所有的类加载器请求都会传给顶层的启动类加载器,只有当父类加载器反馈自己无法完成该加载请求(父类加载器未找到对应的类),子加载器才会尝试自己去加载。

  1. 首先判断该类是否已经被加载
  2. 该类未被加载,如果父类不为空,交给父类加载
  3. 如果父类为空,交给bootstrap classloader 加载
  4. 如果类还是无法被加载到,则触发findclass,抛出classNotFoundException(findclass这个方法当前只有一个语句,就是抛出classNotFoundException),如果想自己实现类加载器的话,可以继承classLoader后重写findclass方法,加载对应的类)。

源码:

protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // First, check if the class has already been loaded
            // 查找加载 class文件
            Class<?> c = findLoadedClass(name);
            // 
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    if (parent != null) {
                        // 优先由 父加载器加载
                        c = parent.loadClass(name, false);
                    } else {
                    	// parent == null 时由 BootstrapClassLoader 加载
                        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();
                    // 若父类加载器 未加载成功 则由自身查找加载
                    
                    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;
        }
    }

6、Tomcat的加载机制

双亲委派机制有他存在的意义,不过也存在许多场景是需要破坏这个机制的。比如 tomcat web容器里面部署了很多的应用程序,但是这些应用程序对于第三方类库的依赖版本却不一样,这些第三方类库的路径又是一样的,如果采用默认的双亲委派类加载机制,那么是无法加载多个相同的类。所以,Tomcat破坏双亲委派原则,提供隔离机制,为每个web容器单独提供一个WebAppClassLoader加载器。优先加载 Web 应用自己定义的类,没有遵照双亲委派的约定,每一个应用有自己的类加载器——WebAppClassLoader负责加载本身的目录下的class文件,加载不到时再交给CommonClassLoader加载,这和双亲委派刚好相反。

参考文献:
[1]: java类加载机制

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

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

相关文章

计算机的操作系统

目录 ❤ 什么是操作系统? ❤ 什么是文件? ❤ 什么是应用程序? ❤ 为什么要有操作系统? ​❤ 操作系统有什么用? ❤ 操作系统和应用程序的启动 python从小白到总裁完整教程目录:https://blog.csdn.net/weixin_67859959/article/details/129328397?spm1001.201…

json-server的使用

流程 1.安装json-server的两个依赖 npm -g i json-server npm install -g json-server 2.安装axios依赖 npm i axios 3.全局导入axios使用src目录下main.js文件内 import axios from ‘axios’; 4.配置全局默认地址&#xff1a;src目录下main.js文件内 axios.defaults.bas…

十九、java虚拟机堆

堆的核心概述 1.一个JVM实例只存在一个堆内存&#xff0c;堆也是java内存管理的核心区域。 2.Java堆区子啊JVM启动的时候即被创建&#xff0c;其空间大小也就确定了&#xff0c;是jvm管理的最大一块内存空间&#xff0c; 1&#xff09;堆内存的大小是可以调节的。 3.《java虚拟…

运行时数据区及程序计数器

运行时数据区 概述 运行时数据区&#xff0c;也就是下图这部分&#xff0c;它是在类加载完成后的阶段 当我们通过前面的&#xff1a;类的加载-> 验证 -> 准备 -> 解析 -> 初始化 这几个阶段完成后&#xff0c;就会用到执行引擎对我们的类进行使用&#xff0c;同时…

记录Paint部分常用的方法

Paint部分常用的方法1、实例化之后Paint的基本配置2、shader 和 ShadowLayer3、pathEffect4、maskFilter5、colorFilter6、xfermode1、实例化之后Paint的基本配置 Paint.Align Align指定drawText如何将其文本相对于[x,y]坐标进行对齐。默认为LEFTPaint.Cap Cap指定了笔画线和路…

SpringBoot整合定时任务和邮件发送(邮箱 信息轰炸 整蛊)

SpringBoot整合定时任务和邮件发送&#xff08;邮箱 信息轰炸 整蛊&#xff09; 目录SpringBoot整合定时任务和邮件发送&#xff08;邮箱 信息轰炸 整蛊&#xff09;1.概述2.最佳实践2.1创建项目引入依赖(mail)2.2 修改yml配置文件2.3 启动类添加EnableScheduling注解2.4 执行的…

设计模式(三)--适配器模式(Adapter Pattern)

将一个接口转换成客户希望的另一个接口&#xff0c;适配器模式使接口不兼容的那些类可以一起工作.比如我们日常开发中使用到的slf4j就使用了适配器模式&#xff0c;slf4j提供了一系列打日志的api,底层调用的是log4j或者logback来打日志&#xff0c;而作为调用者&#xff0c;不需…

C++基础(二)—— 类和对象(类的封装)、对象的构造和析构(浅拷贝、深拷贝、explicit、动态分配内存)

【上一篇】C基础&#xff08;一&#xff09;—— C概述、C对C的扩展(作用域、struct类型、引用、内联函数、函数默认参数、函数占位参数、函数重载)1. 类和对象的基本概念1.1 C和C中struct区别c语言struct只有变量c语言struct 既有变量&#xff0c;也有函数1.2 类的封装我们编写…

【建议收藏】超详细的Canal入门,看这篇就够了!!!

概述 canal是阿里巴巴旗下的一款开源项目&#xff0c;纯Java开发。基于数据库增量日志解析&#xff0c;提供增量数据订阅&消费&#xff0c;目前主要支持了MySQL&#xff08;也支持mariaDB&#xff09;。 背景 早期&#xff0c;阿里巴巴B2B公司因为存在杭州和美国双机房部…

Vue3 核心模块源码解析(中)

【Vue3 核心模块源码解析(上)】讲到了 Vue2 与 Vue3的一些区别&#xff0c;Vue3 新特性的使用&#xff0c;以及略微带了一点源码。那么这篇文章就要从Vue3 模块源码解析 与 Vue3 执行逻辑解析这两个方面去给大家剖析 Vue3 的深层次&#xff0c;一起学习起来吧&#xff01; 这里…

6.深入理解SecurityConfigurer

深入理解SecurityConfigurer 一、SecurityConfigurer SecurityConfigurer 在 Spring Security 中是一个非常重要的角色。在前面的内容中曾经多次提到过&#xff0c; Spring Security 过滤器链中的每一个过滤器&#xff0c;都是通过 xxxConfigurer 来进行配置的&#xff0c;而这…

文件跳过回收站删除了常见原因|找回方法

演示机型&#xff1a;技嘉 H310M HD22.0系统版本&#xff1a;Windows 10 专业版软件版本&#xff1a;云骑士数据恢复软件3.21.0.92你有没有遇到文件跳过回收站而直接删除的情况&#xff1f;如果有的话&#xff0c;你是知道是什么原因吗&#xff1f;文件跳过回收站删除了怎么办&…

【JavaScript速成之路】JavaScript数组

&#x1f4c3;个人主页&#xff1a;「小杨」的csdn博客 &#x1f525;系列专栏&#xff1a;【JavaScript速成之路】 &#x1f433;希望大家多多支持&#x1f970;一起进步呀&#xff01; 文章目录前言1&#xff0c;初识数组1.1&#xff0c;数组1.2&#xff0c;创建数组1.3&…

SCI期刊收不收费也有门道,你知道吗?

什么是OA期刊? OA期刊是在互联网上在线出版的学术刊物&#xff0c;英文全称是OpenAccess Journal&#xff0c;中文译为“开放存取期刊”。OA期刊不同于传统的学术期刊如《自然》、《科学》等&#xff0c;采取的是向读者收费的运营模式&#xff0c;读者只有付费订阅&#xff0…

【MySQL】表的数据处理

哈喽&#xff0c;大家好&#xff01;我是保护小周ღ&#xff0c;本期为大家带来的是 MySQL 数据表中数据的基本处理的操作&#xff0c;数据表的增删改查&#xff0c;更多相关知识敬请期待&#xff1a;保护小周ღ *★,*:.☆(&#xffe3;▽&#xffe3;)/$:*.★*一、 添加数据&a…

极简RSS订阅器Miniflux

什么是 Miniflux &#xff1f; Miniflux 是一个极简主义的 RSS 阅读器。它简单、快速、轻便且非常易于使用。Miniflux 是静态编译的单个二进制文件&#xff0c;没有使用任何复杂的框架&#xff0c;也没有外部依赖&#xff0c;简单、快速、轻巧且超级容易安装。支持 Atom、RSS 1…

Docker(五)--Docker网络--源生网络、自定义网络

文章目录一、源生网络1.docker 的网桥---bridge2.host网络模型3.none 网络模型二、自定义网络模型1.bridge驱动2.指定网关和子网3.自定义网络其中内嵌dns解析4.不同网段的容器通信一、源生网络 我们先把server7上的harbor仓库down掉&#xff0c;然后查看网络&#xff0c;可以看…

微服务导学

一、微服务导学1.1 对比单体架构与分布式架构单体架构将业务的所有功能集中再一个项目中开发&#xff0c;打成一个包部署。分布式架构缺点&#xff1a; 模块多导致部署麻烦&#xff1b;架构复杂&#xff0c;难度大1.2 微服务经过良好架构设计的分布式架构方案&#xff0c;微服务…

【Springboot系列】解析Springboot事件机制,从入门到大师

系列文章&#xff1a;Spring Boot学习大纲&#xff0c;可以留言自己想了解的技术点 继续写Springboot系列&#xff0c;争取早点结束。 1、是什么 Spring的事件&#xff08;Application Event&#xff09;为Bean与Bean之间的消息通信提供了支持 事件机制中有三种角色&#x…

RTOS中事件集的实现原理以及实用应用

事件集的原理 RTOS中事件集的实现原理是通过位掩码来实现的。事件集是一种用于在任务之间传递信号的机制。在RTOS中&#xff0c;事件集通常是一个32位的二进制位向量。每个位都代表一个特定的事件&#xff0c;例如信号、标志、定时器等。 当一个任务等待一个或多个事件时&…