当前位置: 首页 > news >正文

java的双亲委派模型-附源码分析

1、类加载器

1.1 类加载的概念

  要了解双亲委派模型,首先我们需要知道java的类加载器。所谓类加载器就是通过一个类的全限定名来获取描述此类的二进制字节流,然后把这个字节流加载到虚拟机中,获取响应的java.lang.Class类的一个实例。我们把实现这个动作的代码模块称为“类加载器”。

1.2 类与类加载器

  对于任意的一个类,都需要由加载它的类加载器和这个类本身一同建立其在Java虚拟机中的唯一性,每个类加载器,都拥有一个独立的类名称空间,即:比较两个类是否“相等”,只有在这两个类是由同一个类加载器加载的前提下才有意义,否则,即使这两个类源于同一个Class文件,被同一个虚拟机加载,只要加载它们的类加载器不同,那这两个类就必定不想等。

package com.demo.test;import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;/*** @author lxc* @createTime 2023-02-09 10:20* @description*/
public class ClassLoaderTest {public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {ClassLoader myloader = new ClassLoader() {@Overridepublic Class<?> loadClass(String name) throws ClassNotFoundException {String fileName = name.substring(name.lastIndexOf(".") + 1) + ".class";InputStream is = getClass().getResourceAsStream(fileName);if (is == null) {return super.loadClass(name);}try {byte[] b = new byte[is.available()];is.read(b);return defineClass(name,b,0,b.length);} catch (IOException e) {throw new RuntimeException(e);}}};Object obj = myloader.loadClass("com.demo.test.ClassLoaderTest").newInstance();System.out.println(obj.getClass());System.out.println(obj instanceof ClassLoaderTest);Class<?> clazz = Class.forName("com.demo.test.ClassLoaderTest");Constructor<?> constructor = clazz.getConstructor();Object obj1 = constructor.newInstance();System.out.println(obj1 instanceof ClassLoaderTest);}
}

运行结果如下:

obj对象的Class:class com.demo.test.ClassLoaderTest
obj1对象的Class:class com.demo.test.ClassLoaderTest
false
true

从运行结果来看,第一句和第二句来看,两个对象都是由类class com.demo.test.ClassLoaderTest实例化出来的对象;
从第三句可以看出,这个对象与类class com.demo.test.ClassLoaderTest做所属类型检查时却返回了false,这是因为虚拟机中存在了两个ClassLoaderTest,一个是由我们自定义的类加载器加载的,
另一个是由系统应用程序类加载器加载的,虽然都来自同一个Class文件,但依然是两个独立的类,做对象所属类型检查时结果为false。

2、双亲委派模型

2.1 类加载器的分类

  从虚拟机的角度来看,存在两种不同的类加载器:一种是启动类加载器(Bootstrap ClassLoader),这个类加载器使用C++语言实现,是虚拟机自身的一部分;另一个就是所有的其他类加载器,这些类加载器都是由Java语言实现,独立于虚拟机外部,并且全都继承字抽象类java.lang.ClassLoader。
  从开发者角度来看,类加载器可以分为以下四类:启动类加载器(Bootstrap ClassLoader)、扩展类加载器(Extension ClassLoader)、应用程序类加载(Application ClassLoader)和自定义类加载器。

  • 启动类加载器(Bootstrap ClassLoader):这个类加载器是加载核心java库,负责将<JAVA_HOME>/jre/lib目录中的,或者被-Xbootclasspath参数所指定的路径中的,并且是虚拟机识别的(仅仅按照文件名识别,如rt.jar,名字不符合的类库即使放在lib目录下也不会被加载)类库加载到虚拟机内存中。开发者不能直接使用启动类加载器。
  • 扩展类加载器(Extension ClassLoader):这个类加载器是由sun.misc.Launcher$ExtClassLoader实现,它负责加载<JAVA_HOME>/jre/lib/ext目录中的,或者被java.ext.dirs系统变量所指定的路径中的所有类库,开发者可以直接使用该类加载器。
  • 应用程序类加载器(Application ClassLoader):这个类加载器是由sun.misc.Launcher$AppClassLoader实现。这个类加载器是ClassLoader中的getSystemClassLoader()方法的返回值,所以一般也称之为系统类加载器。它负责加载用户类路径(ClassPath)上所指定的类库,开发者可以直接使用这个类加载器,如果应用程序中没有定义过自己的类加载器,一般情况下这就是程序中默认的类加载器。
      通过上面的分类我们可以看到,这三种类加载器只能加载各自所负责的目录下的类库,而不能加载超过其目录范围的类库,这也就是我们常常说的双亲委派模型中的可见性原则。
      我们平时所写的应用程序都是由这三种类加载器相互配合进行加载的,如果有必要,还可以加上自己定的类加载器。

