java八股文面试[JVM]——如何打破双亲委派模型
-
双亲委派模型的第一次“被破坏”是重写自定义加载器的loadClass(),jdk不推荐。一般都只是重写findClass(),这样可以保持双亲委派机制.而loadClass方法加载规则由自己定义,就可以随心所欲的加载类,典型的打破双亲委派模型的框架和中间件有tomcat与osgi
-
双亲委派模型的第二次“被破坏”是ServiceLoader和Thread.setContextClassLoader()。即线程上下文类加载器(contextClassLoader)。双亲委派模型很好地解决了各个类加载器的基础类统一问题(越基础的类由越上层的加载器进行加载),基础类之所以被称为“基础”,是因为它们总是作为被调用代码调用的API。但是,如果基础类又要调用用户的代码,那该怎么办呢?线程上下文类加载器就出现了。
-
SPI。这个类加载器可以通过java.lang.Thread类的setContextClassLoader()方法进行设置,如果创建线程时还未设置,它将会从父线程中继承一个;如果在应用程序的全局范围内都没有设置过,那么这个类加载器默认就是应用程序类加载器。了有线程上下文类加载器,JNDI服务使用这个线程上下文类加载器去加载所需要的SPI代码,也就是父类加载器请求子类加载器去完成类加载动作,这种行为实际上就是打通了双亲委派模型的层次结构来逆向使用类加载器,已经违背了双亲委派模型,但这也是无可奈何的事情。Java中所有涉及SPI的加载动作基本上都采用这种方式,例如JNDI,JDBC,JCE,JAXB和JBI等。
-
线程上下文类加载器默认情况下就是AppClassLoader,那为什么不直接通过getSystemClassLoader()获取类加载器来加载classpath路径下的类的呢?其实是可行的,但这种直接使用getSystemClassLoader()方法获取AppClassLoader加载类有一个缺点,那就是代码部署到不同服务时会出现问题,如把代码部署到Java Web应用服务或者EJB之类的服务将会出问题,因为这些服务使用的线程上下文类加载器并非AppClassLoader,而是Java Web应用服自家的类加载器,类加载器不同。,所以我们应用该少用getSystemClassLoader()。总之不同的服务使用的可能默认ClassLoader是不同的,但使用线程上下文类加载器总能获取到与当前程序执行相同的ClassLoader,从而避免不必要的问题
-
-
双亲委派模型的第三次“被破坏”是由于用户对程序动态性的追求导致的,这里所说的“动态性”指的是当前一些非常“热门”的名词:代码热替换、模块热部署等,简答的说就是机器不用重启,只要部署上就能用。
前言
比较两个类是否“相等”,前提是这两个类由同一个类加载器加载,
否则,即使这两个类来源于同一个Class 文件,被同一个虚拟机加载,
只要加载它们的类加载器不同,那么这两个类就必定不相等。
打破双亲委派
如下是一个自定义的类加载器TestClassLoader,并重写了findClass和loadClass:
public class TestClassLoader extends ClassLoader {public TestClassLoader(ClassLoader parent) {super(parent);}@Overrideprotected Class<?> findClass(String name) throws ClassNotFoundException {// 1、获取class文件二进制字节数组byte[] data = null;try {System.out.println(name);String namePath = name.replaceAll("\\.", "\\\\");String classFile = "C:\\study\\myStudy\\ZooKeeperLearning\\zkops\\target\\classes\\" + namePath + ".class";ByteArrayOutputStream baos = new ByteArrayOutputStream();FileInputStream fis = new FileInputStream(new File(classFile));byte[] bytes = new byte[1024];int len = 0;while ((len = fis.read(bytes)) != -1) {baos.write(bytes, 0, len);}data = baos.toByteArray();} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}// 2、字节码加载到 JVM 的方法区,// 并在 JVM 的堆区建立一个java.lang.Class对象的实例// 用来封装 Java 类相关的数据和方法return this.defineClass(name, data, 0, data.length);}@Overridepublic Class<?> loadClass(String name) throws ClassNotFoundException{Class<?> clazz = null;// 直接自己加载clazz = this.findClass(name);if (clazz != null) {return clazz;}// 自己加载不了,再调用父类loadClass,保持双亲委托模式return super.loadClass(name);}
}
测试:初始化自定义的类加载器,需要传入一个parent,指定其父类加载器,那就先指定为加载TestClassLoader的类加载器为TestClassLoader的父类加载器吧:
public static void main(String[] args) throws Exception {// 初始化TestClassLoader,被将加载TestClassLoader类的类加载器设置为TestClassLoader的parentTestClassLoader testClassLoader = new TestClassLoader(TestClassLoader.class.getClassLoader());System.out.println("TestClassLoader的父类加载器:" + testClassLoader.getParent());// 加载 DemoClass clazz = testClassLoader.loadClass("study.stefan.classLoader.Demo");System.out.println("Demo的类加载器:" + clazz.getClassLoader());
}
运行如下测试代码,发现报错了:
找不到java\lang\Object.class
,我加载study.stefan.classLoader.Demo
类和Object有什么关系呢?
转瞬想到java中所有的类都隐含继承了超类Object,加载study.stefan.classLoader.Demo,也会加载父类Object。Object和study.stefan.classLoader.Demo并不在同个目录,那就找到Object.class的目录(将jre/lib/rt.jar解压),修改TestClassLoader#findClass如下:
遇到前缀为java.的就去找官方的class文件。
运行测试代码:
还是报错了!!! 报错信息为:Prohibited package name: java.lang
看意思是java禁止用户用自定义的类加载器加载java.开头的官方类,也就是说只有启动类加载器BootstrapClassLoader才能加载java.开头的官方类。
得出结论,因为java中所有类都继承了Object,而加载自定义类study.stefan.classLoader.Demo,之后还会加载其父类,而最顶级的父类Object是java官方的类,只能由BootstrapClassLoader加载
跳过AppClassLoader和ExtClassLoader
既然如此,先将study.stefan.classLoader.Demo交由BootstrapClassLoader加载即可。
由于java中无法直接引用BootstrapClassLoader,所以在初始化TestClassLoader时,传入parent为null,也就是TestClassLoader的父类加载器设置为BootstrapClassLoader:
package com.stefan.DailyTest.classLoader;public class Test {public static void main(String[] args) throws Exception {// 初始化TestClassLoader,并将加载TestClassLoader类的类加载器// 设置为TestClassLoader的parentTestClassLoader testClassLoader = new TestClassLoader(null);System.out.println("TestClassLoader的父类加载器:" + testClassLoader.getParent());// 加载 DemoClass clazz = testClassLoader.loadClass("com.stefan.DailyTest.classLoader.Demo");System.out.println("Demo的类加载器:" + clazz.getClassLoader());}
}
双亲委派的逻辑在 loadClass,由于现在的类加载器的关系为TestClassLoader —>BootstrapClassLoader,所以TestClassLoader中无需重写loadClass。
运行测试代码:
成功了,Demo类由自定义的类加载器TestClassLoader加载的,双亲委派模型被破坏了。
如果不破坏双亲委派,那么Demo类处于classpath下,就应该是AppClassLoader加载的,所以真正破坏的是AppClassLoader这一层的双亲委派
一个比较完整的自定义类加载器
一般情况下,自定义类加载器都是继承URLClassLoader,具有如下类关系图:
tomcat是如何打破双亲委派的
Tomcat中可以部署多个web项目,为了保证每个web项目互相独立,所以不能都由AppClassLoader加载,所以自定义了类加载器WebappClassLoader,WebappClassLoader继承自URLClassLoader,重写了findClass和loadClass,并且WebappClassLoader的父类加载器设置为AppClassLoader。
WebappClassLoader.loadClass中会先在缓存中查看类是否加载过,没有加载,就交给ExtClassLoader,ExtClassLoader再交给BootstrapClassLoader加载;都加载不了,才自己加载;自己也加载不了,就遵循原始的双亲委派,交由AppClassLoader递归加载。
Web应用默认的类加载顺序是(打破了双亲委派规则):
先从JVM的BootStrapClassLoader中加载。
加载Web应用下/WEB-INF/classes中的类。
加载Web应用下/WEB-INF/lib/*.jap中的jar包中的类。
加载上面定义的System路径下面的类。
加载上面定义的Common路径下面的类。
如果在配置文件中配置了``,那么就是遵循双亲委派规则,加载顺序如下:
先从JVM的BootStrapClassLoader中加载。
加载上面定义的System路径下面的类。
加载上面定义的Common路径下面的类。
加载Web应用下/WEB-INF/classes中的类。
加载Web应用下/WEB-INF/lib/*.jap中的jar包中的类。
1 Tomcat对用户类库与类加载器的规划
在其目录结构下有三组目录(“/common/”、“/server/”、“/shared/”)可以存放Java类库,另外还可以加上Web应用程序本身的目录“/WEB-INF/”,一共4组,把Java类库放置在这些目录中的含义分别如下:
放置在/commom目录中:类库可被Tomcat和所有的Web应用程序共同使用
放置在/server目录中:类库可被Tomcat使用,对所有的Web应用程序都不可见
放置在/shared目录中:类库可被所有的Web应用程序所共同使用,但对Tomcat自己不可见
放置在/WebApp/WEB-INF目录中:类库仅仅可以被此Web应用程序使用,对Tomcat和其他Web应用程序都不可见
为了支持这套目录结构,并对目录里面的类库进行加载和隔离,Tomcat自定义了多个类加载器,这些类加载器按照经典的双亲委派模型来实现,所下图:
最上面的三个类加载器是JDK默认提供的类加载器,这三个加载器的的作用之前也说过,这里不再赘述了,而CommonClassLoader、CatalinaClassLoader、SharedClassLoader和WebAppClassLoader则是Tomcat自己定义的类加载器,他们分别加载/common/、/server/、/shared/和/WebApp/WEB-INF/中的Java类库。其中WebApp类加载器和jsp类加载器通常会存在多个实例,每一个Web应用程序对应一个WebApp类加载器,每一个jsp文件对应一个Jsp类加载器
从上图的委派关系可以看出,CommonClassLoader能加载的类都可以被CatalinaClassLoader和SharedClassLoader使用,而CatalinaClassLoader和SharedClassLoader自己能加载的类则与对方相互隔离。WebAppClassLoader可以使用SharedClassLoader加载到的类,但各个WebAppClassLoader实例之间相互隔离。而JasperLoader的加载范围仅仅是这个JSP文件所编译出来的哪一个Class,它出现的目的就是为了被丢弃:当服务器检测到JSP文件被修改时,会替换掉目前的JasperLoader的实例,并通过在建立一个新的Jsp类加载器来实现JSP文件的HotSwap功能
tomcat对于不同应用需要有不同的隔离环境。
tomcat给每个应用都创建了一个WebApp ClassLoader类加载器
重写了load方法:不再向上查找,而是在本类查找不到后再向上。对于其他的需要共享的例如Redis,可以在上层Share ClassLoader中共享。
OSGI是如何打破双亲委派的
既然说到OSGI,就要来解释一下OSGi是什么,以及它的作用
OSGi(Open Service Gateway Initiative):是OSGi联盟指定的一个基于Java语言的动态模块化规范,这个规范最初是由Sun、IBM、爱立信等公司联合发起,目的是使服务提供商通过住宅网管为各种家用智能设备提供各种服务,后来这个规范在Java的其他技术领域也有不错的发展,现在已经成为Java世界中的“事实上”的模块化标准,并且已经有了Equinox、Felix等成熟的实现。OSGi在Java程序员中最著名的应用案例就是Eclipse IDE
OSGi中的每一个模块(称为Bundle)与普通的Java类库区别并不大,两者一般都以JAR格式进行封装,并且内部存储的都是Java Package和Class。但是一个Bundle可以声明它所依赖的Java Package(通过Import-Package描述),也可以声明他允许导出发布的Java Package(通过Export-Package描述)。在OSGi里面,Bundle之间的依赖关系从传统的上层模块依赖底层模块转变为平级模块之间的依赖(至少外观上如此),而且类库的可见性能得到精确的控制,一个模块里只有被Export过的Package才可能由外界访问,其他的Package和Class将会隐藏起来。除了更精确的模块划分和可见性控制外,引入OSGi的另外一个重要理由是,基于OSGi的程序很可能可以实现模块级的热插拔功能,当程序升级更新或调试除错时,可以只停用、重新安装然后启动程序的其中一部分,这对企业级程序开发来说是一个非常有诱惑性的特性
OSGi之所以能有上述“诱人”的特点,要归功于它灵活的类加载器架构。OSGi的Bundle类加载器之间只有规则,没有固定的委派关系。例如,某个Bundle声明了一个它依赖的Package,如果有其他的Bundle声明发布了这个Package,那么所有对这个Package的类加载动作都会为派给发布他的Bundle类加载器去完成。不涉及某个具体的Package时,各个Bundle加载器是平级关系,只有具体使用某个Package和Class的时候,才会根据Package导入导出定义来构造Bundle间的委派和依赖
另外,一个Bundle类加载器为其他Bundle提供服务时,会根据Export-Package列表严格控制访问范围。如果一个类存在于Bundle的类库中但是没有被Export,那么这个Bundle的类加载器能找到这个类,但不会提供给其他Bundle使用,而且OSGi平台也不会把其他Bundle的类加载请求分配给这个Bundle来处理
一个例子:假设存在BundleA、BundleB、BundleC三个模块,并且这三个Bundle定义的依赖关系如下:
BundleA:声明发布了packageA,依赖了java.*的包
BundleB:声明依赖了packageA和packageC,同时也依赖了Java.*的包
BundleC:声明发布了packageC,依赖了packageA
那么,这三个Bundle之间的类加载器及父类加载器之间的关系如下图:
由于没有涉及到具体的OSGi实现,所以上图中的类加载器没有指明具体的加载器实现,只是一个体现了加载器之间关系的概念模型,并且只是体现了OSGi中最简单的加载器委派关系。一般来说,在OSGi中,加载一个类可能发生的查找行为和委派关系会比上图中显示的复杂,类加载时的查找规则如下:
以java.*开头的类,委派给父类加载器加载
否则,委派列表名单内的类,委派给父类加载器加载
否则,Import列表中的类,委派给Export这个类的Bundle的类加载器加载
否则,查找当前Bundle的ClassPath,使用自己的类加载器加载
否则,查找是否在自己的Fragment Bundle中,如果是,则委派给Fragment bundle的类加载器加载
否则,查找Dynamic Import列表的Bundle,委派给对应Bundle的类加载器加载
否则,查找失败
从之前的图可以看出,在OSGi里面,加载器的关系不再是双亲委派模型的树形架构,而是已经进一步发展成了一种更复杂的、运行时才能确定的网状结构
相关面试题:一个类的静态块是否可能被执行两次
一个自于网易面试官的一个问题,一个类的静态块是否可能被执行两次。
答案:如果一个类,被两个 osgi的bundle加载, 然后又有实例被初始化,其静态块会被执行两次
什么是SPI 机制
Spi 机制加载第三方扩展的jar包类初始化。
mysql, dubbo rpc
SPi机制的原理:
java SPI全称Service Provider Interface 。是java 提供的一套用来被第三方实现的API,他可以用来启用框架扩展和替换组件。实际上是基于接口编程+策略模式+配置文件 组合实现的动态加载机制。
JDBC
原本的JDBC: Class.forName(“DriverName”) 是通过调用Driver中静态代码块中的将Driver注册
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!");}}
}
使用SPI的JDBC:
在mysql的jar包中的META-INF/services/java.sql.Driver 文件中指明当前使用的Driver,然后可以直接调用
Connection conn= DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb?characterEncoding=GBK", "root", "");
问题是 :一个类的加载器和调用他的加载器相同
这里调用的是 bootstrap类加载器,无法加载到子类厂商中的类
方法:使用线程上下文加载器
public class DriverManager {static {loadInitialDrivers();println("JDBC DriverManager initialized");}private static void loadInitialDrivers() {//省略代码//这里就是查找各个sql厂商在自己的jar包中通过spi注册的驱动ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);Iterator<Driver> driversIterator = loadedDrivers.iterator();try{while(driversIterator.hasNext()) {driversIterator.next();}} catch(Throwable t) {// Do nothing}//省略代码}
}
使用Thread类的 getContextClassLoader
public static <S> ServiceLoader<S> load(Class<S> service) {ClassLoader cl = Thread.currentThread().getContextClassLoader();return ServiceLoader.load(service, cl);}public static <S> ServiceLoader<S> load(Class<S> service,ClassLoader loader){return new ServiceLoader<>(service, loader);}
整个mysql的驱动加载过程:
第一,获取线程上下文类加载器,从而也就获得了应用程序类加载器(也可能是自定义的类加载器)
第二,从META-INF/services/java.sql.Driver文件中获取具体的实现类名“com.mysql.jdbc.Driver”
第三,通过线程上下文类加载器去加载这个Driver类,从而避开了双亲委派模型的弊端
SPI参考:39 如何破坏双亲委派机制原则 - 简书
知识来源:
JVM问题(一) -- 如何打破双亲委派模型_如何打破双亲委派机制_leo_messi94的博客-CSDN博客
打破双亲委派的几种办法_破坏双亲委派_hhpub的博客-CSDN博客
相关文章:

java八股文面试[JVM]——如何打破双亲委派模型
双亲委派模型的第一次“被破坏”是重写自定义加载器的loadClass(),jdk不推荐。一般都只是重写findClass(),这样可以保持双亲委派机制.而loadClass方法加载规则由自己定义,就可以随心所欲的加载类,典型的打破双亲委派模型的框架和中间件有tomc…...

一加11/Ace2/10Pro手机如何实现全局120HZ高刷-游戏超级流畅效果
已经成功root啦。安卓13目前也一样支持LSPosed框架,如果你对LSP框架有需求,也可以使 自测120HZ刷新率诞生以后,很多小伙伴用上了就很难回来啦,一加11/Ace2/10Pro/9pro手 机厂商也对新机做了很多的适配,让我们日常使用起…...

微服务主流框架概览
微服务主流框架概览 目录概述需求: 设计思路实现思路分析1.HSF2.Dubbo 3.Spring Cloud5.gRPC Service mesh 参考资料和推荐阅读 Survive by day and develop by night. talk for import biz , show your perfect code,full busy,skip hardness,make a be…...

Python Flask Web开发二:数据库创建和使用
前言 数据库在 Web 开发中起着至关重要的作用。它不仅提供了数据的持久化存储和管理功能,还支持数据的关联和连接,保证数据的一致性和安全性。通过合理地设计和使用数据库,开发人员可以构建强大、可靠的 Web 应用程序,满足用户的…...

快速学会git版本管理——上传gitee仓库
首先在gitee右上角有一个新建仓库 创建之后打开自己想要上传的文件 右键打开 Git Bash Here 接下来会弹出git的窗口 首先先初始化仓库 用git命令 git init 然后用git add . 上传所有文件上传到暂存区(上一篇文章说过add是单个文件,add . 是所有文件) 没有显示错误 …...

应用在智能洗衣机触摸屏上的电容式触摸芯片
智能型全自动洗衣机可以自动判断水温、水位、衣质衣量、衣物的脏污情况,决定投放适量的洗涤剂和的洗涤程序。当洗衣桶内衣物的多少和质地不同,而注入水使其达到相同的水位时,其总重量是不同的。利用这一点,通过对洗衣电动机低速转…...

npm版本升级报错
解决方法: 执行npm install --legacy-peer-deps依赖对等 npm install xxx --legacy-peer-deps命令用于绕过peerDependency里依赖的自动安装;它告诉npm忽略项目中引入的各个依赖模块之间依赖相同但版本不同的问题,以npm v4-v6的方式去继续执行…...

Vue+Element-ui+SpringBoot搭建后端汽车租赁管理系统
最近在做项目,花了一周的时间搭建了一个十分完备的汽车租赁后端管理系统。页面采用纯Vue2Element-ui搭建,后端采用SpringbootMybatis搭建,数据库采用Mysql。包括了登录验证,根据不同权限进入不同界面、数据增删改查、表格分页、表…...
PKU校园网连接失败
校园网连接失败 连上校园网,显示已经连接但是没有网络,手动输入校园网门户( its.pku.edu.cn )也没有用。 使用 windows自带的疑难解答,分析发现dns解析异常。 解决方案 手动配置IPV4的dns。 同学的电脑可以正常连接dns,将同学…...

STM32存储左右互搏 I2C总线读写FRAM MB85RC16
STM32存储左右互搏 I2C总线读写FRAM MB85RC16 在较低容量存储领域,除了EEPROM的使用,还有铁电存储器FRAM的使用,相对于EEPROM, 同样是非易失性存储单元,FRAM支持更高的访问速度, 其主要优点为没有EEPROM持续写操作跨页…...
【typeof instanceof Object.prototype.toString constructor区别】
几个数据类型判断区别 typeofinstanceofObject.prototype.toStringconstructor typeof 它返回的是一个字符串,表示未经过计算的操作数的类型 typeof(undefined) //"undefined"typeof(null) //"object"typeof(100) //"number"typeof…...
ARM Codec要求
文章目录 前言一、驱动1. linux kernel driver (非V4L2驱动)1.1 porting guide1.2 programing guide1.3 CPU占用率统计1.4 memory使用统计(不包含input/output/working buffer) 2. freeRTOS driver2.1 porting guide,驱动所支持freeRTOS版本列表2.2 programing guid…...

QT多线程
1.QT4.7以前的版本-----线程处理方式 1. 出现的警告 直接使用从UI—>转到槽,就会出现警告 2. 出现的错误 error: invalid operands of types QTimer* and void (QTimer::*)(QTimer::QPrivateSignal) to binary operator& 错误:无效的操作数类型’QTimer…...
【linux命令讲解大全】059.命令行利器:快速执行指定命令的command命令
文章目录 command补充说明语法参数实例 从零学 python command 调用并执行指定的命令。 补充说明 command 命令用于调用指定的命令并执行,命令执行时不查询 shell 函数。command 命令只能执行 shell 内部的命令。 语法 command [参数]参数 指令:需…...

opencv-4.5.2-android-sdk.zip安装教程
opencv-4.5.2-android-sdk.zip: 下载链接:百度网盘 请输入提取码 提取码:s0p2 导入模块的方法: ①、导入模块 ②、定位到sdk目录 点击ok就行,就导入成功了。导入成功后会多出一个可展开的opencv文件夹(自己命名的),一定要能展…...

接口自动化测试系列-excel管理测试用例
代码源码: 框架结构 核心代码 excel数据处理 from configureUtil.LogUtil import getlog logger getlog(targetNameHandleData) import xlrd from openpyxl import load_workbook,workbook from openpyxl.styles import Font, colors import openpyxl import o…...

Spring——Spring的控制反转IOC
摘要 IoC 不是一种技术,只是一种思想,一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合、更优良的程序。传统应用程序都是由我们在类内部主动创建依赖对象,从而导致类与类之间高耦合,难于测试;…...
基于CentOS7.5构建LVS-DR 群集,并启用Nginx负载均衡,一键完成。
在两台服务器上的步骤: 安装必要软件:在两台服务器上,安装必要的软件,包括ipvsadm和keepalived。使用以下命令安装软件: sudo yum install ipvsadm keepalived -y 禁用防火墙或配置规则:禁用防火墙或根据实…...

redis 数据结构(二)
整数集合 整数集合是 Set 对象的底层实现之一。当一个 Set 对象只包含整数值元素,并且元素数量不时,就会使用整数集这个数据结构作为底层实现。 整数集合结构设计 整数集合本质上是一块连续内存空间,它的结构定义如下: typed…...

Hadoop依赖环境配置与安装部署
目录 什么是Hadoop?一、Hadoop依赖环境配置1.1 设置静态IP地址1.2 重启网络1.3 再克隆两台服务器1.4 修改主机名1.5 安装JDK1.6 配置环境变量1.7 关闭防火墙1.8 服务器之间互传资料1.9 做一个host印射1.10 免密传输 二、Hadoop安装部署2.1 解压hadoop的tar包2.2 切换…...

iOS 26 携众系统重磅更新,但“苹果智能”仍与国行无缘
美国西海岸的夏天,再次被苹果点燃。一年一度的全球开发者大会 WWDC25 如期而至,这不仅是开发者的盛宴,更是全球数亿苹果用户翘首以盼的科技春晚。今年,苹果依旧为我们带来了全家桶式的系统更新,包括 iOS 26、iPadOS 26…...
将对透视变换后的图像使用Otsu进行阈值化,来分离黑色和白色像素。这句话中的Otsu是什么意思?
Otsu 是一种自动阈值化方法,用于将图像分割为前景和背景。它通过最小化图像的类内方差或等价地最大化类间方差来选择最佳阈值。这种方法特别适用于图像的二值化处理,能够自动确定一个阈值,将图像中的像素分为黑色和白色两类。 Otsu 方法的原…...
OpenLayers 分屏对比(地图联动)
注:当前使用的是 ol 5.3.0 版本,天地图使用的key请到天地图官网申请,并替换为自己的key 地图分屏对比在WebGIS开发中是很常见的功能,和卷帘图层不一样的是,分屏对比是在各个地图中添加相同或者不同的图层进行对比查看。…...
大语言模型(LLM)中的KV缓存压缩与动态稀疏注意力机制设计
随着大语言模型(LLM)参数规模的增长,推理阶段的内存占用和计算复杂度成为核心挑战。传统注意力机制的计算复杂度随序列长度呈二次方增长,而KV缓存的内存消耗可能高达数十GB(例如Llama2-7B处理100K token时需50GB内存&a…...

推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材)
推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材) 这个项目能干嘛? 使用 gemini 2.0 的 api 和 google 其他的 api 来做衍生处理 简化和优化了文生图和图生图的行为(我的最主要) 并且有一些目标检测和切割(我用不到) 视频和 imagefx 因为没 a…...

人机融合智能 | “人智交互”跨学科新领域
本文系统地提出基于“以人为中心AI(HCAI)”理念的人-人工智能交互(人智交互)这一跨学科新领域及框架,定义人智交互领域的理念、基本理论和关键问题、方法、开发流程和参与团队等,阐述提出人智交互新领域的意义。然后,提出人智交互研究的三种新范式取向以及它们的意义。最后,总结…...

Kafka入门-生产者
生产者 生产者发送流程: 延迟时间为0ms时,也就意味着每当有数据就会直接发送 异步发送API 异步发送和同步发送的不同在于:异步发送不需要等待结果,同步发送必须等待结果才能进行下一步发送。 普通异步发送 首先导入所需的k…...

AI+无人机如何守护濒危物种?YOLOv8实现95%精准识别
【导读】 野生动物监测在理解和保护生态系统中发挥着至关重要的作用。然而,传统的野生动物观察方法往往耗时耗力、成本高昂且范围有限。无人机的出现为野生动物监测提供了有前景的替代方案,能够实现大范围覆盖并远程采集数据。尽管具备这些优势…...
提升移动端网页调试效率:WebDebugX 与常见工具组合实践
在日常移动端开发中,网页调试始终是一个高频但又极具挑战的环节。尤其在面对 iOS 与 Android 的混合技术栈、各种设备差异化行为时,开发者迫切需要一套高效、可靠且跨平台的调试方案。过去,我们或多或少使用过 Chrome DevTools、Remote Debug…...
写一个shell脚本,把局域网内,把能ping通的IP和不能ping通的IP分类,并保存到两个文本文件里
写一个shell脚本,把局域网内,把能ping通的IP和不能ping通的IP分类,并保存到两个文本文件里 脚本1 #!/bin/bash #定义变量 ip10.1.1 #循环去ping主机的IP for ((i1;i<10;i)) doping -c1 $ip.$i &>/dev/null[ $? -eq 0 ] &&am…...