JVM字节码与类的加载——类的加载过程详解

文章目录

  • 1、概述
  • 2、加载(Loading)阶段
    • 2.1、加载完成的操作
    • 2.2、二进制流的获取方式
    • 2.3、类模型与Class实例的位置
    • 2.4、数组类的加载
  • 3、链接(Linking)阶段
    • 3.1、链接阶段之验证(Verification)
      • 3.1.1、格式检查
      • 3.1.2、字节码的语义检查
      • 3.1.3、字节码验证
      • 3.1.4、符号引用验证
    • 3.2、链接阶段之准备(Preparation)
    • 3.3、链接阶段之解析(Resolution)
  • 4、初始化(Initialization)阶段
    • 4.1、static与final搭配
    • 4.2、<clinit>()方法的线程安全性
    • 4.3、类的初始化时机:主动使用和被动使用
      • 4.3.1、主动使用
      • 4.3.2、被动使用
  • 5、类的使用(Using)
  • 6、类的卸载(Unloading)
    • 6.1、类、类的加载器、类的Class对象、类的实例之间的引用关系
    • 6.2、类的生命周期
    • 6.3、案例
    • 6.4、类的卸载
    • 6.5、回顾:方法区的垃圾回收
  • 7、小结

我们知道class文件是存放在磁盘上的,如果想要在JVM中使用class文件,需要将其加载至内存当中。了解了class文件的结构,本帖将详细介绍class文件加载到内存中的过程。

1、概述

在Java中数据类型分为基本数据类型和引用数据类型。基本数据类型由JVM预先定义,可以直接被用户使用,引用数据类型则需要执行类的加载才可以被用户使用。Java虚拟机规范中规定,class文件加载到内存,再到类卸载出内存会经历7个阶段,分别是加载、验证、准备、解析、初始化、使用和卸载,其中,验证、准备和解析3个阶段统称为链接(Linking),整个过程称为类的生命周期,如下图所示:
在这里插入图片描述

2、加载(Loading)阶段

2.1、加载完成的操作

所谓加载,简而言之就是将Java类的class文件加载到机器内存中,并在内存中构建出Java类的原型,也就是类模板对象。所谓类模板对象,其实就是Java类在JVM内存中的一个快照,JVM将从class文件中解析出的常量池、类字段、类方法等信息存储到类模板对象中。JVM在运行期可以通过类模板对象获取Java类中的任意信息,能够访问Java类中的成员变量,也能调用Java方法,反射机制便是基于这一基础,如果JVM没有将Java类的声明信息存储起来,则JVM在运行期也无法使用反射。在加载类时,JVM必须完成以下3件事情:

  • (1)通过类的全名,获取类的二进制数据流。
  • (2)解析类的二进制数据流为方法区内的数据结构(Java类模型)。
  • (3)创建java.lang.Class类的实例,作为方法区中访问类数据的入口。

2.2、二进制流的获取方式

JVM可以通过多种途径产生或获得类的二进制数据流,下面列举了常见的几种方式:

  • (1)通过文件系统读入一个后缀为.class的文件(最常见)。
  • (2)读入jar、zip等归档数据包,提取类文件。
  • (3)事先存放在数据库中的类的二进制数据。
  • (4)使用类似于HTTP之类的协议通过网络加载。
  • (5)在运行时生成一段Class的二进制信息。

在获取到类的二进制信息后,JVM就会处理这些数据,并最终转为一个java.lang.Class的实例。如果输入数据不是JVM规范的class文件的结构,则会抛出“ClassFormatError”异常。

2.3、类模型与Class实例的位置