2.2 双亲委派模型中各类加载器之间的层次关系

在这里插入图片描述
  类加载器之间这种层次关系,我们称之为类加载的双亲委派模型。双亲委派模型中,除了顶层的启动类加载器外,其余的类加载器都应当有自己的父-类加载。这里的父子关系不是以继承关系来实现的,而都是使用组合的关系来复用父-类加载的代码。

2.3 双亲委派模型中类加载的工作过程

  如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父-类加载去完成,每一个层次的类加载器(启动类加载器除外)都是如此,因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有当父-类加载器反馈自己无法完成这个加载请求时,子-类加载器才会尝试自己去加载。
下面我们看段源码,从代码角度看一下这个工作过程:

protected Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException{synchronized (getClassLoadingLock(name)) {// First, check if the class has already been loaded//首先检查请求的类是否被加载过Class<?> c = findLoadedClass(name);if (c == null) {//如果没有被加载过,就进行加载操作long t0 = System.nanoTime();try {//加载时,如果存在父-类加载器,就用父-类加载器加载//如果没有父-类加载器,就说明这个类加载器是启动类加载器,就找启动类加载器进行加载if (parent != null) {c = parent.loadClass(name, false);} else {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.//在父-类加载器无法完成加载的时候,再调用本身的findClass方法来进行类加载long t1 = System.nanoTime();c = findClass(name);// this is the defining class loader; record the statssun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);sun.misc.PerfCounter.getFindClasses().increment();}}if (resolve) {resolveClass(c);}return c;}}

  双亲委派模型对于保证java的稳定运行很重要,但从上面的源码来看,实现还是比较简单的,双亲委派模型的核心代码主要都在java.lang.ClassLoader的loadClass()方法中,大体逻辑如下:
  先检查是否已经被加载过,若没有加载则调用父-类加载的loadClass()方法,若父类加载为空则默认使用启动类加载器做父-类加载器加载。如果父-类加载器加载失败,抛出ClassNotFoundException异常后,再调用自己的findClass()方法进行加载。

2.4 双亲委派模型的目的

  那么我们思考一下,java为什么采用双亲委派模型呢?从上面双亲委派模型的工作过程,我们看出,java类随着它的类加载器一起具备了带有优先级的层次关系。例如类java.lang.Integer,它存放在核心包rt.jar中,那么无论哪一个类加载器要加载这个类,最终都
是要委派给处于最顶端的启动类加载器进行加载,从而是的Integer类在程序的各中类加载器环境中都是同一个类;相反,如果没有使用双亲委派模型,而是由各个类加载器自行去加载的话,当用户自己编写了一个名为java.lang.Integer类并放到ClassPath中,那么系统将会出现多个不同的Integer类,这样就会造成java体系中最基础的行为都无法保证(连最基本的类型都不唯一),程序将变得一片混乱。你可能会说,我自定义一个类加载去加载java.lang.Integer,直接重写loadClass方法,从而破坏掉双亲委派模型不就行了。
  我们写个简单的例子试下。

public class MyClassLoader extends ClassLoader {@Overridepublic Class<?> loadClass(String name) throws ClassNotFoundException {String className = null;if (name.startsWith("java.lang")) {className = "/" + name.replace(".", "/") + ".class";} else {className = name.substring(name.lastIndexOf(".") + 1) + ".class";}InputStream is = getClass().getResourceAsStream(className);if (is == null) {return super.loadClass(name);}try {byte[] bytes = new byte[is.available()];is.read(bytes);return defineClass(name, bytes, 0, bytes.length);} catch (IOException e) {throw new RuntimeException(e);}}public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {ClassLoader myLoader = new MyClassLoader();Object obj = myLoader.loadClass("java.lang.Integer").newInstance();System.out.println(obj);}
}

结果输出:

Exception in thread "main" java.lang.SecurityException: Prohibited package name: java.langat java.lang.ClassLoader.preDefineClass(ClassLoader.java:662)at java.lang.ClassLoader.defineClass(ClassLoader.java:761)at java.lang.ClassLoader.defineClass(ClassLoader.java:642)at com.demo.test.MyClassLoader.loadClass(MyClassLoader.java:28)at com.demo.test.MyClassLoader.main(MyClassLoader.java:36)

  从源码分析来看,主要是defineClass方法调用的preDefineClass方法异常,在preDefineClass这个方法中我们看到:

	 if ((name != null) && name.startsWith("java.")) {throw new SecurityException("Prohibited package name: " +name.substring(0, name.lastIndexOf('.')));}

即如果是以java.开头的包下的类,都只能用启动类加载器来加载。

2.5 双亲委派模型的三个原则

  双亲委派模型有三个基本原则:委托性、可见性和唯一性原则。

  • 委托性原则:当子类加载器收到类加载请求时,会将加载请求向上委托给父类加载器;
  • 可见性原则:每种类加载器都有自己可加载类库的范围,超出这个范围是不可见的,即无法加载的;
  • 唯一性原则:这是双亲委派模型的核心,也是最重要的目的。

2.6 为什么要打破双亲委派模型

  我们这里主要说一下JDBC为什么要打破双亲委派模型,其他的方面我后续再分析。
  我们以mysql数据库驱动为例来说明。最早我们使用mysql数据库驱动的时候,一般是这样写代码:

    Class.forName("com.mysql.jdbc.Driver");Connection conn = DriverManager.getConnection("jdbc:mysql://host:port/dbname?useUnicode=true&characterEncoding=utf-8&useSSL=false", "username", "password");

  其中com.mysql.jdbc.Driver下的Driver.class的源码如下:

			 package com.mysql.jdbc;import java.sql.DriverManager;import java.sql.SQLException;public class Driver extends NonRegisteringDriver implements java.sql.Driver {public Driver() throws SQLException {}static {try {DriverManager.registerDriver(new Driver());} catch (SQLException var1) {throw new RuntimeException("Can't register driver!");}}}

  从com.mysql.jdbc的Driver.java源码中看到,在Driver类中向DriverManager注册了对应的驱动实现类。
  而从JDBC4.0以后,开始支持使用SPI的方式来注册这个Driver,这样当我们使用不同jdbc驱动时,就不用手动修改Class.forName加载的驱动类,只需要加入相关的jar包就行了。所以上面的数据库连接代码可以简写成如下:

Connection conn = DriverManager.getConnection("jdbc:mysql://host:port/dbname?useUnicode=true&characterEncoding=utf-8&useSSL=false", "username", "password");

这就不需要Class.forName(“com.mysql.jdbc.Driver”)了。
了解SPI的同学都知道,在DriverManager中,这时候对应的驱动类大体是这么加载的:
  1.通过从META-INF/services/java.sql.Driver文件中获取具体的实现类”com.mysql.jdbc.Driver“;
  2.通过Class.forName(“com.mysql.jdbc.Driver”)将这个类加载进来。
  但是DriverManager是在java.sql中,在rt.jar包中,这个包中的类只能使用启动类加载器进行加载,那么根据类加载的机制,当被装载的类引用了另外一个类的时候,虚拟机就会使用装载第一个类的类装载器装载被引用的类。:启动类加载器还要去加载mysql驱动jar中的类(com.mysql.jdbc.Driver),这显然是不可能的,根据双亲委派模型的可见性原则,启动类加载器找不到这个mysql类库,所以无法加载。
  这个问题更加有适用性的说法应该是:JAVA核心包中的类去调用开发者实现的类的方法,这时候就会出现启动类加载器无法加载到具体实现类的问题。所以想让启动类加载器(顶层类加载器)加载可见范围之外的类库,只能破坏双亲委派模型中的可见性原则,让启动类加载器可以”加载“到可见范围之外的类库。主要这里我加了个引号,因为这个地方并不是真的是由启动类加载器加载了com.mysql.jdbc.Driver这个类库,其实还是由Application ClassLoader系统类加载器加载完成的,只不过从表面上看起来是破坏了可见行原则,实质上并没有破坏双亲委派原则。
  下面我们看DriverManager是怎么实现的。
  DriverManager加载时,会执行静态代码块,在静态代码块中,会执行loadInitialDrivers方法。而这个方法中会加载对应的驱动类。

public class DriverManager {static {loadInitialDrivers();println("JDBC DriverManager initialized");}private static void loadInitialDrivers() {String drivers;try {drivers = AccessController.doPrivileged(new PrivilegedAction<String>() {public String run() {return System.getProperty("jdbc.drivers");}});} catch (Exception ex) {drivers = null;}AccessController.doPrivileged(new PrivilegedAction<Void>() {public Void run() {// 根据配置文件加载驱动实现类,下面这个方法中说明了所使用的类加载器ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);Iterator<Driver> driversIterator = loadedDrivers.iterator();try{while(driversIterator.hasNext()) {driversIterator.next();}} catch(Throwable t) {// Do nothing}return null;}});println("DriverManager.initialize: jdbc.drivers = " + drivers);if (drivers == null || drivers.equals("")) {return;}String[] driversList = drivers.split(":");println("number of Drivers:" + driversList.length);for (String aDriver : driversList) {try {println("DriverManager.Initialize: loading " + aDriver);Class.forName(aDriver, true,ClassLoader.getSystemClassLoader());} catch (Exception ex) {println("DriverManager.Initialize: load failed: " + ex);}}}
}public static <S> ServiceLoader<S> load(Class<S> service) {//使用了一个线程上下文类加载器ClassLoader cl = Thread.currentThread().getContextClassLoader();return ServiceLoader.load(service, cl);}

  ExtClassLoader和AppClassLoader都是通过Launcher类来创建的,在Launcher类的构造函数中我们可以看到线程上下文类加载器默认是AppClassLoader。Launcher类中无参构造方法:

public Launcher() {ExtClassLoader var1;try {var1 = Launcher.ExtClassLoader.getExtClassLoader();} catch (IOException var10) {throw new InternalError("Could not create extension class loader", var10);}try {this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);} catch (IOException var9) {throw new InternalError("Could not create application class loader", var9);}//设置当前线程的上下文类加载器就是AppClassLoaderThread.currentThread().setContextClassLoader(this.loader);String var2 = System.getProperty("java.security.manager");if (var2 != null) {SecurityManager var3 = null;if (!"".equals(var2) && !"default".equals(var2)) {try {var3 = (SecurityManager)this.loader.loadClass(var2).newInstance();} catch (IllegalAccessException var5) {} catch (InstantiationException var6) {} catch (ClassNotFoundException var7) {} catch (ClassCastException var8) {}} else {var3 = new SecurityManager();}if (var3 == null) {throw new InternalError("Could not create SecurityManager: " + var2);}System.setSecurityManager(var3);}

2.7 如何打破双亲委派模型?

  在ClassLoader中有几个核心方法,上面我们已经展示了loadClass的基本源码,下面我们再简略看一下(去掉了一些代码细节):

    package java.lang;public abstract class ClassLoader {protected Class defineClass(byte[] b); protected Class<?> findClass(String name); protected Class<?> loadClass(String name, boolean resolve) {synchronized (getClassLoadingLock(name)) {// 1. 检查类是否已经被加载过Class<?> c = findLoadedClass(name);if (c == null) {try {if (parent != null) {//2. 委托给父类加载c = parent.loadClass(name, false);} else {//3. 父类不存在的,交给启动类加载器c = findBootstrapClassOrNull(name);}} catch (ClassNotFoundException e) { }if (c == null) {//4. 父类加载器无法完成类加载请求时,调用自身的findClass方法来完成类加载c = findClass(name);}}return c;}
}
  • defineClass 方法:调用 native 方法将 字节数组解析成一个 Class 对象;
  • findClass 方法:抽象类ClassLoader中默认抛出ClassNotFoundException,需要继承类自己去实现,目的是通过文件系统或者网络查找类;
  • loadClass 方法: 首先根据类的全限定名检查该类是否已经被加载过,如果没有被加载,那么当子加载器持有父加载器的引用时,那么委托给父加载器去尝试加载,如果父类加载器无法完成加载,再交给子类加载器进行加载。loadClass方法 就是实现了双亲委派机制。
      ClassLoader 的三个重要方法,那么如果需要自定义一个类加载器的话,直接继承 ClassLoader类,一般情况只需要重写 findClass 方法即可,自己定义加载类的路径,可以从文件系统或者网络环境。但是,如果想打破双亲委派机制,那么还要重写 loadClass 方法。

相关文章:

java的双亲委派模型-附源码分析

1、类加载器 1.1 类加载的概念 要了解双亲委派模型&#xff0c;首先我们需要知道java的类加载器。所谓类加载器就是通过一个类的全限定名来获取描述此类的二进制字节流&#xff0c;然后把这个字节流加载到虚拟机中&#xff0c;获取响应的java.lang.Class类的一个实例。我们把实…...

Docker 笔记

Docker docker pull redis&#xff1a;5.0 docker images [image:57DAAA3E-CC88-454B-B8AC-587E27C9CD3A-85324-0001A93C6707F2A4/93F703D2-5F44-49AB-83C7-05E2E22FB226.png] Docker有点类似于虚拟机 区别大概&#xff1a; docker&#xff1a;启动 Docker 相当于启动宿主操…...

用户认证-cookie和session

无状态&短链接 短链接的概念是指&#xff1a;将原本冗长的URL做一次“包装”&#xff0c;变成一个简洁可读的URL。 什么是短链接-> https://www.cnblogs.com/54chensongxia/p/11673522.html HTTP是一种无状态的协议 短链接&#xff1a;一次请求和一次响应之后&#…...

UUID的弊端以及雪花算法

目录 一、问题 为什么需要分布式全局唯一ID以及分布式ID的业务需求 ID生成规则部分硬性要求 ID号生成系统的可用性要求 二、一般通用方案 &#xff08;一&#xff09;UUID &#xff08;二&#xff09;数据库自增主键 &#xff08;三&#xff09;Redis生成全局id策略 三…...

使用netty+springboot打造的tcp长连接通讯方案

文章目录项目背景正文一、项目架构二、项目模块三、业务流程四、代码详解1.消息队列2.执行类3.客户端五、测试六、源码后记项目背景 最近公司某物联网项目需要使用socket长连接进行消息通讯&#xff0c;捣鼓了一版代码上线&#xff0c;结果BUG不断&#xff0c;本猿寝食难安&am…...

【正点原子FPGA连载】第十章PS SYSMON测量温度电压实验 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南

1&#xff09;实验平台&#xff1a;正点原子MPSoC开发板 2&#xff09;平台购买地址&#xff1a;https://detail.tmall.com/item.htm?id692450874670 3&#xff09;全套实验源码手册视频下载地址&#xff1a; http://www.openedv.com/thread-340252-1-1.html 第十章PS SYSMON…...

AcWing《蓝桥杯集训·每日一题》—— 1460 我在哪?

AcWing《蓝桥杯集训每日一题》—— 1460. 我在哪&#xff1f; 文章目录AcWing《蓝桥杯集训每日一题》—— 1460. 我在哪&#xff1f;一、题目二、解题思路三、代码实现本次博客我是通过Notion软件写的&#xff0c;转md文件可能不太美观&#xff0c;大家可以去我的博客中查看&am…...

AcWing《蓝桥杯集训·每日一题》—— 3729 改变数组元素

AcWing《蓝桥杯集训每日一题》—— 3729. 改变数组元素 文章目录AcWing《蓝桥杯集训每日一题》—— 3729. 改变数组元素一、题目二、解题思路三、代码实现本次博客我是通过Notion软件写的&#xff0c;转md文件可能不太美观&#xff0c;大家可以去我的博客中查看&#xff1a;北天…...

如何熟练掌握Python在气象水文中的数据处理及绘图【免费教程】

pythonPython由荷兰数学和计算机科学研究学会的吉多范罗苏姆于1990年代初设计&#xff0c;作为一门叫做ABC语言的替代品。Python提供了高效的高级数据结构&#xff0c;还能简单有效地面向对象编程。Python语法和动态类型&#xff0c;以及解释型语言的本质&#xff0c;使它成为多…...

Leetcode详解JAVA版

目录1. 两数之和14. 最长公共前缀15. 三数之和18. 四数之和19. 删除链表的倒数第 N 个结点21. 合并两个有序链表28. 找出字符串中第一个匹配项的下标36. 有效的数独42. 接雨水43. 字符串相乘45. 跳跃游戏 II53. 最大子数组和54. 螺旋矩阵55. 跳跃游戏62. 不同路径70. 爬楼梯73.…...

LeetCode 83. 删除排序链表中的重复元素

原题链接 难度&#xff1a;easy\color{Green}{easy}easy 题目描述 给定一个已排序的链表的头 headheadhead &#xff0c; 删除所有重复的元素&#xff0c;使每个元素只出现一次 。返回 已排序的链表 。 示例 1&#xff1a; 输入&#xff1a;head [1,1,2] 输出&#xff1a;…...

RMI简易实现(基于maven)

参考其它rmi&#xff08;remote method invocation&#xff09;的代码后&#xff0c;加入了自己思考。整个工程基于maven构建&#xff0c;我觉得maven的模块化可比较直观地演示rmi 目录 项目结构图 模块解读 pom文件 rmi-impl rmi-common-interface rmi-server rmi-cli…...

‘excludeSwitches‘ 的 [‘enable-logging‘] 和[‘enable-automation‘]

selenium 使用 chrome 浏览器的 chromedriver 时&#xff0c;可以加参数&#xff0c; chrome_optionswebdriver.ChromeOptions() chrome_options.add_experimental_option(excludeSwitches,[enable-logging]) chrome_options.add_experimental_option(excludeSwitches,[enable…...

华为OD机试 - 最短木板长度(Python)| 真题+思路+考点+代码+岗位

最短木板长度 题目 小明有 n n n 块木板,第 i i i(1≤ i i...

第一个Python程序-HelloWorld与Python解释器

数据来源 01 第一个Python程序-HelloWorld 1&#xff09;打开cmd&#xff1a; windows R 打开运行窗口输入cmd 2&#xff09;进入Python编写页面 输入&#xff1a;python 3&#xff09;然后输入要写的Python代码然后回车 print("Hello World!!!") print() …...

C++数据类型

目录 一、基本的内置类型 二、typedef声明 三、枚举类型 一、基本的内置类型 C 为程序员提供了种类丰富的内置数据类型和用户自定义的数据类型。下表列出了七种基本的 C 数据类型&#xff1a; 类型关键字布尔型bool字符型char整型int浮点型float双浮点型double无类型void宽…...

华为OD机试 - 考古学家(Python)| 真题+思路+考点+代码+岗位

考古学家 题目 有一个考古学家发现一个石碑 但是很可惜 发现时其已经断成多段 原地发现 N 个断口整齐的石碑碎片 为了破解石碑内容 考古学家希望有程序能帮忙计算复原后的石碑文字组合数 ,你能帮忙吗 备注: 如果存在石碑碎片内容完全相同,则由于碎片间的顺序不影响复原后…...

常用调试golang的bug以及性能问题的实践方法

文章目录如何分析程序运行时间和CPU利用率情况1.shell内置time指令/usr/bin/time指令如何分析golang程序的内存使用情况&#xff1f;1.内存占用情况查看如何分析golang程序的CPU性能情况1.性能分析注意事项2.CPU性能分析A.Web界面查看B.使用pprof工具查看如何分析程序运行时间和…...

什么是溶血症?什么是ABO溶血?溶血检查些什么?

什么是溶血症&#xff0c;什么是ABO溶血&#xff1f;女人是O型血&#xff0c;男人是其他血型的夫妻配对&#xff0c;最担心的是胎儿溶血症。从理论上讲&#xff0c;只要夫妻双方血型不同&#xff0c;母亲一定缺乏胎儿从父亲那里遗传的抗原。当任何人接触到他们缺乏的抗原时&…...

NLP实践——知识图谱问答模型FiD

NLP实践——知识图谱问答模型FiD0. 简介1. 模型结构2. 召回3. 问答4. 结合知识的问答0. 简介 好久没有更新了&#xff0c;今天介绍一个知识图谱问答&#xff08;KBQA&#xff09;模型&#xff0c;在此之前我一直在用huggingface的Pipeline中提供的QA模型&#xff0c;非常方便但…...

uni-app学习笔记二十二---使用vite.config.js全局导入常用依赖

在前面的练习中&#xff0c;每个页面需要使用ref&#xff0c;onShow等生命周期钩子函数时都需要像下面这样导入 import {onMounted, ref} from "vue" 如果不想每个页面都导入&#xff0c;需要使用node.js命令npm安装unplugin-auto-import npm install unplugin-au…...

[ICLR 2022]How Much Can CLIP Benefit Vision-and-Language Tasks?

论文网址&#xff1a;pdf 英文是纯手打的&#xff01;论文原文的summarizing and paraphrasing。可能会出现难以避免的拼写错误和语法错误&#xff0c;若有发现欢迎评论指正&#xff01;文章偏向于笔记&#xff0c;谨慎食用 目录 1. 心得 2. 论文逐段精读 2.1. Abstract 2…...

【Go】3、Go语言进阶与依赖管理

前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课&#xff0c;做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程&#xff0c;它的核心机制是 Goroutine 协程、Channel 通道&#xff0c;并基于CSP&#xff08;Communicating Sequential Processes&#xff0…...

【2025年】解决Burpsuite抓不到https包的问题

环境&#xff1a;windows11 burpsuite:2025.5 在抓取https网站时&#xff0c;burpsuite抓取不到https数据包&#xff0c;只显示&#xff1a; 解决该问题只需如下三个步骤&#xff1a; 1、浏览器中访问 http://burp 2、下载 CA certificate 证书 3、在设置--隐私与安全--…...

【开发技术】.Net使用FFmpeg视频特定帧上绘制内容

目录 一、目的 二、解决方案 2.1 什么是FFmpeg 2.2 FFmpeg主要功能 2.3 使用Xabe.FFmpeg调用FFmpeg功能 2.4 使用 FFmpeg 的 drawbox 滤镜来绘制 ROI 三、总结 一、目的 当前市场上有很多目标检测智能识别的相关算法&#xff0c;当前调用一个医疗行业的AI识别算法后返回…...

Rapidio门铃消息FIFO溢出机制

关于RapidIO门铃消息FIFO的溢出机制及其与中断抖动的关系&#xff0c;以下是深入解析&#xff1a; 门铃FIFO溢出的本质 在RapidIO系统中&#xff0c;门铃消息FIFO是硬件控制器内部的缓冲区&#xff0c;用于临时存储接收到的门铃消息&#xff08;Doorbell Message&#xff09;。…...

Maven 概述、安装、配置、仓库、私服详解

目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

学校时钟系统,标准考场时钟系统,AI亮相2025高考,赛思时钟系统为教育公平筑起“精准防线”

2025年#高考 将在近日拉开帷幕&#xff0c;#AI 监考一度冲上热搜。当AI深度融入高考&#xff0c;#时间同步 不再是辅助功能&#xff0c;而是决定AI监考系统成败的“生命线”。 AI亮相2025高考&#xff0c;40种异常行为0.5秒精准识别 2025年高考即将拉开帷幕&#xff0c;江西、…...

深度学习习题2

1.如果增加神经网络的宽度&#xff0c;精确度会增加到一个特定阈值后&#xff0c;便开始降低。造成这一现象的可能原因是什么&#xff1f; A、即使增加卷积核的数量&#xff0c;只有少部分的核会被用作预测 B、当卷积核数量增加时&#xff0c;神经网络的预测能力会降低 C、当卷…...

使用Matplotlib创建炫酷的3D散点图:数据可视化的新维度

文章目录 基础实现代码代码解析进阶技巧1. 自定义点的大小和颜色2. 添加图例和样式美化3. 真实数据应用示例实用技巧与注意事项完整示例(带样式)应用场景在数据科学和可视化领域,三维图形能为我们提供更丰富的数据洞察。本文将手把手教你如何使用Python的Matplotlib库创建引…...