JVM——方法内联之去虚化
引入
在Java虚拟机的即时编译体系中,方法内联是提升性能的核心手段,但面对虚方法调用(invokevirtual
/invokeinterface
)时,即时编译器无法直接内联,必须先进行去虚化(Devirtualization)——将动态绑定的虚方法转换为静态可确定的直接调用。这一过程是连接多态抽象与高效执行的关键桥梁,直接决定了虚方法能否被有效内联,进而影响程序性能。
虚方法调用的挑战
虚方法调用的本质是动态绑定:运行时根据对象实际类型确定目标方法。例如:
abstract class BinaryOp {public abstract int apply(int a, int b);
}
class Add extends BinaryOp {public int apply(int a, int b) { return a + b; }
}
BinaryOp op = new Add();
op.apply(2, 1); // 编译时无法确定具体调用Add.apply还是Sub.apply
这种动态性导致即时编译器无法直接内联,必须通过去虚化技术将其转换为直接调用,才能进一步展开方法体。
去虚化的核心目标
唯一目标确定:证明虚方法调用存在唯一目标方法,转换为invokestatic
/invokespecial
等直接调用指令。
条件适配:若无法确定唯一目标,则生成类型测试代码,将虚调用转换为条件化的直接调用。
基于类型推导的完全去虚化:精准定位动态类型
类型推导的核心逻辑
通过数据流分析,在IR图中确定调用者的动态类型,消除虚方法的多态性。典型场景包括明确的对象创建和强制类型转换。
代码示例:明确的动态类型
public static int foo() {BinaryOp op = new Add(); // 动态类型为Add,无多态可能return op.apply(2, 1);
}
public static int bar(BinaryOp op) {op = (Add) op; // 强制转换,编译器确保运行时类型为Addreturn op.apply(2, 1);
}
IR图分析:动态类型的精准表达
foo方法内联前IR图:
0: Start
2: New Add // 创建Add实例,类型明确
9: Invoke#Add.apply // 直接调用Add.apply,无需动态分派
11: Return
- 2号节点
New Add
直接确定op
的类型,9号Invoke
节点明确指向Add.apply
,无需虚方法表查找。
bar方法内联前IR图:
0: Start
3: InstanceOf // 强制转换前的类型检查
9: Invoke#Add.apply // 转换后类型确定,调用具体实现
11: Return
- 3号
InstanceOf
节点确保类型安全,后续调用与foo方法一致。
内联后的极致优化
foo方法内联及逃逸分析后IR图:
0: Start
11: Return 3 // 常量折叠后直接返回2+1的结果
- 内联后
Add.apply
的代码被展开,结合常量折叠,条件判断和字段访问被优化为单一返回节点。
bar方法内联后IR图:
0: Start
11: Return 3 // 同样完成常量折叠,消除类型转换开销
- 强制转换的安全性由运行时检查保证,但内联后代码路径与foo方法一致。
失败案例:notInlined方法的局限性
public static int notInlined(BinaryOp op) {if (op instanceof Add) { // 理论上可能推导为Add,但编译器选择放弃return op.apply(2, 1);}return 0;
}
IR图分析:
10: Invoke#BinaryOp.apply // 仍为虚方法调用,未被去虚化
- 原因:类型推导需全局数据流分析,成本较高,编译器优先依赖后续去虚化手段。
编译器策略:局部优化优先
C2和Graal仅在无需额外分析即可确定类型时进行类型推导去虚化(如new
对象、强制转换),避免全局分析的高成本。这一策略在保持优化效率的同时,覆盖了大部分明确类型场景。
基于类层次分析的完全去虚化:静态结构的深度挖掘
类层次分析的核心思想
通过分析已加载的类,判断抽象方法是否仅有一个实现。若成立,则注册“唯一实现”假设,将虚调用转换为直接调用。
单实现场景:假设的建立与验证
public static int test(BinaryOp op) {return op.apply(2, 1);
}
编译时状态:若仅加载Add
类,编译器假设BinaryOp.apply
唯一实现为Add.apply
。
IR图变化:
0: Start
13: Constant 3 // 内联Add.apply后的常量结果
8: Return 3 // 直接返回结果,无需类型检测
- 动态类型检测被移至假设,IR图省略所有类型相关节点。
假设失效与去优化
类加载冲击:后续加载Sub
类,假设失效,触发去优化。
// 运行时加载Sub类后,原编译结果被标记为“not entrant”
System.out.println("JITTest::test made not entrant");
假设注册机制:编译器为每个去虚化结果添加类层次假设(如“BinaryOp
仅有Add
子类”),类加载器实时验证这些假设。
final修饰符的优化价值
显式不可变:final class Add
明确禁止继承,编译器无需假设,直接确定调用目标。
Effective Final:即使未标记final
,若类层次分析确定无子类,仍可去虚化,但需注册假设。
接口方法的特殊性
无法完全去虚化:接口允许动态实现,Java虚拟机必须保留类型测试(如invokeinterface
指令的动态检查),因此C2放弃接口方法的类层次分析去虚化,依赖条件去虚化。
条件去虚化:动态类型的概率性匹配
类型Profile:运行时类型的记忆库
Java虚拟机为每个虚调用点收集高频出现的动态类型(如Add
和Sub
),形成类型Profile。默认最多记录2个类型,超过则视为不完整。
条件去虚化的实现过程
伪代码逻辑
public static int test(BinaryOp op) {if (op.getClass() == Add.class) { // 匹配Profile中的类型return 2 + 1; // 内联Add.apply} else if (op.getClass() == Sub.class) {return 2 - 1; // 内联Sub.apply} else {// 处理未记录类型(去优化或虚调用)}
}
IR图关键节点:TypeSwitch的作用
完整Profile场景:
27: TypeSwitch // 按Profile中的类型依次匹配
21: Deopt TypeCheckInliningViolated // 匹配失败时触发去优化
- 若所有记录类型均不匹配,且Profile完整(记录所有出现过的类型),则重新收集类型并去优化。
不完整Profile场景(Graal特有):
21: Invoke#BinaryOp.apply // 回退到虚方法调用
- Graal生成虚调用代码,通过内联缓存或方法表动态绑定,避免频繁去优化。
编译器差异:C2与Graal的策略分歧
C2处理:不完整Profile时直接使用内联缓存,不进行条件去虚化。
Graal处理:生成包含虚调用的IR图,平衡优化收益与编译成本。
性能权衡
优势:覆盖大部分高频类型,提升热点路径性能。
局限:低频类型仍需动态分派,且Profile容量限制可能导致不完整匹配。
IR图深度解析:去虚化前后的节点变换
完全去虚化的节点简化
阶段 | foo方法关键节点变化 | 核心优化点 |
---|---|---|
内联前 | 9号Invoke#BinaryOp.apply(虚调用) | 存在动态分派开销 |
去虚化后 | 9号Invoke#Add.apply(直接调用) | 消除虚方法表查找 |
内联及优化后 | 13号Constant 3(常量折叠) | 条件分支与字段访问被消除 |
条件去虚化的节点膨胀
新增节点:TypeSwitch
(类型匹配)、Phi
(返回值聚合)、Deopt
(去优化触发)。
控制流变化:单一调用路径变为多分支结构,每个分支对应一个记录类型的内联代码。
失败场景的IR图特征
notInlined方法:保留Invoke#BinaryOp.apply
节点,条件判断未被优化,性能与虚调用一致。
接口方法:必须包含InstanceOf
或TypeTest
节点,无法省略动态类型检测。
实践与调试:揭开去虚化的神秘面纱
复现去优化过程
通过以下代码观察类加载导致的去优化日志:
public class JITTest {static abstract class BinaryOp { /* ... */ }static class Add extends BinaryOp { /* ... */ }static class Sub extends BinaryOp { /* ... */ }public static int test(BinaryOp op) { return op.apply(2, 1); }public static void main(String[] args) throws Exception {// 高频调用触发内联for (int i = 0; i < 400_000; i++) test(new Add());// 加载Sub类,触发去优化Class.forName("JITTest$Sub");}
}
启动参数:-XX:+PrintCompilation -XX:CompileCommand='dontinline JITTest.test'
预期输出:JITTest::test made not entrant
,表示编译结果因假设失效被回收。
观察类型Profile
通过-XX:+PrintTypeProfile
打印类型Profile信息,查看虚调用点的动态类型分布:
[TypeProfile] Method: JITTest.test(BinaryOp)InvokeVirtual #BinaryOp.apply:Types: Add (90%), Sub (10%)
- 输出解读:
Add
占90%,Sub
占10%,编译器据此生成条件判断分支。
代码优化建议
- 标记确定类型:对确定无继承的类/方法添加
final
,简化编译器假设。 - 减少动态分派:通过工厂模式限制子类数量,提升类层次分析成功率。
- 监控去优化:通过
-XX:+PrintDeoptimization
跟踪去优化事件,定位低效路径。
总结
去虚化技术是Java虚拟机在动态性与高效执行之间的精妙平衡,它不仅是即时编译器的核心模块,更是理解多态优化的关键窗口。从类型推导的精准打击到条件匹配的动态适应,每一种去虚化方式都体现了编译优化的工程智慧。掌握这些技术,不仅能写出更易被优化的代码,更能深入理解Java性能优化的底层逻辑,在复杂业务场景中释放程序的最大潜力。
去虚化的三重境界
- 类型推导:精准定位明确类型,适用于局部作用域内的确定调用。
- 类层次分析:基于静态类结构建立假设,覆盖单实现场景。
- 条件匹配:借助运行时Profile,处理高频多态调用。
编译器的平衡艺术
- 效率与安全:类型推导和类层次分析追求极致优化,但受限于假设和类加载动态性。
- 通用与特殊:条件去虚化牺牲部分优化深度,换取对复杂多态的普遍支持。
开发者的行动指南
- 代码设计:利用
final
、密封类(Sealed Class)减少多态层次,降低去虚化难度。- 性能调优:通过虚拟机参数观察去虚化效果,针对热点路径优化类型Profile。
相关文章:
JVM——方法内联之去虚化
引入 在Java虚拟机的即时编译体系中,方法内联是提升性能的核心手段,但面对虚方法调用(invokevirtual/invokeinterface)时,即时编译器无法直接内联,必须先进行去虚化(Devirtualizationÿ…...
1.6 关于static和final的修饰符
一.static static是静态修饰符,用于修饰类成员(变量,方法,代码块) 被修饰的类成员属于类,不必生成示例,即可直接调用属性或者方法。 关于代码块,被static修饰的代码块是静态代码块…...
Django 中时区的理解
背景 设置时区为北京时间 TIME_ZONE ‘Asia/Shanghai’ # 启用时区支持 USE_TZ True 这样设置的作用 前端 (实际上前端el-date-picker 显示的是当地时区的时间) Element组件转换后,我们是东八区,前端传给后端的时间为&…...
hadoop中创建MySQL新数据库数据表
在Hadoop环境中创建MySQL数据库和数据表,通常需要通过MySQL命令行工具来完成,而不是直接在Hadoop中操作。以下是具体步骤: 1. 登录MySQL 首先,需要登录到MySQL服务器。在命令行中输入以下命令: mysql -u root -p 输…...
ridecore流水线解读
文章目录 流水线stage分属前后端PCpipelineIFIDDPDP 与 SW 中间没有latchSWCOM 源码地址 流水线stage分属前后端 IF -> ID -> DP -> SW -> EX -> COM分类阶段说明前端IF指令获取阶段。PC 使用分支预测器,访问指令存储器。典型前端操作。前端ID解码并…...
基于C语言实现网络爬虫程序设计
如何用好C语言来做爬虫,想必接触过的大神都能说扥头头是道,但是对于新手小白来说,有这么几点需要注意的。根据设计程序结构,我们需要一个队列来管理待爬取的URL,一个集合或列表来记录已访问的URL。主循环从队列中取出U…...
github 上的 CI/CD 的尝试
效果 步骤 新建仓库设置仓库的 page 新建一个 vite 的项目,改一下 vite.config.js 中的 base 工作流 在项目的根目录下新建一个 .github/workflows/ci.yml 文件,然后编辑一下内容 name: Build & Deploy Vue 3 Appon:push:branches: [main]permi…...
飞书配置表数据同步到数据库中
这是我的从飞书取数据的代码 def get_employee_from_feishu():staff_setting settings.FEISHU_SETTING["sales_order"]["employee"]app_token staff_setting ["app_token"]table_id staff_setting ["table_id"]page_token Noneh…...
Nacos源码—9.Nacos升级gRPC分析八
大纲 10.gRPC客户端初始化分析 11.gRPC客户端的心跳机制(健康检查) 12.gRPC服务端如何处理客户端的建立连接请求 13.gRPC服务端如何映射各种请求与对应的Handler处理类 14.gRPC简单介绍 12.gRPC服务端如何处理客户端的建立连接请求 (1)gRPC服务端是如何启动的 (2)connec…...
开源免费无广告专注PDF编辑、修复和管理工具 办公学术 救星工具
各位PDF处理小能手们!我跟你们说啊,今天要给大家介绍一款超牛的国产开源PDF处理工具,叫PDFPatcher,也叫PDF补丁丁。它就像一个PDF文档的超级修理工,专门解决PDF编辑、修复和管理的各种难题。 这软件的核心功能和特点&a…...
C++设计模式——单例模式
单例模式 方法1:C11 线程不安全懒汉模式(不推荐) 懒汉式单例模式在第一次使用时才创建实例,但这种方式在多线程环境下可能会出现问题。 class Singleton { private:static Singleton* instance; // 静态指针,用于存储…...
装饰器在Python中的作用及在PyTorchMMDetection中的实战应用
装饰器在Python中的作用 1. 装饰器是什么?为什么它很重要? 装饰器(Decorator)是Python中的一种高级语法,用于在不修改原函数代码的情况下,动态增强函数的功能。它的核心思想是**"装饰"现有函数*…...
时间序列预测建模的完整流程以及数据分析【学习记录】
文章目录 1.时间序列建模的完整流程2. 模型选取的和数据集2.1.ARIMA模型2.2.数据集介绍 3.时间序列建模3.1.数据获取3.2.处理数据中的异常值3.2.1.Nan值3.2.2.异常值的检测和处理(Z-Score方法) 3.3.离散度3.4.Z-Score3.4.1.概述3.4.2.公式3.4.3.Z-Score与…...
【工作记录】Kong Gateway入门篇之简介
1. 什么是 Kong Gateway? Kong Gateway 是一个开源的、云原生的 API 网关,专为现代微服务架构设计。它基于 Nginx 和 Lua 构建,提供了高性能、可扩展的 API 管理解决方案。Kong Gateway 不仅能够处理 API 请求的路由和负载均衡,还…...
华为鸿蒙电脑能否作为开发机?开发非鸿蒙应用?
目录 一、鸿蒙电脑作为开发机的核心能力1. 硬件与系统架构2. 开发工具链支持 二、开发非鸿蒙应用的可行性分析1. 适配优势与局限性2. 生态限制 三、鸿蒙电脑的核心适用场景1. 推荐开发场景2. 目标用户群体3. 非推荐场景 四、未来生态演进与战略意义五、总结 一、鸿蒙电脑作为开…...
jackson-dataformat-xml引入使用后,响应体全是xml
解决方案: https://spring.io/blog/2013/05/11/content-negotiation-using-spring-mvc import org.springframework.context.annotation.Configuration; import org.springframework.http.MediaType; import org.springframework.web.servlet.config.annotation.Con…...
【deekseek】TCP Offload Engine
是的,TOE(TCP Offload Engine)通过专用硬件电路(如ASIC或FPGA)完整实现了TCP/IP协议栈,将原本由CPU软件处理的协议计算任务完全转移到网卡硬件中。其延迟极低的核心原因在于 硬件并行性、零拷贝架构 和 绕过…...
Flannel Host-gw模式的优缺点
Host-gw 模式的特点、优缺点 优点 高性能:无封装开销,数据包直接通过主机路由表转发,延迟和吞吐量接近原生网络。零额外开销:不使用隧道或封装,无额外字节,带宽利用率最高。配置简单:只需配置…...
SD-HOST Controller design-----SD CLK 设计
hclk的分频电路,得到的分频时钟作为sd卡时钟。 该模块最终输出两个时钟:一个为fifo_sd_clk,另一个为out_sd_clk_dft。当不分频时,fifo_sd_clk等于hclk;当分频时候,div_counter开始计数,记到相应分频的时候…...
zabbix最新版本7.2超级详细安装部署(一)
如果文章对你有用,请留下痕迹在配置过程中有问题请及时留言,本作者可以及时更新文章 目录 1、提前准备环境 2、zabbix7.2安装部署 3、安装并配置数据库 4、为Zabbix server配置数据库 5、为Zabbix前端配置PHP 6、启动Zabbix server和agent进程 7、关闭防…...
BFS算法篇——打开智慧之门,BFS算法在拓扑排序中的诗意探索(上)
文章目录 引言一、拓扑排序的背景二、BFS算法解决拓扑排序三、应用场景四、代码实现五、代码解释六、总结 引言 在这浩瀚如海的算法世界中,有一扇门,开启后通向了有序的领域。它便是拓扑排序,这个问题的解决方法犹如一场深刻的哲学思考&#…...
【Nova UI】十六、打造组件库之滚动条组件(中):探秘滑块的计算逻辑
序言 在上篇文章中,我们完成了滚动条组件开发的前期准备工作,包括理论推导、布局规划和基础设置。现在,我们将把这些准备转化为实际代码,开启滚动条组件的具体开发之旅🌟。我们会详细阐述如何实现各项功能,…...
题海拾贝:P1833 樱花
Hello大家好!很高兴我们又见面啦!给生活添点passion,开始今天的编程之路! 我的博客:<但凡. 我的专栏:《编程之路》、《数据结构与算法之美》、《题海拾贝》、《C修炼之路》 欢迎点赞,关注&am…...
集成钉钉消息推送功能
1. 概述 本文档详细描述了在若依框架基础上集成钉钉消息推送功能的开发步骤。该功能允许系统向指定钉钉用户发送文本和富文本消息通知。 2. 环境准备 2.1 钉钉开发者账号配置 登录钉钉开发者平台:https://open.dingtalk.com/创建/选择企业内部应用获取以下关键信…...
texlive 与 Texmaker 安装
一、安装 Texmaker 1、下载Texmaker 链接地址: Texmaker (free cross-platform latex editor) 点击 FREE DOWNLOAD ,点击 Texmaker_6.0.1_Win_x64.msi ,下载即可。 2、安装Texmaker 双击如下文件 若出现如下,点击更多信息 点击仍要运行 …...
Milvus(21):过滤搜索、范围搜索、分组搜索
1 过滤搜索 ANN 搜索能找到与指定向量嵌入最相似的向量嵌入。但是,搜索结果不一定总是正确的。您可以在搜索请求中包含过滤条件,这样 Milvus 就会在进行 ANN 搜索前进行元数据过滤,将搜索范围从整个 Collections 缩小到只搜索符合指定过滤条件…...
AD PCB布局时常用的操作命令
1. 框选 往右下方框选:选中矩形接触到的对象(选中整体才会被选中) 往左上方框选:选中矩形接触到的对象(选中局部,也是选中整体) 线选:快捷键S,弹出界面: …...
[免费]微信小程序医院预约挂号管理系统(uni-app+SpringBoot后端+Vue管理端)【论文+源码+SQL脚本】
大家好,我是java1234_小锋老师,看到一个不错的微信小程序医院预约挂号管理系统(uni-appSpringBoot后端Vue管理端),分享下哈。 项目视频演示 【免费】微信小程序医院预约挂号管理系统(uni-appSpringBoot后端Vue管理端) Java毕业设计_哔哩哔哩…...
分析Docker容器Jvm 堆栈GC信息
# 打印jvm启动参数 docker exec -ti <容器名> jcmd 1 VM.flags-XX:CICompilerCount3 -XX:InitialHeapSize1073741824 -XX:MaxHeapSize2147483648 -XX:MaxMetaspaceSize157286400 -XX:MaxNewSize715653120 -XX:MinHeapDeltaBytes524288 -XX:NewSize357564416 -XX:OldSize7…...
Java——集合基础
一、集合与数组的特点对比 1.集合类的特点:提供一种存储空间可变的存储模型,存储的数据容量可以发生改变 2.集合和数组的区别 共同点:都是存储数据的容器不同点:数组的容量是固定的,集合的容量是可变的 3.如果存储…...
spark MySQL数据库配置
Spark 连接 MySQL 数据库的配置 要让 Spark 与 MySQL 数据库实现连接,需要进行以下配置步骤。下面为你提供详细的操作指南和示例代码: 1. 添加 MySQL JDBC 驱动依赖 你得把 MySQL 的 JDBC 驱动添加到 Spark 的类路径中。可以通过以下两种方式来完成&a…...
http断点续传
🛑 默认的 http.server(Python 的 SimpleHTTPRequestHandler)在某些版本和实现中并不可靠地支持 HTTP Range 请求(即断点续传)。 尤其在 Python 3.7~3.10 之间的某些版本中,这种支持是不完整或不可预测的。…...
# YOLOv3:基于 PyTorch 的目标检测模型实现
YOLOv3:基于 PyTorch 的目标检测模型实现 引言 YOLOv3(You Only Look Once)是一种流行的单阶段目标检测算法,它能够直接在输入图像上预测边界框和类别概率。YOLOv3 的优势在于其高效性和准确性,使其在实时目标检测任…...
Mac修改hosts文件方法
Mac修改hosts文件方法 在 macOS 上修改 hosts 文件需要管理员权限 步骤 1:打开终端 通过 Spotlight 搜索(Command 空格)输入 Terminal,回车打开。或进入 应用程序 > 实用工具 > 终端。 步骤 2:备份 hosts 文件…...
构建你的第一个简单AI助手 - 入门实践
在当今AI迅速发展的时代,构建自己的AI助手不再是高不可攀的技术壁垒。即使对于刚接触AI开发的程序员,也可以利用现代大语言模型(LLM)API构建功能丰富的AI助手。本文将带您完成一个简单但实用的AI助手构建过程,帮助您在日常工作中提高效率。 …...
Qt在统信UOS及银河麒麟Kylin系统中进行软件开发的环境配置,打包发布和注意事项
前述 之前由于项目的产品需要,必须将原本Windows上的产品移植到信创环境,也就是现在的主流国产操作系统统信UOS及银河麒麟Kylin。 先大概讲下信创系统: 信创系统就像是中国自己打造的 “数字基建”,目的是让咱们国家的信息技术不…...
一个完整的项目示例:taro开发微信小程序
前一周完成了一个项目,体测成绩转换的工具,没做记录,。这次计划开发一个地图应用小程序,记录一下。方便给使用的人。 一、申请微信小程序,填写相应的信息,取得开发者ID。这个要给腾讯地图使用的。 二、申…...
二次封装 el-dialog 组件:打造更灵活的对话框解决方案
文章目录 引言为什么需要二次封装?封装思路代码实现1. 基础封装组件 (Dialog.vue)2. Vue中引入使用示例 封装后的优势进阶优化建议 总结 引言 在 Vue 项目中,Element UI 的 el-dialog 是一个非常实用的对话框组件。但在实际开发中,我们经常会…...
3.2 一点一世界
第一步:引入背景与动机 “一点一世界”这个概念来源于泰勒公式的思想,即通过一个点及其导数信息来近似描述整个函数的行为。这种方法在数学分析中非常有用,因为它允许我们将复杂的函数简化为多项式形式,从而更容易进行计算和理解…...
力扣第156场双周赛
1. 找到频率最高的元音和辅音 给你一个由小写英文字母(a 到 z)组成的字符串 s。你的任务是找出出现频率 最高 的元音(a、e、i、o、u 中的一个)和出现频率最高的辅音(除元音以外的所有字母),并返…...
学习日志05 java
1 java里面的类型转换怎么做?int转double为例 在 Java 里,把int转换为double有自动类型转换和强制类型转换两种方式。下面为你详细介绍: 自动类型转换(隐式转换) 由于double的取值范围比int大,Java 能够…...
4.7/Q1,GBD数据库最新文章解读
文章题目:Burden of non-COVID-19 lower respiratory infections in China (1990-2021): a global burden of disease study analysis DOI:10.1186/s12931-025-03197-7 中文标题:中国非 COVID-19 下呼吸道感染负担(1990-2021 年&a…...
do while
先进再查 import java.util.Scanner;public class Hello {public static void main(String[] args) {Scanner in new Scanner(System.in);int number in.nextInt();int count 0;do{number number / 10;count count 1;} while( number > 0 );System.out.println(count…...
MySQL 主从复制与读写分离
一、MySQL 主从复制 (0)概述 MySQL 主从复制是一种数据同步机制,允许数据从一个主数据库(Master)复制到一个或多个从数据库(Slave)。其主要用途包括: 数据冗余与灾备:通…...
CSS3 基础知识、原理及与CSS的区别
CSS3 基础知识、原理及与CSS的区别 CSS3 基础知识 CSS3 是 Cascading Style Sheets 的第3个版本,是CSS技术的升级版本,于1999年开始制订,2001年5月23日W3C完成了CSS3的工作草案。 CSS3 主要模块 选择器:更强大的元素选择方式盒…...
第十七章:Llama Factory 深度剖析:易用性背后的微调框架设计
章节引导:在模型定制的实践中,Llama Factory (github.com/hiyouga/LLaMA-Factory) 以其惊人的易用性和对多种开源大模型、多种参数高效微调方法(PEFT)的广泛支持,迅速成为开源社区的热门选择。你可能已经熟练掌握了如何…...
SpringSecurity当中的CSRF防范详解
CSRF防范 什么是CSER 以下是基于 CSRF 攻击过程的 顺序图 及详细解释,结合多个技术文档中的攻击流程: CSRF 攻击顺序图 #mermaid-svg-FqfMBQr8DsGRoY2C {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#m…...
CSRF防范歪招
不保存到Cookie里呗 如果每次请求都强制通过请求头携带Token,并且不将Token存储在Cookie中,这种设计可以有效防御CSRF攻击。以下是具体原因和关键实现要点: 1. 防御原理 CSRF攻击的本质是攻击者伪造用户的请求,利用浏览器自动携…...
MyBatis与MyBatis-Plus深度分析
MyBatis与MyBatis-Plus深度分析 一、MyBatis原理与基础 1. MyBatis核心原理 MyBatis是一个半自动ORM框架,主要原理包括: SQL与代码分离:通过XML或注解配置SQL语句动态SQL:提供if、choose、foreach等标签实现动态SQL结果集映射…...
STM32 变量加载到flash的过程中
在STM32中,BIN文件内需要加载到RAM的数据由链接脚本(Linker Script)和启动代码(Startup Code)共同决定,具体机制如下: 一、BIN文件内容结构 STM32的BIN文件包含三类数据: Co…...