1、类模型的位置
加载的类在JVM中创建相应的类结构,类结构会存储在方法区中。
2、Class实例的位置
类加载器将class文件加载至方法区后,会在堆中创建一个Java.lang.Class对象,用来封装类位于方法区内的数据结构,该Class对象是在加载类的过程中创建的,每个类都对应有一个Class类型的对象。类模型和Class实例的位置对应关系如下图所示:
在这里插入图片描述
外部可以通过访问代表Order类的Class对象来获取Order类的数据结构。java.lang.Class类的构造方法是私有的,只有JVM能够创建。java.lang.Class实例是访问类型元数据的入口,也是实现反射的关键数据。通过Class类提供的接口,可以获得目标类所关联的class文件中具体的数据结构、方法、字段等信息。如代码清单如下所示,展示了如何通过java.lang.Class类获取方法信息。
在这里插入图片描述
通过上面的代码可以直接获取到String类的方法信息,运行结果如下,由于String类方法太多,只展示部分方法。
在这里插入图片描述

2.4、数组类的加载

创建数组类的情况稍微有些特殊,数组类由JVM在运行时根据需要直接创建,所以数组类没有对应的class文件,也就没有二进制形式,所以也就无法使用类加载器去创建数组类。但数组的元素类型仍然需要依靠类加载器去创建。创建数组类的过程如下:

  • (1)如果数组的元素类型是引用类型,那么就遵循定义的加载过程递归加载和创建数组的元素类型,JVM使用指定的元素类型和数组维度来创建新的数组类。
  • (2)如果数组的元素是基本数据类型,比如int类型的数组,由于基本数据类型是由JVM预先定义的,所以也不需要类加载,只需要关注数组维度即可。

如果数组的元素类型是引用类型,数组类的可访问性就由元素类型的可访问性决定。否则数组类的可访问性将被缺省定义为public。

3、链接(Linking)阶段

3.1、链接阶段之验证(Verification)

类加载到机器内存后,就开始链接操作,验证是链接操作的第一步。验证的目的是保证加载的字节码是合法、合理并符合规范的。验证的步骤比较复杂,实际要验证的项目也很繁多,如下图所示:
在这里插入图片描述
验证的内容涵盖了类数据信息的格式检查、语义检查、字节码验证、符号引用验证,其中格式检查会和加载阶段一起执行。验证通过之后,类加载器才会成功将类的二进制数据信息加载到方法区中。格式检查之外的验证操作将会在方法区中进行。如果不在链接阶段进行验证,那么class文件运行时依旧需要进行各种检查,虽然链接阶段的验证拖慢了加载速度,但是却提高了程序执行的速度,正所谓“磨刀不误砍柴工”。

3.1.1、格式检查

主要检查是否以魔数OxCAFEBABE开头,主版本和副版本号是否在当前JVM的支持范围内,数据中每一个项是否都拥有正确的长度等。

3.1.2、字节码的语义检查

JVM会进行字节码的语义检查,但凡在语义上不符合规范的,JVM也不会验证通过,比如JVM会检查下面4项语义是否符合规范:

  • (1)是否所有的类都有父类的存在(Object除外)。
  • (2)是否一些被定义为final的方法或者类被重写或继承了。
  • (3)非抽象类是否实现了所有抽象方法或者接口方法。
  • (4)是否存在不兼容的方法,比如方法的签名除了返回值不同,其他都一样。

3.1.3、字节码验证

JVM还会进行字节码验证,字节码验证也是验证过程中最为复杂的一个过程。它试图通过对字节码流的分析,判断字节码是否可以被正确地执行,比如JVM会验证字节码中的以下内容。

  • (1)在字节码的执行过程中,是否会跳转到一条不存在的指令。
  • (2)函数的调用是否传递了正确类型的参数。
  • (3)变量的赋值是不是给了正确的数据类型等。
  • (4)检查栈映射帧的局部变量表和操作数栈是否有着正确的数据类型。

遗憾的是,百分之百准确地判断一段字节码是否可以被安全执行是无法实现的,因此,该过程只是尽可能地检查出可以预知的明显的问题。如果在这个阶段无法通过检查,JVM也不会正确装载这个类。但是,如果通过了这个阶段的检查,也不能说明这个类是完全没有问题的。在前面3次检查中,已经排除了文件格式错误、语义错误以及字节码的不正确性。但是依然不能确保类是没有问题的。

3.1.4、符号引用验证

