突破JVM边界:类加载三重门与栈帧的生存法则
类加载子系统
文件验证阶段
类加载子系统在加载Class文件时,首先会验证文件格式规范,检查文件开头的魔数标识,确保这是一个合法的JVM字节码文件。
职责边界
该子系统仅负责将Class文件加载到内存中,并不关心后续能否成功执行——执行阶段由执行引擎(Execution Engine)负责。这种职责分离的设计符合JVM模块化架构思想。
内存存储机制
加载后的类信息会被存储在方法区(JVM规范中的逻辑概念,具体实现如HotSpot的元空间Metaspace),这些信息包括:
类型的完整有效名称
直接父类的完整有效名称
类型的修饰符(public/abstract/final等)
类型直接接口的有序列表
类文件生命周期
硬盘上的.class文件本质是编译后的字节码模板,通过以下流程完成转换:
物理存储:原始.class文件存在于磁盘
内存加载:类加载器将其载入方法区
元数据模板:加载后形成被称为"DNA元数据模板"的结构化数据
实例化:运行时根据该模板创建对象实例
加载器核心作用
类加载器作为关键运载工具,承担着从物理文件到内存元数据的转换桥梁作用,其工作流程包括:
通过二进制字节流读取.class文件
将静态存储结构转化为方法区的运行时数据结构
生成代表该类的java.lang.Class对象
整个过程严格遵循"加载→验证→准备→解析→初始化"的标准化流程,确保类型系统安全。
加载(Loading)
加载阶段主要完成以下工作:
二进制字节流获取:根据类的全限定名(如 java.lang.Object)查找并读取 .class 文件的二进制字节流。
数据结构转换:将字节流的静态存储结构(Class 文件格式)转换为方法区的运行时数据结构。
Class 对象创建:在堆内存中生成一个 java.lang.Class 对象,作为访问方法区类元数据的入口。
链接(Linking)
链接阶段分为三个子阶段:
(1) 验证(Verification)
确保 Class 文件的二进制字节流符合 JVM 规范,防止恶意代码破坏虚拟机安全。
包括文件格式验证(魔数 0xCAFEBABE)、元数据验证、字节码验证、符号引用验证等。
(2) 准备(Preparation)
类变量内存分配:为 static 变量(类变量)分配内存,并设置默认初始值(如 int 为 0,boolean 为 false)。
final 修饰的 static 变量:已在编译期确定值,准备阶段直接赋最终值(如 static final int x = 10 直接赋 10)。
实例变量不处理:实例变量(非 static)在对象实例化时在堆内存分配,不在准备阶段处理。
(3) 解析(Resolution)
将常量池中的符号引用(如 java/lang/Object)转换为直接引用(如内存地址)。
涉及类、方法、字段的解析,可能触发相关类的加载。
初始化(Initialization)
初始化阶段是类加载的最后一步,核心是执行类构造器 <clinit>() 方法:
- <clinit>() 方法的生成规则
由 javac 编译器自动合并以下两部分内容,并按源码中的声明顺序生成字节码指令:
所有类变量(静态变量)的显式赋值语句
static {} 静态代码块中的逻辑
- 父类优先初始化原则
若当前类存在父类(非 Object 类),JVM 会优先递归执行父类的 <clinit>() 方法,确保父类静态成员先完成初始化。
- 线程安全机制
JVM 通过隐式加锁(synchronized)保证 <clinit>() 在多线程环境下仅执行一次,避免并发重复初始化。
类加载器的分类
启动类加载器(Bootstrap ClassLoader)
实现语言:C/C++
职责:负责加载Java核心库类(如java.lang.*)
特殊性质:唯一没有父类的加载器,作为JVM内核的一部分存在
扩展类加载器(Extension ClassLoader)
实现语言:Java
继承关系:父类为启动类加载器
职责:加载JRE扩展目录(jre/lib/ext)中的类库
应用程序类加载器(AppClassLoader)
实现语言:Java
继承关系:父类为扩展类加载器
职责:加载用户程序的-classpath路径下的类
双亲委派机制
Class对象的按需加载与双亲委派机制
Class对象采用按需加载机制,只有当程序首次主动使用该类时,才会被加载到内存中。加载过程中遵循严格的双亲委派模型,其核心流程如下:
递归溯源检查
当一个类加载器收到加载请求时,会优先递归检查其父类加载器(直至启动类加载器)。检查顺序为:
当前加载器 → 父加载器 → ... → 启动类加载器
自上而下委派
若父类加载器能加载目标类(如启动类加载器已加载java.lang.String),则直接返回已存在的Class对象。
若所有父类加载器均无法加载,才会由当前加载器尝试加载。
核心优势
单例性保障
通过层级委派确保每个类在JVM中仅存在唯一的Class对象,避免重复加载引发的冲突。
安全性防护
强制优先使用父加载器(特别是启动类加载器)的核心API,有效防止用户篡改基础类。
示例:即使自定义java.lang.String类:
public class String { // 用户自定义的伪造String类
private int age;
}
由于启动类加载器已加载标准java.lang.String,系统类加载器将直接复用父加载器的结果,确保核心库的安全性。
判断两个class对象是否是同一个对象的必要条件:
两个对象的完整类名(包括包名)必须完全一致
两个对象的类加载器(实例化该类的类加载器)必须相同
PC寄存器
作用:存储下一条指令的地址,为执行引擎提供指令读取位置
PC寄存器具有内存占用极小、访问速度极快的特性。由于Java虚拟机采用单线程方法执行模型(每个线程在任何时刻仅执行一个方法),PC寄存器会精确记录当前执行方法的下一条指令地址。
PC寄存器设计为线程私有存储空间,这是由现代多线程并发机制决定的。当多个线程并发执行时,操作系统采用时间片轮转调度策略,但各线程的执行进度存在差异。若共享PC寄存器会导致执行流混乱:例如线程1执行至第五条指令后被挂起,线程2若复用同一PC寄存器,将在错误位置继续执行。通过私有化设计,每个线程都能独立保存执行状态——线程1可保存第五条指令地址,线程2可保存第九条指令地址,待重新获得CPU时间片时,各线程都能从专属PC寄存器恢复正确的执行位置。
虚拟机栈
栈和堆
栈是方法运行的单位,而堆则是数据存储的单位。
基本内容
栈的定义
每个线程在运行时都会创建一个虚拟机栈。这个虚拟机栈内部包含了多个栈帧,每个栈帧都对应着一个正在执行的方法。需要特别指出的是,虚拟机栈是线程私有的,这意味着每个线程都拥有自己独立的虚拟机栈,其他线程无法对其进行访问或干扰。
生命周期
虚拟机栈的生命周期与线程的生命周期紧密相连。当线程被创建并启动时,其对应的虚拟机栈也随之创建;而当线程执行完毕,线程结束时,该线程的虚拟机栈也会随之销毁。这种紧密的关联确保了线程在执行过程中的数据独立性和管理的便利性。
作用
在方法执行的过程中,栈发挥着不可或缺的作用。
每当一个方法开始执行时,系统会为该方法创建一个栈帧,并将其压入虚拟机栈中。在这个栈帧里,保存着该方法执行所需的各种信息。
当方法执行结束时,与之对应的栈帧会从虚拟机栈中弹出并销毁。
具体而言,栈的主要作用包括:
- 保存变量和部分结果:在方法执行期间,方法内部所定义的局部变量以及一些中间计算结果都会被保存在栈帧之中。这样,在方法的执行过程中,可以随时对这些变量和结果进行访问和操作。
- 参与方法调用和返回:方法之间的调用和返回过程也依赖于栈来实现。当一个方法调用另一个方法时,被调用方法的栈帧会被压入栈中;当被调用方法执行完毕后,其栈帧会从栈中弹出,控制权回到调用方法,继续后续的执行流程。
优点
栈帧
栈作为一种数据存储方式,具有快速高效的特点。在方法执行时,栈的操作遵循“后进先出”的原则,方法进入栈的过程就如同将物品放入一个特定顺序的容器,而方法结束出栈时,则按照相反的顺序取出。这种机制使得栈的操作速度非常快,能够满足程序高效运行的需求。
此外,使用栈来设计指令集虽然会使指令集的数量相对增多,但它具有一个显著的优势,那就是相对于寄存器而言,栈可以无视平台差异。不同的硬件平台在寄存器的数量、位置和使用规则等方面可能存在较大差异,这给程序的跨平台移植带来了困难。而栈的操作相对独立于具体的硬件实现,程序员在使用栈进行编程时,无需过多关心底层硬件的具体细节,大大提高了程序的可移植性。
栈的存储单位
在程序运行过程中,栈有着独特的存储单位。具体而言,一个线程会对应一个栈,而在这个栈当中,每一个栈帧都和一个方法相对应,它主要负责存储方法在运行时的各类数据信息。
需要特别注意的是,一个栈帧是无法调用另一个线程的栈帧的。
从本质上来说,栈帧实际上就是一个特定的内存区块,它是一个包含丰富信息的数据集合,专门用于存储方法在执行过程中的详细信息。
栈的运行原理
栈的运行遵循特定的规则,其中栈帧的返回方式主要分为两种情况。
- 正常的 return 返回
- 在方法执行过程中发送异常时的返回
当当前的栈帧完成其执行任务后,它会将相应的消息返回给前一个栈帧,随后程序会开始继续执行前一个栈帧所包含的操作,如此有序地推进程序的执行流程。
栈帧的组成
栈帧由多个重要部分组成,这些部分协同工作,确保方法的顺利执行。具体包括:
- 局部变量表
- 操作数栈
- 动态链接
- 方法返回地址
- 附加信息
局部变量表
局部变量表本质上是一个数字类型的数组结构,主要存储方法参数和局部变量信息。其支持的数据类型包含基本数据类型、引用数据类型以及returnAddress类型(指向操作数栈中返回地址的指针)。
该数据结构存储于虚拟机栈帧中,随栈帧的销毁而自动回收,天然具备线程隔离特性。
容量规划方面,局部变量表的存储空间在编译阶段即可确定,通过maximum local variables参数进行定义,运行期间无法动态调整。
方法调用深度与局部变量表存在关联关系。当方法参数和局部变量数量较多时,单个栈帧占用的内存空间会增大,导致虚拟机能够维护的栈帧总数相应减少。
作用域限制方面,局部变量表仅在所属方法执行期间有效,方法执行结束后其存储内容将不可访问。
slot(槽)
-
索引规则
- slot索引从0开始计数,最后一个有效索引值为数组长度减一。
-
slot定义
- slot是局部变量表的基本存储单元,用于存放方法参数、局部变量及返回地址等信息。
-
数据类型占用规则
- 32位及以下数据类型(如
int
,float
,引用类型
)占用1个slot。 - 64位数据类型(
long
,double
)占用连续2个slot。 char
,byte
,boolean
在编译时转为int
类型存储(false
对应0,true
对应非0值)。
- 32位及以下数据类型(如
-
数据访问方式
- 通过slot索引可直接访问对应数据内容。
- 例外:64位变量(如
long a=22
)占用索引3和4,实际操作需同时处理两个slot,但变量绑定以起始索引(如3)为标识。
-
实例方法中的槽位分配
- 参数与局部变量按声明顺序依次填充分配slot。
- 特殊规则:实例方法的索引0固定保留给
this
引用(静态方法无此占用)。 - 基本类型及
returnAddress
直接存值,引用类型存对象地址指针。
-
槽位复用机制
- 当变量超出作用域后,其占用的slot可能被后续变量复用,但需注意潜在的数据覆盖风险。
局部变量与静态变量的对比分析
1. 变量表的加载机制
- 局部变量表:在方法调用时创建,其加载需遵循特定顺序。首先将参数表(即方法形参)写入局部变量表的起始位置(索引0),随后根据方法体内部定义的变量顺序和作用域分配后续槽位(Slot)。例如,实例方法的
this
引用会占用索引0,后续参数和局部变量依次排列。 - 类变量表(静态变量表):属于类元数据的一部分,在类加载过程中完成初始化。首次初始化在编译阶段设置零值(如
int
类型为0),第二次初始化在运行时通过代码中的显式赋值完成。
2. 初始化规则对比
- 类变量:
- 编译期:由JVM自动赋予零值(如
static int count = 0
中的count
初始为0)。 - 运行期:通过静态代码块或直接赋值语句完成最终初始化(如
static { count = 100; }
)。
- 编译期:由JVM自动赋予零值(如
- 局部变量:
- 无编译期默认初始化,必须在使用前显式赋值。若未初始化直接使用,JVM会抛出错误。
3. 内存分配与性能影响
- 局部变量表:
- 垃圾回收根节点:局部变量表中引用的对象会被标记为GC Roots
操作数栈
1. 基本结构与实现
操作数栈是JVM栈帧的核心组成部分,与局部变量表并列存储于每个方法调用的栈帧中。尽管栈本身可采用数组或链表实现,但操作数栈必须通过数组结构实现,以满足严格的LIFO(后进先出)规则。其底层通过数组模拟栈操作,但禁止直接通过索引访问元素,仅允许通过push
(入栈)和pop
(出栈)指令操作数据。
2. 核心功能与特性
- 中间结果存储:用于暂存算术运算、逻辑运算的中间结果(如
iadd
指令的加法操作)。 - 临时变量空间:方法执行时,局部变量表中的值需通过操作数栈传递(如
iload
加载变量到栈顶,istore
将结果存回局部变量表)。
3. 数据类型与内存布局
- 类型兼容性:支持任意Java数据类型,32位类型(如
int
、float
)占1个栈单位,64位类型(如long
、double
)占2个栈单位。 - 栈深度控制:最大深度在编译期确定。
4. 执行引擎与编译验证
- 栈式执行引擎:HotSpot JVM的解释器基于操作数栈实现指令执行
- 双重类型检查:
- 编译期:验证操作数栈与指令的类型匹配性(如
iadd
要求栈顶为两个int
)。 - 类加载期:在类初始化时再次校验,防止运行时类型错误。
- 编译期:验证操作数栈与指令的类型匹配性(如
5. 返回值与指令流程
- 返回值处理:方法返回时,若有返回值则压入当前操作数栈,PC寄存器跳转至下一条指令。
栈顶缓存技术
虽然栈操作不涉及显式内存寻址,但频繁的入栈和出栈操作仍会导致效率下降。为此,现代处理器引入栈顶缓存机制,将栈顶元素暂存于寄存器组中。这种优化设计通过减少对内存的频繁访问,有效提升了指令执行效率。
动态链接
每个栈帧中都维护着一个关键引用,该引用指向运行时常量区中对应方法的符号引用。这个引用的核心作用在于:当方法被调用时,JVM能够通过该引用准确定位到目标方法在内存中的位置。值得注意的是,运行时常量区中实际存储的是方法的符号引用信息,而非可直接执行的直接引用。动态链接的核心任务就是将这些在编译期确定的符号引用,转换为程序运行时能够识别的直接内存地址。
从类文件编译过程来看,编译器会将方法对应的符号引用写入运行时常量区。此时方法内部引用的变量和方法都保持符号形式,例如方法参数、局部变量表中的引用等。这种设计源于编译期间无法预知方法的具体运行时地址,因此需要动态链接机制在程序运行阶段完成地址解析。
形象化理解这个过程:我们可以将方法调用类比为建筑工程。栈帧中存储的符号引用相当于建筑的设计图纸,而动态链接则如同施工团队依据图纸完成建筑主体的搭建工作。只有通过动态链接的"施工",程序才能真正获得可执行的直接内存引用。
作为JVM内存管理的重要组成部分,运行时常量池承担着双重职责:既存储方法调用所需的符号引用信息,也保存类和接口的全局常量。
方法调用
在Java的运行时绑定机制中,运行时常量池中的符号引用会根据绑定时机被转换为直接引用。根据绑定发生的阶段不同,可分为两种类型:
1️⃣ 早期绑定(静态绑定):在编译期即可确定符号引用对应的直接引用
2️⃣ 晚期绑定(动态绑定):需在运行时解析具体引用地址
值得注意的是,绑定过程在整个生命周期中仅执行一次。
关于Java方法的虚函数特性:
默认情况下,所有非final
修饰的非静态方法都具备虚函数特征(支持动态绑定)。若需禁止方法被覆盖并关闭虚函数机制,可通过final
关键字修饰该方法。此时编译器会将其视为普通方法处理,采用早期绑定机制。
虚方法和非虚方法
非虚方法(编译期绑定方法)包括:私有方法、静态方法、final修饰的方法、父类方法(通过super调用)和实例构造器。这些方法在编译阶段即可确定具体执行目标。其余方法均为虚方法(运行时绑定方法)。
各字节码指令对应的方法调用类型:
- invokestatic:专门调用静态方法(属于非虚方法)
- invokespecial:调用四类特殊方法:
- 构造方法
- 私有方法
- 父类方法(super调用)
- 实例初始化方法(构造器)
- invokevirtual:主要调用虚方法,但实际包含两类方法:
- 普通虚方法(需运行时动态绑定)
- final修饰的实例方法(虽属非虚方法,但JVM仍通过此指令调用,因其属于实例方法且无需动态分派)
- invokeinterface:调用接口中声明的方法
注:final方法虽使用invokevirtual指令调用,但由于其不可覆盖特性,JVM可在编译期完成静态分派优化。
invokedynamic
invokedynamic指令的核心作用
invokedynamic是Java 7引入的字节码指令,用于实现动态方法解析。
类型检查延迟至运行期
- 变量无显式类型声明,类型由赋值时的值决定(如
var a = 1; a = "ddd";
合法)。 - 类型错误仅在运行时暴露(如调用字符串的
parseInt()
方法会抛出异常)。
方法重写的本质与虚方法表机制解析
一、方法重写的执行流程
-
类型获取与匹配
当调用重写方法时,JVM首先从操作数栈顶获取对象实例的实际类型(即动态类型)C
-
权限校验与虚方法表写入
- 若匹配到方法,执行访问权限校验:
- 子类方法修饰符必须 ≥ 父类方法(
private< 默认 < protected < public
) - 若权限不足则抛出
IllegalAccessError
(编译期常见于访问控制符冲突,运行期多因类加载冲突导致)
- 子类方法修饰符必须 ≥ 父类方法(
- 校验通过后,将方法直接引用写入虚方法表(VMT)对应槽位
- 若匹配到方法,执行访问权限校验:
-
继承链回溯机制
- 若当前类未找到匹配方法,按继承链向上回溯(子类→父类→祖父类...)
- 最终未找到时抛出
AbstractMethodError
(常见于接口/抽象类实现不完整)
二、虚方法表的核心作用
-
动态分派优化
- 每个类加载完成后,在链接阶段构建虚方法表
- 表中存储可被重写方法的直接引用(非虚方法如
static/final/private
不参与)
-
内存布局示例
class Animal {void eat() { ... } // 虚方法表槽位0void sleep() { ... } // 虚方法表槽位1 }class Dog extends Animal {@Override void eat() { ... } // 覆盖父类槽位0void bark() { ... } // 新增槽位2 }
Dog
的虚方法表前部继承自Animal
,重写方法覆盖原槽位,新增方法追加到尾部
-
性能对比
调用类型 查找方式 时间复杂度 适用场景 虚方法 虚方法表间接跳转 O(1) 多态场景 非虚方法 静态地址直接调用 - final/static方法
方法返回地址(Return Address)
方法返回地址用于存储调用者在方法调用后的下一条待执行指令。当方法执行完毕时,JVM需要通过该地址控制程序流程的恢复。
方法终止机制
方法可通过两种方式终止:
- 正常返回:通过返回指令(如ireturn/areturn/dreturn等)主动结束
- 异常终止:因未捕获异常导致非正常退出
共性特征:
- 无论何种终止方式,均会恢复调用者的执行上下文
- 调用者的程序计数器(PC)将被重置为方法返回地址
- 调用者的局部变量表等执行环境将被恢复
差异特性:
- 正常返回会将返回值推送到调用者的操作数栈
- 异常返回不会传递任何返回值
- 异常终止需通过异常表进行错误处理
返回指令类型
根据返回值类型,JVM采用不同的字节码指令:
- boolean/byte/short/int → ireturn
- long → lreturn
- float → freturn
- double → dreturn
- 对象引用 → areturn
- void方法 → return
栈帧处理流程
- 方法退出时触发栈帧弹出操作
- 正常返回:
- 将返回值推送至调用者操作数栈顶
- PC寄存器载入方法返回地址
- 异常返回:
- 清空操作数栈
- 通过异常表查找匹配的异常处理器
- 异常处理时不会保留当前方法的返回值
栈的一些问题
栈空间不足时可通过JVM参数-Xss调整栈大小,但需注意:
- 调整栈大小无法彻底避免StackOverflowError,例如陷入死循环时持续申请栈帧仍会导致溢出
- 栈内存分配存在权衡关系:过大的栈设置会挤占堆内存空间,可能引发OutOfMemoryError
垃圾回收机制(GC)作用范围说明:
- 虚拟机栈(JVM Stack)不参与GC管理
- 堆(Heap)和方法区(Method Area)是GC的主要工作区域
- 方法调用栈帧的创建/销毁由JVM自动管理,无需GC介入
关于局部变量的线程安全性:
- 纯方法内局部变量(非共享状态)天然线程安全
- 需警惕以下情况:
- 方法参数可能被多线程调用持有
- 返回值可能被外部对象引用
- 不可变对象(如String)作为参数/返回值时具有线程安全性
- 读操作不涉及线程安全问题,写操作需同步控制
相关文章:
突破JVM边界:类加载三重门与栈帧的生存法则
类加载子系统 文件验证阶段 类加载子系统在加载Class文件时,首先会验证文件格式规范,检查文件开头的魔数标识,确保这是一个合法的JVM字节码文件。 职责边界 该子系统仅负责将Class文件加载到内存中,并不关心后续能否成功执行—…...
VSCode 查看文件的本地修改历史
1. 使用时间线视图(Timeline) 新版 VSCode 内置了一个叫 Timeline(时间线) 的功能,可以查看: 本地文件修改记录(包括保存历史)Git 提交历史(如果仓库是 Git 管理的&…...
在QGraphicsView中精确地以鼠标为锚缩放图片
在pyqt中以鼠标所在位置为锚点缩放图片-CSDN博客中的第一个示例中,通过简单设置: self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse) 使得QGraphicsView具有了以鼠标为锚进行缩放的功能。但是,其内部应当是利用了滚动条的移动来…...
【Python数据驱动决策】数据分析与可视化全流程实战指南
目录 前言技术背景与价值当前技术痛点解决方案概述目标读者说明一、技术原理剖析核心概念图解核心作用讲解关键技术模块说明技术选型对比二、实战演示环境配置要求核心代码实现案例1:销售数据清洗案例2:月度销售趋势分析案例3:产品关联分析(热力图)运行结果验证三、性能对…...
报错解决:ModuleNotFoundError: No module named ‘triton.ops‘
报错原因:2024.5.21之后, triton.ops 被移动到另一个工程 triton-lang/kernels中。 参考链接:官方解释 解决方案:换用2024.5.21之前发布的版本。 pip3 install triton2.3.0...
相机-IMU联合标定:相机-IMU外参标定
文章目录 📚简介🚀标定工具kalibr🚀标定数据录制🚀相机-IMU外参标定📚简介 在 VINS(视觉惯性导航系统) 中,相机-IMU外参标定 是确保多传感器数据时空统一的核心环节,其作用可概括为以下关键点: 坐标系对齐(空间同步),外参误差会导致视觉特征点投影与IMU预积…...
Qt C++数据库实验
一、实验目的和要求 1、掌握Qt中数据库SQL类数据库的查询、插入和更新操作。 2、熟悉Qt界面设计中常用的控件。 3、了解数据库相关类。 二、实验内容 1、设计一个数据库操作软件,完成数据库的相关操作。 2、建立按钮的信号与槽函数,实现点击按钮进…...
相机-IMU联合标定:IMU标定
文章目录 📚 简介🚀标定工具安装📌 IMU标定工具 code_utils📌 IMU标定工具 imu_utils:🚀标定数据录制🚀IMU标定📚 简介 在 VINS(Visual-Inertial Navigation System,视觉惯性导航系统) 中,IMU标定 是确保系统高精度运行的关键环节。IMU(惯性测量单元)本身…...
榕壹云信用租赁系统:基于ThinkPHP+MySQL+UniApp的全链路免押租赁解决方案
信用租赁时代的全流程数字化革新 随着共享经济与信用体系的深度融合,传统租赁行业正面临效率与信任的双重挑战。榕壹云信用租赁系统依托ThinkPHP高性能框架、MySQL数据库与UniApp跨平台开发技术,构建了一套覆盖设备租赁全生命周期的数字化解决方案。通过整合多因子身份认证、…...
C语言中的指针详解
指针是C语言中非常强大且复杂的特性之一,它为我们提供了更灵活的内存管理方式,使得程序能够直接操作内存,提升效率和性能。尽管指针非常强大,但如果不理解它的概念和使用方式,很容易出现错误。因此,理解指针…...
NIPS2021 | 视觉 Transformer 的有趣特性
Intriguing Properties of Vision Transformers 摘要-Abstract引言-Introduction相关工作-Related Work视觉Transformer的有趣特性-Intriguing Properties of Vision Transformers视觉Transformer对遮挡具有鲁棒性吗?-Are Vision Transformers Robust to Occlusions…...
贪心算法-2208.将数组和减半的最小操作数-力扣(LeetCode)
一、题目解析 这里要注意恰好这个字眼,说明对任意数减小一半是不需要向上取整的,所以我们需要定义double类型的数据。 二、算法解析 我们需要将数组和减小为一半的次数最少,所以根据贪心算法,我们需要取数组中最大的数进行减半操…...
如何搭建spark yarn 模式的集群集群。
下载 App 如何搭建spark yarn 模式的集群集群。 搭建Spark on YARN集群的详细步骤 Spark on YARN模式允许Spark作业在Hadoop YARN资源管理器上运行,利用YARN进行资源调度。以下是搭建步骤: 一、前提条件 已安装并配置好的Hadoop集群(包括HDF…...
嵌入式开发面试典型编程题解析:排序算法、指针操作、字符处理、递归原理等基础原理的深度解析。
在嵌入式开发面试中,编程题是常见的考察形式,旨在检验求职者对基础编程知识的掌握和应用能力。以下是几道典型的嵌入式面试编程题及详细解析,帮助新手逐步理解和掌握相关知识点。 一、用交换法对学生成绩降序排序 题目描述 在嵌入式系统开…...
DeepSeek+即梦:AI视频创作从0到1全突破
目录 一、开启 AI 视频创作大门:前期准备1.1 注册与登录1.2 熟悉工具界面1.3 硬件与网络要求 二、用 DeepSeek 构思视频脚本2.1 明确创作主题与目标2.2 编写优质提示词2.3 生成并优化脚本 三、即梦 AI 实现画面生成3.1 文生图基础操作3.2 调整参数提升画质3.3 保持人…...
npm init、换源问题踩坑
文章目录 一、 问题复现二、问题解决 一、 问题复现 成功安装nodejs 以及 npm 版本如下: > node -v > v20.18.0 > npm -v > 10.8.2使用 npm init 命令时延时过长,考虑换源,使用指令 npm config set registry https://registr…...
TRex 控制台命令解析
TRex 是一种高性能的网络测试工具,用于生成和分析网络流量。以下是对这些命令的简要解释: 一、help Console Commands(控制台命令) capture:管理 PCAP 捕获。debug:用于开发的内部调试器。events&#x…...
【Shell 脚本入门】轻松上手的实战指南
🌈 个人主页:Zfox_ 🔥 系列专栏:Shell脚本编程 目录 一:🔥 什么是 Shell 🦋 常见的 Shell 类型 二:🔥 什么是 Shell 脚本 🦋 Shell 脚本规则🦋 第…...
数据结构*栈
栈 什么是栈 这里的栈与我们之前常说的栈是不同的。之前我们说的栈是内存栈,它是JVM内存的一部分,用于存储局部变量、方法调用信息等。每个线程都有自己独立的栈空间,当线程启动时,栈就会被创建;线程结束,…...
零基础制作Freertos智能小车(教程非常简易)持续更新中....
从现开始,将陆续推出各类简单的DIY电子设计,由简入深,将自己的制作过程全部分享出来,巩固自己知识的同时希望借此机会认识更多喜欢电子设计的小伙伴。 本次小车的主控芯片采用stm32f103c8t6,主要是便宜好用&am…...
Leetcode - 双周赛155
目录 一,3527. 找到最常见的回答二,3528. 单位转换 I三,3529. 统计水平子串和垂直子串重叠格子的数目四,3530. 有向无环图中合法拓扑排序的最大利润 一,3527. 找到最常见的回答 题目列表 本题是一道模拟题࿰…...
详解RabbitMQ工作模式之工作队列模式
目录 工作队列模式 概念 特点 应用场景 工作原理 注意事项 代码案例 引入依赖 常量类 编写生产者代码 编写消费者1代码 编写消费者2代码 先运行生产者,后运行消费者 先运行消费者,后运行生产者 工作队列模式 概念 在工作队列模式中&#x…...
QGIS+mcp的安装和使用
QGISmcp的安装和使用 安装qgis_mcp 下载qgis_mcp: git clone https://github.com/jjsantos01/qgis_mcp.git安装uv uv是一个由Rust语言编写的python包管理工具,旨在提供比传统工具(如 pip)更高效的依赖管理和虚拟环境操作。 p…...
Java基础361问第16问——枚举为什么导致空指针?
我们看一段代码 public enum Color {RED, BLUE, YELLOW;public static Color parse(String color) {return null;} }public static void main() {Color color Color.parse("");// 极具迷惑性,大家日常开发肯定这么写过switch (color) {case RED:break;c…...
在 C# .NET 中驾驭 JSON:使用 Newtonsoft.Json 进行解析与 POST 请求实战
JSON (JavaScript Object Notation) 已经成为现代 Web 应用和服务之间数据交换的通用语言。无论你是开发后端 API、与第三方服务集成,还是处理配置文件,都绕不开 JSON 的解析与生成。在 C# .NET 世界里,处理 JSON 有多种选择,其中…...
CentOS7——Docker部署java服务
1、安装Docker 首先要确保系统已安装 Docker,若未安装,可以参考我的另一篇文章现在CentOS7上安装Docker,文章地址如下: CentOS7系统安装Docker教程-CSDN博客 Docker当中要安装必备的软件,比如Java运行必要的JDK&#…...
Python-Part2-集合、字典与推导式
Python-Part2-集合、字典与推导式 1. set集合 ⽆序,去掉重复数据。 set1 {1,2,3,4,5,5,4,3,2,1}print(type(set1))print(set1)set2.add(66666)set2.remove(55)#不能使用下标访问set,所以修改操作一般为remove操作 add操作2.dict 字典 字典ÿ…...
《AI大模型应知应会100篇》第39篇:多模态大模型应用:文本、图像和音频的协同处理
第39篇:多模态大模型应用:文本、图像和音频的协同处理 摘要 随着人工智能技术的发展,多模态大模型(Multimodal Large Models)已经成为AI领域的热点之一。这些模型能够同时处理文本、图像、音频等多种模态数据…...
kvm学习小结
安装相关包 安装虚拟化相关包 apt install qemu-kvm qemu-system libvirt-clients libvirt-daemon-system vlan bridge-utils 安装界面相关包 apt install xinit gdmd 配置机器允许root登录 检查cpu是否支持虚拟化 egrep -o vmx|svm /proc/cpuinfo 执行命令systemctl s…...
k8s基本概念-YAML
YAML介绍 YAML是“YAML Aint a Markup Language” (YAML不是一种置标语言)的递归缩进写,早先YAML的意思其实是:“Yet Another Markup Language”(另一种置标语言) YAML是一个类似XML、JSON的标记性语言。YAML强调以数据为中心,并不是以标识语言为重点。因而YAML本身的定义…...
wps批注线条怎么取消去掉wps批注后有竖线
wps批注线条怎么取消去掉wps批注后有竖线 问题 图片 解决方案 图片 word批注线条取消的方法: 1.打开Word文档,点击需要删除的批注。 2.然后点击工具栏“审阅”选项。 3.接着点击“接受“ 4.接受对文档所做的所有修订(H)...
深度解析算法之分治(归并)
48.排序数组 题目链接 给你一个整数数组 nums,请你将该数组升序排列。 你必须在 不使用任何内置函数 的情况下解决问题,时间复杂度为 O(nlog(n)),并且空间复杂度尽可能小。 示例 1: 输入: nums [5,2,3,1] 输出&am…...
僵尸进程是什么?
僵尸进程(Zombie Process)是指在 Unix/Linux 系统中,一个子进程已经终止,但其父进程尚未对它进行善后处理(即没有读取其退出状态),导致子进程的进程表项仍然保留在系统中。由于这个进程已经结束…...
城市群出行需求的时空分形
城市群出行需求的时空分形 原文:He, Zhengbing. “Spatial-temporal fractal of urban agglomeration travel demand.” Physica A: Statistical Mechanics and its Applications 549 (2020): 124503. 1. Introduction(引言) 城市区域的重…...
LangChain入门(二)安装开发环境
1.安装conda Conda 是一个开源的软件包管理系统和环境管理系统,用于安装多个版本的软件包及其依赖关系,并在它们之间轻松切换。 Anaconda是一个开源的Python发行版本,其包含了conda、python等软件包,numpy、pandas、scipy等科学…...
如何开展有组织的AI素养教育?
一、AI素养的定义与核心内涵 AI素养是智能时代个体适应与创新能力的综合体现,其内涵随着技术发展动态扩展,包含以下核心维度: 知识体系:理解AI基本原理(如算法、数据、算力)、技术边界及发展趋势ÿ…...
InnoDB对LRU算法的优化
标准 LRU 算法的核心思想是:当缓存空间不足时,淘汰掉最近最少使用的数据块(Page)。它通常用一个链表来实现,链表头部是最近访问的 Page,链表尾部是最久未访问的 Page。 然而,在数据库系统中直接…...
云原生--核心组件-容器篇-7-Docker私有镜像仓库--Harbor
1、Harbor的定义与核心作用 定义: Harbor是由VMware开源的企业级容器镜像仓库系统,后捐赠给 CNCF (Cloud Native Computing Foundation)。它基于Docker Registry扩展了企业级功能,用于存储、分发和管理容器镜像(如Docker、OCI标准…...
TypeScript 实用类型深度解析:Partial、Pick、Record 的妙用
需求背景:在后台系统的用户管理模块中,我们常遇到这样的场景:修改用户资料时只需要传部分字段,展示用户列表时要隐藏敏感信息,快速查找用户需要ID索引等等,这些业务需求都可以通过 TypeScript 的实用类型优…...
【Pandas】pandas DataFrame rmod
Pandas2.2 DataFrame Binary operator functions 方法描述DataFrame.add(other)用于执行 DataFrame 与另一个对象(如 DataFrame、Series 或标量)的逐元素加法操作DataFrame.add(other[, axis, level, fill_value])用于执行 DataFrame 与另一个对象&…...
如何搭建spark yarn 模式的集群集群
以下是搭建Spark YARN模式集群的一般步骤: 准备工作 - 确保集群中各节点安装了Java环境,并配置好 JAVA_HOME 环境变量。 - 各节点间能通过SSH免密登录。 - 安装并配置好Hadoop集群,YARN作为Hadoop的资源管理器,Spark YARN模式需要…...
云原生--核心组件-容器篇-6-Docker核心之-镜像仓库(公共仓库,私有仓库,第三方仓库)
1、Docker仓库的定义与核心作用 定义: Docker仓库(Docker Registry)是用于存储、分发和管理Docker镜像的集中式存储库。它类似于代码仓库,但专门用于容器镜像的版本控制和共享。它允许开发人员和IT团队高效地管理、部署和分享容器…...
mysql8.0版本部署+日志清理+rsync备份策略
mysql安装:https://blog.csdn.net/qq_39399966/article/details/120205461 系统:centos7.9 数据库版本:mysql8.0.28 1.卸载旧的mysql,保证环境纯净 rpm -qa | grep mariadb mariadb-5.... rpm -e --nodeps 软件 rpm -e --nodeps mariadb-5.…...
搭建spark yarn 模式的集群集群
一.引言 在大数据处理领域,Apache Spark 是一个强大的分布式计算框架,而 YARN(Yet Another Resource Negotiator)是 Hadoop 的资源管理系统。将 Spark 运行在 YARN 模式下,可以充分利用 YARN 强大的资源管理和调度能力…...
在uni-app中使用Painter生成小程序海报
在uni-app中使用Painter生成小程序海报 安装Painter 从GitHub下载Painter组件:https://github.com/Kujiale-Mobile/Painter 将painter文件夹复制到uni-app项目的components目录下 配置页面 在需要使用海报的页面的pages.json中配置 {"path": "pag…...
Uni-app网络请求AES加密解密实现
Uni-app 网络请求封装与 AES 加密解密实现 下面我将为你提供一个完整的 Uni-app 网络请求封装方案,包含 POST 请求的统一处理、请求参数和响应数据的 AES 加密解密。 1. 创建加密解密工具类 首先创建一个 crypto.js 文件用于处理 AES 加密解密: // u…...
uniapp实现统一添加后端请求Header方法
uniapp把请求写完了,发现需要给接口请求添加头部,每个接口去添加又很麻烦,uniapp可以统一添加,并且还能给某些接口设置不添加头部。 一般用于添加token登录验证信息。 在 main.js 文件中配置。 代码如下: // 在…...
uniapp打包apk如何实现版本更新
我们做的比较简单,在后端设置版本号,并在uniapp的config.js中定义版本号,每次跟后端的进行对比,不一致的话就更新。 一、下载apk 主要代码(下载安装包,并进行安装,一般得手动同意安装…...
【Java开发日记】OpenFeign 的 9 个坑
目录 坑一:用对Http Client 1.1 feign中http client 1.2 ribbon中的Http Client 坑二:全局超时时间 坑三:单服务设置超时时间 坑四:熔断超时时间 4.1 使用feign超时 4.2 使用ribbon超时 4.3 使用自定义Options 坑五&…...
RocketMQ 存储核心:深入解析 CommitLog 设计原理
一、引言 在分布式消息队列系统中,消息存储的可靠性和高吞吐能力是衡量系统优劣的核心指标。Apache RocketMQ 作为一款高性能、高可用的分布式消息中间件,其独特的 CommitLog 存储机制在消息持久化过程中扮演了关键角色。本文将深入剖析 CommitLog 的设…...