class文件中的常量池会通过字符串记录将要使用的其他类或者方法。因此,在验证阶段,JVM就会检查这些类或者方法是否存在,检查当前类是否有权限访问这些数据,如果一个需要使用的类无法在系统中找到,则会抛出“NoClassDefFoundError”错误,如果一个方法无法被找到,则会抛出“NoSuchMethodError”错误。注意,这个过程发生在链接阶段的解析环节。

3.2、链接阶段之准备(Preparation)

当一个类验证通过时,JVM就会进入准备阶段。准备阶段主要负责为类的静态变量分配内存,并将其初始化为默认值。JVM为各类型变量默认的初始值如下表所示:
在这里插入图片描述
Java并不直接支持boolean类型,对于boolean类型,内部实现是int,int的默认值是0,对应的boolean类型的默认值是false。

注意,这个阶段不会为使用static final修饰的基本数据类型初始化为0,因为final在编译的时候就会分配了,准备阶段会显式赋值。也不会为实例变量分配初始化,因为实例变量会随着对象一起分配到Java堆中。这个阶段并不会像初始化阶段那样会有初始化或者代码被执行。代码清单如下展示了static final修饰的基本数据类型不会被初始化为0。
在这里插入图片描述
查看该类的字节码字段属性,如下图所示:
在这里插入图片描述
如果类字段的字段属性表中存在ConstantValue属性,那么在准备阶段该类字段value就会被显式赋值,也就是说在准备阶段,num的值是1,而不是0。仅被static修饰的类变量,在准备阶段初始化为默认值。

3.3、链接阶段之解析(Resolution)

在准备阶段完成后,类加载进入解析阶段。解析阶段主要负责将类、接口、字段和方法的符号引用转为直接引用。

符号引用就是一些字面量的引用,和JVM的内部数据结构及内存布局无关。比如class文件中,常量池存储了大量的符号引用。在程序实际运行时,只有符号引用是不够的,比如当println()方法被调用时,系统需要明确知道该方法的位置。

以方法为例,JVM为每个类都准备了一张方法表,将其所有的方法都列在表中,当需要调用一个类的方法的时候,只要知道这个方法在方法表中的偏移量就可以直接调用该方法。通过解析操作,符号引用就可以转变为目标方法在类中方法表中的位置,从而使得方法被成功调用。代码清单如下演示了方法在解析阶段的调用过程。
在这里插入图片描述
其对应的字节码如下:

     0 getstatic #2 <java/lang/System.out>3 ldc #3 <atguigu>5 invokevirtual #4 <java/io/PrintStream.println>8 return

invokevirtual #4 <java/io/PrintStream.println>方法的符号引用指向常量池中第四个选项,如下图所示:
在这里插入图片描述
方法调用的常量是类中方法的符号引用,包含类名和方法以及方法参数,解析阶段就是获取这些属性在内存中的地址,具体过程如下图所示:
在这里插入图片描述

通过第4项常量找到第21项类名常量和第22项方法的名称描述符即可。

4、初始化(Initialization)阶段

类的初始化是类装载的最后一个阶段。如果前面的步骤都没有问题,那么表示类可以顺利装载到系统中,然后JVM才会开始执行Java字节码,也就是说到了初始化阶段,JVM才真正开始执行类中定义的Java程序代码。初始化阶段的重要工作是执行类的()方法(即类初始化方法),该方法仅能由Java编译器生成并被JVM调用,程序开发者无法自定义一个同名的方法,也无法直接在Java程序中调用该方法。()方法是由类静态成员的赋值语句以及static语句块合并产生的。通常在加载一个类之前,JVM总是会试图加载该类的父类,因此父类的()方法总是在子类()方法之前被调用,也就是说,父类的static语句块优先级高于子类,简要概括为由父及子,静态先行。

Java编译器并不会为所有的类都产生()方法。以下情况class文件中将不会包含()方法:

  • (1)一个类中并没有声明任何的类变量,也没有静态代码块时。
  • (2)一个类中声明类变量,但是没有明确使用类变量的初始化语句以及静态代码块来执行初始化操作时。
  • (3)一个类中包含static final修饰的基本数据类型的字段,这些类字段初始化语句采用编译时常量表达式。

代码清单如下展示了哪些情况不会产生()方法:
在这里插入图片描述
查看该类对应的方法信息,如下图所示,可以看到不存在()方法。
在这里插入图片描述

4.1、static与final搭配

static与final定义的变量在准备阶段完成赋值,但是并不是所有的变量都在链接阶段的准备阶段完成赋值,下面通过代码案例说明不同情况下的不同阶段赋值,如代码清单如下所示:
在这里插入图片描述
对应字节码指令在()方法中:
在这里插入图片描述
从字节码指令中看到只有定义类成员变量a、INTEGER_CONSTANT1、INTEGER_CONSTANT2和s1时是在初始化阶段的()方法中完成。那么另外两个类变量是怎么赋值的呢?通过jclasslib查看字段属性表,如下图所示,可以看到只有INT_CONSTANT和helloworld0两个常量拥有ConstantValue,说明INT_CONSTANT = 10和String s0="helloworld0"是在链接阶段的准备阶段完成的。
在这里插入图片描述
我们得出的结论就是,基本数据类型和String类型使用static和final修饰,并且显式赋值中不涉及方法或构造器调用,其初始化是在链接阶段的准备环节进行,其他情况都是在初始化阶段进行赋值。

4.2、()方法的线程安全性

对于()方法的调用,JVM会在内部确保其多线程环境中的安全性。JVM会保证一个类的()方法在多线程环境中被正确地加锁、同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的()方法,其他线程都需要阻塞等待,直到活动线程执行()方法完毕。正是因为方法()带锁线程安全的,如果在一个类的()方法中有耗时很长的操作,就可能造成多个线程阻塞,导致死锁,这种死锁是很难发现的,因为并没有可用的锁信息。如果之前的线程成功加载了类,则等在队列中的线程就没有机会再执行()方法了,当需要使用这个类时,JVM会直接返回给它已经准备好的信息。

4.3、类的初始化时机:主动使用和被动使用

初始化阶段是执行类构造器()方法的过程。虽然有些类已经存在()方法,但是并不确定什么时候会触发执行,可以触发()方法的情景称为主动使用,不能触发()方法执行的情景称为被动使用。主动使用可以触发类的初始化,被动使用不能触发类的初始化。

4.3.1、主动使用

JVM不会无条件地装载class文件,class文件只有在首次使用的时候才会被装载。JVM规定,一个类或接口在初次使用前,必须要进行初始化。这里的“使用”是指主动使用,主动使用包含下列几种情况。

(1)创建一个类的实例时,比如使用new关键字、反射、克隆或反序列化等方式创建实例。首先创建Order类,Order类中写了一段静态代码块,如下代码清单所示:
在这里插入图片描述
一个类被初始化的标志就是执行()方法,查看Order类的()方法,如下图所示,说明只要执行了静态代码块就表示执行了()方法,即Order类被初始化。
在这里插入图片描述
代码清单如下演示了new[test()方法]关键字创建实例、反序列化[test2()方法],以及反射[test3()方法]都会调用类的初始化,代码中如果输出了Order类中对应的输出语句即表示执行了类的初始化。注意,案例中序列化[test1()方法]的作用仅仅是将对象序列化为order.dat文件,为反序列化[test2()方法]做铺垫,虽然序列化[test1()方法]也输出了Order类中的语句,这是因为new关键字调用了类的初始化,而不是序列化调用了类的初始化。
在这里插入图片描述
test()、test2()、test3()方法的执行结果如下,表明使用new关键字、反序列化、反射等方式都会执行类的初始化。

     Order类的初始化过程

(2)调用类的静态方法时,即当使用了字节码invokestatic指令时。

Order类中添加静态方法,如下所示:

     public static void method(){System.out.println("Order method()....");}

ActiveUse1类中添加test4()方法用于调用类的静态方法,如下所示:
在这里插入图片描述
test4()方法执行结果如下,可以发现调用类的静态方法的时候也执行了类的初始化:

     Order类的初始化过程Order method()....

(3)使用类、接口的静态字段时(final修饰特殊考虑),字节码指令中使用了getstatic或者putstatic指令。

在Order类中添加以下属性:

     public static int num1 = 1;public static final int num2 = 2;public static final int num3 = new Random().nextInt(10);

创建ActiveUse2类用于测试使用类的静态字段是否会触发类的初始化,如代码清单如下所示:
在这里插入图片描述
test1()方法的执行结果如下所示:

     Order类的初始化过程test1()方法执行结果:1

test2()方法的执行结果如下所示:

     test2()方法执行结果:2

test3()方法的执行结果如下所示:

     Order类的初始化过程test3()方法执行结果:8

从结果来看,当字段使用static修饰且没有使用final字段修饰时,如果使用该字段会触发类的初始化;当static和final同时修饰字段时,且该字段是一个固定值则不会触发类的初始化,因为该类型的字段在链接过程的准备阶段就已经被初始化赋值了,不需要类初始化以后才能使用,所以不会执行类的初始化;num3是因为在程序执行之前无法确定具体的数值,所以需要执行类的初始化以后才能继续执行。

上面讲述了类的静态字段是否触发类的初始化,接下来再测试接口的静态字段是否会触发接口的初始化,创建CompareA接口如代码清单所示:
在这里插入图片描述
查看CompareA中()方法,如下图所示,可以看到如果创建了线程对象t并且里面的代码块语句输出则表示执行了()方法。
在这里插入图片描述
在ActiveUse2类中添加test4()和test5()方法用于测试接口的静态属性是否会触发类的初始化:
在这里插入图片描述
test4()方法执行结果如下:

     test4()方法执行结果:1

test5()方法执行结果如下:

     CompareA的初始化test5()方法执行结果:3

可以看到接口的静态字段和类的静态字段对类的初始化效果是一样的,需要注意的是接口的字段默认是由static final修饰的,接口中没有字段是被static单独修饰的。

(4)初始化子类时,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化。JVM虚拟机初始化一个类时,要求它的所有父类都已经被初始化,但是这条规则并不适用于接口。在初始化一个类时,并不会先初始化它所实现的接口;在初始化一个接口时,并不会先初始化它的父接口。因此,一个父接口并不会因为它的子接口或者实现类的初始化而初始化。只有当程序首次使用特定接口的静态字段时,才会导致该接口的初始化。

4.3.2、被动使用

除了以上的情况属于主动使用,其他的情况均属于被动使用。被动使用不会引起类的初始化。也就是说并不是在代码中出现的类,就一定会被加载或者初始化。如果不符合主动使用的条件,类就不会初始化。被动使用包含如以下几种情况:

  • (1)当访问一个静态字段时,只有真正声明这个字段的类才会被初始化。当通过子类引用父类的静态变量,不会导致子类初始化。
  • (2)通过数组定义类引用,不会触发此类的初始化。
  • (3)引用常量不会触发此类或接口的初始化,因为常量在链接阶段已经被显式赋值,主动使用第3条规则我们已经讲过了,不再赘述。
  • (4)调用ClassLoader类的loadClass()方法加载一个类,并不是对类的主动使用,不会导致类的初始化。

5、类的使用(Using)

任何一个类在使用之前都必须经历过完整的加载、链接和初始化3个步骤。一旦一个类成功经历这3个步骤之后,便“万事俱备,只欠东风”,就等着开发者使用了。开发人员可以在程序中访问和调用它的静态类成员信息(比如静态字段、静态方法等),或者使用new关键字创建对象实例。

6、类的卸载(Unloading)

和前面讲过对象的生命周期类似,对象在使用完以后会被垃圾收集器回收,那么对应的类在使用完成以后,也有可能被卸载掉。在了解类的卸载之前,需要先厘清类、类的加载器、类的Class对象和类的实例之间的引用关系。

6.1、类、类的加载器、类的Class对象、类的实例之间的引用关系

(1)类加载器和类的Class对象之间的关系:在类加载器的内部实现中,用一个Java集合来存放所加载类的引用。另外,一个Class对象总是会引用它的类加载器,调用Class对象的getClassLoader()方法,就能获得它的类加载器。由此可见,代表某个类的Class对象与该类的类加载器之间为双向关联关系。

(2)类、类的Class对象、类的实例对象之间的关系:一个类的实例总是引用代表这个类的Class对象。Object类中定义了getClass()方法,这个方法返回代表实例所属类的Class对象的引用。此外,所有的Java类都有一个静态属性class,它引用代表这个类的Class对象。

6.2、类的生命周期

当类被加载、链接和初始化后,它的生命周期就开始了。当代表类的Class对象不再被引用,即不可触及时,Class对象就会结束生命周期,类在方法区内的数据也会被卸载,从而结束类的生命周期。一个类何时结束生命周期,取决于代表它的Class对象何时结束生命周期。

6.3、案例

自定义一个类加载器MyClassLoader加载自定义类Order,那么就可以通过Order的Class对象获取到对应的类加载器,再通过Order类的实例对象获取到类Class对象,如下代码清单所示:
在这里插入图片描述
类、类的加载器、类的Class对象、类的实例之间的引用关系如下图所示:
在这里插入图片描述
myLoader变量和order变量间接引用代表Order类的Class对象,而orderClass变量则直接引用代表Order类的Class对象。如果程序运行过程中,将上图左侧三个引用变量都置为null,此时Order对象结束生命周期,myLoader对象结束生命周期,代表Order类的Class对象也结束生命周期,Order类在方法区内的二进制数据被卸载。当再次有需要时,会检查Order类的Class对象是否存在,如果存在会直接使用,不再重新加载;如果不存在Order类会被重新加载,在JVM的堆区会生成一个新的代表Order类的Class实例。

6.4、类的卸载

通过上面的案例可以知道当类对象没有引用时,可能会产生类的卸载,类的卸载需要满足如下三个条件:

  • (1)该类所有的实例已经被回收。
  • (2)加载该类的类加载器的实例已经被回收。
  • (3)该类对应的Class对象没有任何对方被引用。

但是需要注意,并不是所有类加载器下面的类都可以被卸载,Java自带的三种类加载器的实例是不可以被卸载的,所以它们加载的类在整个运行期间是不可以被卸载的,只有被开发者自定义的类加载器实例加载的类才有可能被卸载。一个已经加载的类被卸载的概率很小,至少被卸载的时间是不确定的。开发者在开发代码的时候,不应该对虚拟机的类卸载做任何假设,在此前提下,再来实现系统中的特定功能。

6.5、回顾:方法区的垃圾回收

方法区的垃圾收集主要回收两部分内容,分别是常量池中废弃的常量和不再使用的类。HotSpot虚拟机对常量池的回收策略是很明确的,只要常量池中的常量没有被任何地方引用,就可以被回收。

JVM判定一个常量是否“废弃”还相对简单,而要判定一个类是否属于“不再被使用的类”的条件就比较苛刻了,需要同时满足下面三个条件:

  • (1)该类所有的实例都已经被回收。也就是Java堆中不存在该类及其任何派生子类的实例。
  • (2)加载该类的类加载器已经被回收。这个条件除非是经过精心设计的可替换类加载器的场景,如OSGi、JSP的重加载等,否则通常是很难达成的。
  • (3)该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。

上述三个条件并不是JVM卸载无用类的必要条件,JVM可以卸载类也可以不卸载类,不会像对象那样没有引用就肯定回收。

7、小结

主要介绍了JVM将class文件加载到内存所经历的过程,这个过程可分为加载、链接和初始化三大步骤。加载阶段主要负责根据类的二进制数据创建类模板对象。链接阶段主要负责获取类或接口并将其组合到JVM的运行时状态,链接又分为验证、准备和解析三个阶段。初始化主要负责为静态字段赋值,以及执行()方法,注意类的初始化仅会被执行一次。学习类的加载过程可以帮助我们更加透彻地理解class文件的执行过程。

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

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

相关文章

Harmony鸿蒙南向驱动开发-I3C

I3C&#xff08;Improved Inter Integrated Circuit&#xff09;总线是由MIPI Alliance开发的一种简单、低成本的双向二线制同步串行总线。 I3C是两线双向串行总线&#xff0c;针对多个传感器从设备进行了优化&#xff0c;并且一次只能由一个I3C主设备控制。相比于I2C&#xf…

langchain LCEL,prompt模块,outputparse输出模块

目录 基本代码 prompt模块 prompt模版控制长度 outputparse格式化输出 LangChain表达式语言&#xff0c;或者LCEL&#xff0c;是一种声明式的方式&#xff0c;可以轻松地将链条组合在一起 langchian 可以使用 通义千问&#xff0c;我们用通义千问&#xff0c;用法也要申请…

基于ros的相机内参标定过程

基于ros的相机内参标定过程 1. 安装还对应相机的驱动2. 启动相机节点发布主题3. 下载camera_calibartion4. 将红框的文件夹复制在自己的工作空间里边&#xff0c;编译5. 标定完成以后&#xff0c;生成内参参数文件camera.yaml。将文件放在对应的路径下&#xff0c;修改config文…

ArcGIS Server 10发布要素服务时遇到的数据库注册问题总结(一)

工作环境&#xff1a; Windows 7 64 位旗舰版 ArcGIS Server 10.1 ArcGIS Desktop 10.1 IIS 7.0 开始的时候以为10.1发布要素服务和10.0一样&#xff0c;需要安装ArcSDE&#xff0c;后来查阅资料发现不需要&#xff0c;数据库直连方式就可以了。 首先我来说一下发布要素服…

stm32开发之threadx+netxduo(tcp 服务端使用记录)

前言 本篇需要用到threadx之动态内存的实现记录 里面的动态内存分配管理代码.开发环境使用的stm32cubemxclion组合芯片使用的是stm32f407zgt6,网口使用的是lan8720&#xff0c;使用cubemx提供的lan8742也可以驱动&#xff0c;注意实际的网口与芯片的引脚 示例代码 tcp 服务端…

Excel文本内容抽取工具[Python]

#创作灵感# 一堆Excel文件&#xff0c;每个打开看太累了。写个脚本直接显示里面的内容多好。最好这些内容可以直接复制到剪切板&#xff0c;方便以后编辑修改。只需要将文件拖动到全屏置顶的文本框内&#xff0c;就能弹出Excel里的内容。支持一次选取多个文件。 开干&#xff…

计算机视觉——引导APSF和梯度自适应卷积增强夜间雾霾图像的可见性算法与模型部署(C++/python)

摘要 在夜间雾霾场景中&#xff0c;可见性经常受到低光照、强烈光晕、光散射以及多色光源等多种因素的影响而降低。现有的夜间除雾方法常常难以处理光晕或低光照条件&#xff0c;导致视觉效果过暗或光晕效应无法被有效抑制。本文通过抑制光晕和增强低光区域来提升单张夜间雾霾…

N1922A是德科技N1922A功率传感器

181/2461/8938产品概述&#xff1a; N192XA 传感器是首款通过将直流参考源和开关电路集成到功率传感器中来提供内部调零和校准的传感器。此功能消除了与使用外部校准源相关的多个连接&#xff0c;从而最大限度地减少了连接器磨损、测试时间和测量不确定性。 连接到 DUT 时进行…

InsectMamba:基于状态空间模型的害虫分类

InsectMamba&#xff1a;基于状态空间模型的害虫分类 摘要IntroductionRelated WorkImage ClassificationInsect Pest Classification PreliminariesInsectMambaOverall Architecture InsectMamba: Insect Pest Classification with State Space Model 摘要 害虫分类是农业技术…

Excel从零基础到高手【办公】

第1课 - 快速制作目录【上篇】第1课 - 快速制作目录【下篇】第2课 - 快速定位到工作表的天涯海角第3课 - 如何最大化显示工作表的界面第4课 - 给你的表格做个瘦身第5课 - 快速定位目标区域所在位置第6课 - 快速批量填充序号第7课 - 按自定义的序列排序第8课 - 快速删除空白行第…

C++数据结构与算法——贪心算法难题

C第二阶段——数据结构和算法&#xff0c;之前学过一点点数据结构&#xff0c;当时是基于Python来学习的&#xff0c;现在基于C查漏补缺&#xff0c;尤其是树的部分。这一部分计划一个月&#xff0c;主要利用代码随想录来学习&#xff0c;刷题使用力扣网站&#xff0c;不定时更…

计算机视觉异常检测——PatchCore面向全召回率的工业异常检测

1. 概述 异常检测问题在工业图像数据分析中扮演着至关重要的角色&#xff0c;其目的是从大量正常数据中识别出异常行为或模式。这一任务的挑战在于&#xff0c;正常数据的样本相对容易获取&#xff0c;而异常情况却因其稀有性和多样性而难以收集。为了解决这一问题&#xff0c…

跟TED演讲学英文:Why AI will spark exponential economic growth by Cathie Wood

TED英文文稿 文章目录 TED英文文稿Why AI will spark exponential economic growthIntroductionVocabularyTranscriptSummary Why AI will spark exponential economic growth Link: https://www.ted.com/talks/cathie_wood_why_ai_will_spark_exponential_economic_growth? …

家庭网络防御系统搭建-将NDR系统的zeek日志集成到securit yonion

在前面的文章中安装了zeek,这里&#xff0c;安装了securityonion&#xff0c;这里&#xff0c;本文讲述如何将zeek生成的日志发送到siem security onion之中。 所有日志集成的步骤分为如下几步&#xff1a; 日志收集配置日志发送接收日志解析配置日志展示配置 ZEEK日志收集配…

大型语言模型如何助力推荐系统:综述研究

论文地址&#xff1a;https://arxiv.org/pdf/2306.05817.pdf 这篇论文主要探讨了推荐系统&#xff08;RS&#xff09;如何从大型语言模型&#xff08;LLM&#xff09;中获益。论文首先指出&#xff0c;随着在线服务和网络应用的快速发展&#xff0c;推荐系统已成为缓解信息过载…

路由器如何端口映射到外网?

随着互联网的发展和普及&#xff0c;远程访问已经成为了现代社会的一个重要需求。在复杂的网络环境下&#xff0c;特别是涉及异地组网的情况下&#xff0c;实现远程访问变得更加困难。本文将介绍一种名为【天联】的组网产品&#xff0c;它可以解决复杂网络环境下的远程连接问题…

搜维尔科技:Patchwork 3D工业仿真实时渲染,将CAD 数据转换成真实感的3D模型以用于工业用途

Patchwork 3D工业仿真 实时渲染点击跳转官网 从实时渲染到真实照片 根据工作阶段所需的逼真度&#xff0c;您可以使用三个渲染引擎&#xff0c;从最快的&#xff08;OpenGL&#xff0c;交互式&#xff09;到最逼真的&#xff08;光线跟踪&#xff0c;Iray物理逼真&#xff09;…

vue中使用axios获取不到响应头Content-Disposition的解决办法

项目中&#xff0c;后端返回的文件流; 前端需要拿到响应头里的Content-Disposition字段的值&#xff0c;从中获取文件名 在控制台Headers中可以看到相关的字段和文件名&#xff0c;但是在axios里面却获取不到 如果想要让客户端访问到相关信息&#xff0c;服务器不仅要在head…

web安全学习笔记【22】——文件上传(1)

WEB攻防-PHP应用&文件上传&函数缺陷&条件竞争&二次渲染&黑白名单&JS绕过 演示案例&#xff1a; PHP-原生态-文件上传-前后端验证PHP-原生态-文件上传-类型文件头验证PHP-原生态-文件上传-后缀黑白名单验证PHP-原生态-文件上传-解析配置&二次渲染…