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

Spark 内存管理机制

Spark 内存管理

堆内内存和堆外内存

作为一个 JVM 进程,Executor 的内存管理建立在 JVM(最小为六十四分之一,最大为四分之一)的内存管理之上,此外spark还引入了堆外内存(不在JVM中的内存),在spark中是指不属于该executor的内存。

堆内内存:

由 JVM 控制,由GC(垃圾回收)进行内存回收,堆内内存的大小,由 Spark 应用程序启动时的 executor-memory 或 spark.executor.memory 参数配置,这些配置在 spark-env.sh 配置文件中。

堆外内存:

不受 JVM 控制,可以自由分配

堆外内存的优点: 减少了垃圾回收的工作。

堆外内存的缺点:

堆外内存难以控制,如果内存泄漏,那么很难排查
堆外内存相对来说,不适合存储很复杂的对象。一般简单的对象或者扁平化的比较适合。

堆内内存
Executor 内运行的并发任务共享 JVM 堆内内存,这些内存被规划为 存储(Storage)内存 和 执行(Execution)内存

一、Storage 内存:


用于存储 RDD 的缓存数据 和 广播(Broadcast)数据,主要用于存储 spark 的 cache 数据,例如RDD的缓存

二、Execution 内存:


执行 Shuffle 时占用的内存,主要用于存放 Shuffle、Join、Sort 等计算过程中的临时数据

三、用户内存(User Memory):


主要用于存储 RDD 转换操作所需要的数据,例如 RDD 依赖等信息

四、预留内存(Reserved Memory):


系统预留内存,会用来存储Spark内部对象。

五、剩余的部分不做特殊规划,那些 Spark 内部的对象实例,或者用户定义的 Spark 应用程序中的对象实例,均占用剩余的空间。

Spark 对堆内内存的管理是一种逻辑上的”规划式”的管理,因为对象实例占用内存的申请和释放都由 JVM 完成,Spark 只能在申请后和释放前记录这些内存。

对于 Spark 中序列化的对象,由于是字节流的形式,其占用的内存大小可直接计算,而对于非序列化的对象,其占用的内存是通过周期性地采样近似估算而得,这种方法降低了时间开销但是有可能误差较大,导致某一时刻的实际内存有可能远远超出预期。此外,在被 Spark 标记为释放的对象实例,很有可能在实际上并没有被 JVM 回收,导致实际可用的内存小于 Spark 记录的可用内存。所以 Spark 并不能准确记录实际可用的堆内内存,从而也就无法完全避免内存溢出(OOM, Out of Memory)的异常。

Spark 通过对存储内存和执行内存各自独立的规划管理,可以决定是否要在存储内存里缓存新的 RDD,以及是否为新的任务分配执行内存。
如果当前 Executor 内存不够用,可以分配到其他内存占用小的 Executor 上。
在一定程度上可以提升其他 Executor 的内存利用率,减少当前 Executor 异常的出现。

堆外内存
为了进一步优化内存的使用以及提高 Shuffle 时排序的效率,Spark 1.6 引入了堆外(Off-heap)内存,使之可以直接在工作节点的系统内存中开辟空间,存储经过序列化的二进制数据。

这种模式不在 JVM 内申请内存,而是调用 Java 的 unsafe 相关 API 进行诸如 C 语言里面的 malloc() 直接向操作系统申请内存,由于这种方式不经过 JVM 内存管理,所以可以避免频繁的 GC,这种内存申请的缺点是必须自己编写内存申请和释放的逻辑。

Spark 可以直接操作系统堆外内存,减少了不必要的内存开销,以及频繁的 GC 扫描和回收,提升了处理性能。堆外内存可以被精确地申请和释放,而且序列化的数据占用的空间可以被精确计算,所以相比堆内内存来说降低了管理的难度,也降低了误差。

在默认情况下堆外内存并不启用,可通过配置 spark.memory.offHeap.enabled 参数启用,并由 spark.memory.offHeap.size 参数设定堆外空间的大小,单位为字节。堆外内存与堆内内存的划分方式相同,所有运行中的并发任务共享存储内存和执行内存。

如果堆外内存被启用,那么 Executor 内将同时存在堆内和堆外内存,两者的使用互补影响,这个时候 Executor 中的 Execution 内存是堆内的 Execution 内存和堆外的 Execution 内存之和,同理,Storage 内存也一样。相比堆内内存,堆外内存只区分 Execution 内存和 Storage 内存。

spark内存分配


静态内存管理


在 Spark 最初采用的静态内存管理机制下,==存储内存、执行内存和其他内存的大小在 Spark 应用程序运行期间均为固定==的,但用户可以应用程序启动前进行配置,堆内内存的分配如图 所示:

可用堆内内存空间计算:

可用的存储内存 = systemMaxMemory * spark.storage.memoryFraction * spark.storage.safetyFraction可用的执行内存 = systemMaxMemory * spark.shuffle.memoryFraction * spark.shuffle.safetyFraction


堆外内存


统一内存管理
Spark 1.6 之后引入的统一内存管理机制,与静态内存管理的区别在于存储内存和执行内存共享同一块空间,可以动态占用对方的空闲区域,如图 所示

堆内
reservedMemory 在 Spark 2.2.1 中是写死的

堆外
其中最重要的优化在于动态占用机制,其规则如下:

程序提交的时候我们都会设定基本的 Execution 内存和 Storage 内存区域(通过 spark.memory.storageFraction 参数设置);

在程序运行时,如果双方的空间都不足时,则存储到硬盘;将内存中的块存储到磁盘的策略是按照 LRU 规则进行的。若己方空间不足而对方空余时,可借用对方的空间;(存储空间不足是指不足以放下一个完整的 Block)

Execution 内存的空间被对方占用后,可让对方将占用的部分转存到硬盘,然后"归还"借用的空间,Storage 占用 Execution 内存的数据被回收后,重新计算即可恢复。

Storage 内存的空间被对方占用后,目前的实现是无法让对方"归还",因为需要考虑 Shuffle 过程中的很多因素,实现起来较为复杂;而且 Shuffle 过程产生的文件在后面一定会被使用到。

Task 之间内存分布
为了更好地使用使用内存,Executor 内运行的 Task 之间共享着 Execution 内存。具体的,Spark 内部维护了一个 HashMap 用于记录每个 Task 占用的内存。

当 Task 需要在 Execution 内存区域申请 numBytes 内存,其先判断 HashMap 里面是否维护着这个 Task 的内存使用情况,①如果没有,则将这个 Task 内存使用置为0,并且以 TaskId 为 key,内存使用为 value 加入到 HashMap 里面。之后为这个 Task 申请 numBytes 内存,②如果 Execution 内存区域正好有大于 numBytes 的空闲内存,则在 HashMap 里面将当前 Task 使用的内存加上 numBytes,然后返回;③如果当前 Execution 内存区域无法申请到每个 Task 最小可申请的内存,则当前 Task 被阻塞,直到有其他任务释放了足够的执行内存,该任务才可以被唤醒。

每个 Task 可以使用 Execution 内存大小范围为 1/2N ~ 1/N,其中 N 为当前 Executor 内正在运行的 Task 个数。一个 Task 能够运行必须申请到最小内存为 (1/2N * Execution 内存);当 N = 1 的时候,Task 可以使用全部的 Execution 内存。

比如如果 Execution 内存大小为 10GB,当前 Executor 内正在运行的 Task 个数为5,则该 Task 可以申请的内存范围为 10 / (2 * 5) ~ 10 / 5,也就是 1GB ~ 2GB的范围。

示例
只用了堆内内存
现在我们提交的 Spark 作业关于内存的配置如下:
–executor-memory 18g
由于没有设置 spark.memory.fraction(Storage 和 Execution 共用内存 占可用内存的比例,默认为0.6) 和 spark.memory.storageFraction(Storage 内存占 Storage 和 Execution 共用内存 比例,默认0.5) 参数,我们可以看到 Spark UI 关于 Storage Memory 的显示如下:

上图很清楚地看到 Storage Memory 的可用内存是 10.1GB,这个数是咋来的呢?根据前面的规则,我们可以得出以下的计算:

systemMemory = spark.executor.memory
reservedMemory = 300MB
usableMemory = systemMemory - reservedMemory
StorageMemory= usableMemory * spark.memory.fraction * spark.memory.storageFraction

把数据代进去,得出结果为:5.312109375 GB。

和上面的 10.1GB 对不上。为什么呢?这是因为 Spark UI 上面显示的 Storage Memory 可用内存其实等于 Execution 内存和 Storage 内存之和,也就是 usableMemory * spark.memory.fraction

我们设置了 --executor-memory 18g,但是 Spark 的 Executor 端通过 Runtime.getRuntime.maxMemory 拿到的内存其实没这么大,只有 17179869184 字节,这个数据是怎么计算的?

Runtime.getRuntime.maxMemory 是程序能够使用的最大内存,其值会比实际配置的执行器内存的值小。这是因为内存分配池的堆部分分为 Eden,Survivor 和 Tenured 三部分空间,而这里面一共包含了两个 Survivor 区域,而这两个 Survivor 区域在任何时候我们只能用到其中一个,所以我们可以使用下面的公式进行描述:

ExecutorMemory = Eden + 2 * Survivor + Tenured
Runtime.getRuntime.maxMemory = Eden + Survivor + Tenured

用了堆内和堆外内存
现在如果我们启用了堆外内存,情况咋样呢?我们的内存相关配置如下:
spark.executor.memory 18g
spark.memory.offHeap.enabled true
spark.memory.offHeap.size 10737418240
从上面可以看出,堆外内存为 10GB,现在 Spark UI 上面显示的 Storage Memory 可用内存为 20.9GB,如下:


Spark 内存管理

凭借统一内存管理机制,Spark 在一定程度上提高了堆内和堆外内存资源的利用率,降低了开发者维护 Spark 内存的难度,但并不意味着开发者可以高枕无忧。譬如,所以如果存储内存的空间太大或者说缓存的数据过多,反而会导致频繁的 GC 垃圾回收,降低任务执行时的性能。

使用建议
首先,建议使用新模式,所以接下来的配置建议都是基于新模式的。

spark.memory.fraction:如果 application spill 或踢除 block 发生的频率过高(可通过日志观察),可以适当调大该值,这样 execution 和 storage 的总可用内存变大,能有效减少发生 spill 和踢除 block 的频率

spark.memory.storageFraction:为 storage 占 storage、execution 内存总和的比例。虽然新方案中 storage 和 execution 之间可以发生内存借用,但总的来说,spark.memory.storageFraction 越大,运行过程中,storage 能用的内存就会越多。所以,如果你的 app 是更吃 storage 内存的,把这个值调大一点;如果是更吃 execution 内存的,把这个值调小一点

spark.memory.offHeap.enabled:堆外内存最大的好处就是可以避免 GC,如果你希望使用堆外内存,将该值置为 true 并设置堆外内存的大小,即设置
spark.memory.offHeap.size,这是必须的

另外,需要特别注意的是,堆外内存的大小不会算在 executor memory 中,也就是说加入你设置了 --executor memory 10G 和 -spark.memory.offHeap.size=10G,那总共可以使用 20G 内存,堆内和堆外分别 10G。

Executor 实际内存
spark执行的时候,可以通过 spark.executor.memory 来设置executor所需的内存大小

spark.yarn.executor.memoryOverhead是executor所需的额外内存开销

默认为max(executorMemory * 0.10,最小值为384)

实际内存等于:executorMem= X+max(X*0.1,384)

var executorMemory = 1024 // 默认值,1024MB
val MEMORY_OVERHEAD_FACTOR = 0.10  // OverHead 比例参数,默认0.1
val MEMORY_OVERHEAD_MIN = 384val executorMemoryOverhead = sparkConf.getInt("spark.yarn.executor.memoryOverhead",
math.max((MEMORY_OVERHEAD_FACTOR * executorMemory).toInt, MEMORY_OVERHEAD_MIN))// 假设有设置参数,即获取参数,否则使用executorMemoryOverhead 的默认值
val executorMem = args.executorMemory + executorMemoryOverhead// 最终分配的executor 内存为 两部分的和
spark-submit --master yarn-cluster --name test --driver-memory 6g --executor-memory 6g


设置的executor-memory 大小为6g,executorMemoryOverhead为默认值,即max(6g*0.1,384MB)=612MB
那总得大小应该为6144MB+612MB=6756MB
然而实际的开销为 7168 MB

为什么?

这就涉及到了规整化因子。

规整化因子介绍
为了易于管理资源和调度资源,Hadoop YARN内置了资源规整化算法,它规定了最小可申请资源量、最大可申请资源量和资源规整化因子,

如果应用程序申请的资源量小于最小可申请资源量,则YARN会将其大小改为最小可申请量,也就是说,应用程序获得资源不会小于自己申请的资源,但也不一定相等;

如果应用程序申请的资源量大于最大可申请资源量,则会抛出异常,无法申请成功;

规整化因子是用来规整化应用程序资源的,应用程序申请的资源如果不是该因子的整数倍,则将被修改为最小的整数倍对应的值,公式为==*ceil(a/b)b==,其中a是应用程序申请的资源,b为规整化因子。

比如,在yarn-site.xml中设置,相关参数如下:

yarn.scheduler.minimum-allocation-mb:最小可申请内存量,默认是1024
yarn.scheduler.minimum-allocation-vcores:最小可申请CPU数,默认是1
yarn.scheduler.maximum-allocation-mb:最大可申请内存量,默认是8096
yarn.scheduler.maximum-allocation-vcores:最大可申请CPU数,默认是4


对于规整化因子,不同调度器不同,具体如下:
FIFO和Capacity Scheduler,规整化因子等于最小可申请资源量,不可单独配置。
Fair Scheduler:规整化因子通过参数yarn.scheduler.increment-allocation-mb和yarn.scheduler.increment-allocation-vcores设置,默认是1024和1。

通过以上介绍可知,应用程序申请到资源量可能大于资源申请的资源量,比如YARN的最小可申请资源内存量为1024,规整因子是1024,如果一个应用程序申请1500内存,则会得到2048内存,如果规整因子是512,则得到1536内存。

具体到我们的集群而言,使用的是默认值1024MB,因而最终分配的值为
ceil(6756/1024)*1024 = 7168

Client 和 Cluster 内存分配的差异
在使用Clietn 和 Cluster 两种方式提交时,资源开销占用也是不同的。
不管CLient或CLuster模式下,ApplicationMaster都会占用一个Container来运行;而Client模式下的Container默认有1G内存,1个cpu核,Cluster模式下则使用driver-memory和driver-cpu来指定;

cluster 提交命令 与 资源占用
 

spark-submit --master yarn-cluster --name testClient --driver-memory 6g --executor-memory 6g --num-executors 10


一共10个executor,加上一个am,共11个,每个都分配了7g,即7102411=78848

client 提交命令 与 资源占用
 

spark-submit --master yarn-client --name testClient --driver-memory 6g --executor-memory 6g  --num-executors 10


一共10个executor,加上一个am,共11个,每个都分配了7g,即7102411=78848

内存的申请与释放
内存的申请和释放是通过以下两个核心方法实现的:

申请内存:在 acquireExecutionMemory 或 acquireStorageMemory 中,系统根据当前的内存使用情况分配内存,并调整 executionMemoryUsed 和 storageMemoryUsed。
释放内存:当任务执行完成或缓存不再需要时,通过 releaseExecutionMemory 或 releaseStorageMemory 将内存归还给系统。

def releaseExecutionMemory(numBytes: Long, taskAttemptId: Long, memoryMode: MemoryMode): Unit = {executionMemoryUsed -= numBytes
}def releaseStorageMemory(numBytes: Long, blockId: BlockId, memoryMode: MemoryMode): Unit = {storageMemoryUsed -= numBytes
}


5.4 内存的动态扩展
        当 Execution Memory 或 Storage Memory 无法满足需求时,Spark 会尝试动态扩展内存的使用。MemoryManager 会检查其他内存池是否有未使用的内存,如果有,则可以临时借用部分内存。

        例如,在 acquireExecutionMemory 中,Spark 会首先检查是否有足够的执行内存,如果不足,则会从存储内存中回收未使用的部分:

val memoryToBorrow = math.min(availableStorageMemory, numBytes - availableExecutionMemory)
executionMemoryUsed += memoryToBorrow


        这种机制保证了 Spark 在内存不足时,能够尽量通过动态扩展来提高内存的利用率,避免因内存不足而导致任务失败。

内存回收与垃圾回收
        Spark 的内存回收机制与 JVM 的垃圾回收机制密切相关。当内存管理器检测到内存不足时,Spark 会尝试触发垃圾回收(GC),以回收未使用的对象和内存。

        Spark 内存管理器与 GC 结合紧密,特别是当执行任务时,临时对象会频繁创建并在任务结束后被回收。因此,适当的 GC 策略(如 G1、CMS)对于 Spark 应用的性能至关重要。        Spark 还提供了多种 GC 调优选项,用户可以通过调整 JVM 参数(如 -Xmx、-XX:MaxGCPauseMillis)和 Spark 参数(如 spark.memory.fraction、spark.memory.storageFraction)来优化内存使用和垃圾回收。

相关文章:

Spark 内存管理机制

Spark 内存管理 堆内内存和堆外内存 作为一个 JVM 进程,Executor 的内存管理建立在 JVM(最小为六十四分之一,最大为四分之一)的内存管理之上,此外spark还引入了堆外内存(不在JVM中的内存),在spark中是指不…...

【Maven】继承和聚合

5. Maven的继承和聚合 5.1 什么是继承 Maven 的依赖传递机制可以一定程度上简化 POM 的配置,但这仅限于存在依赖关系的项目或模块中。当一个项目的多个模块都依赖于相同 jar 包的相同版本,且这些模块之间不存在依赖关系,这就导致同一个依赖…...

NViST运行笔记

文章标题: NViST: In the Wild New View Synthesis from a Single Image with Transformers 1. 环境配置 创建环境 conda create -n nvist python3.9 进入环境 conda activate nvist 安装torch torchvision torchaudio pip install torch2.1.2 torchvision0…...

性能测试工具Grafana、InfluxDB和Collectd的搭建

一、性能监控组成简介 1、监控能力分工:这个系统组合能够覆盖从数据采集、存储到可视化的整个监控流程。Collectd可以收集各种系统和应用的性能指标,InfluxDB提供高效的时序数据存储,而 Grafana 则将这些数据以直观的方式呈现出来。2,实时性能监控:对于需要实时了解系统状…...

JS中的类与对象

面向对象是使用最广泛的一种编程范式,最具代表性的面向对象语言就是Java和C,在它们的理念中,面向对象的三大特性:封装,继承,多态。类,对象,公有/私有方法/属性,各种继承就…...

域名解析系统 DNS

1.域名系统概述 用户与互联网上某台主机通信时,必须要知道对方的IP地址。然而用户很难记住长达32 位的二进制主机地址。即使是点分十进制地址也并不太容易记忆。但在应用层为了便于用户记忆各种网络应用,连接在互联网上的主机不仅有P地址,而…...

Flutter 1.1:下载Flutter环境

1、在AS中下载Flutter插件 在setting的Plugins中下载Flutter,如图所示,可以直接进行搜索查找 2、下载flutter的sdk源代码 flutter中文文档学习 通过Git下载SDK源代码 git clone -b stable https://github.com/flutter/flutter.git3、配置系统变量 3…...

HTML5系列(6)-- 拖放 API 实战指南

前端技术探索系列:HTML5 拖放 API 实战指南 🎯 致读者:探索现代交互技术 👋 前端开发者们, 今天我们将深入探讨 HTML5 中一个强大而实用的特性 —— 拖放 API。这项技术能够让我们创建更加直观和交互性强的用户界面…...

windows下kafka初体验简易demo

这里提供了windows下的java1.8和kafka3.9.0版本汇总,可直接免费下载 【免费】java1.8kafka2.13版本汇总资源-CSDN文库 解压后可以得到一个文件夹 资料汇总内有一个kafka文件资料包.tgz,解压后可得到下述文件夹kafka_2.13-3.9.0,资料汇总内还…...

算法训练(leetcode)二刷第三十三天 | *322. 零钱兑换、*279. 完全平方数、*139. 单词拆分

刷题记录 *322. 零钱兑换*279. 完全平方数*139. 单词拆分 *322. 零钱兑换 leetcode题目地址 dp[j]存储amount为j时所需要的最少硬币数。当j为0时需要0个硬币,因此dp[0]赋值为0. 因为是取最少硬币数,因此初始化需要赋值一个最大值。 状态转移方程&…...

windows的pip镜像源配置

Windows 中 pip 镜像源配置 在 Windows 系统中,为了提高 pip 包的安装速度,我们可以配置 pip 的镜像源。以下是具体的配置步骤: 创建文件夹 在 C:\Users\Administrator\pip 路径下创建一个名为 pip.ini 的文件。 编辑 pip.ini 文件 使用文本…...

Django Rest Framework中嵌套关系的JSON序列化

在 Django Rest Framework (DRF) 中,处理嵌套关系的 JSON 序列化是一个常见需求。以下是如何实现嵌套关系序列化的详细说明,包括序列化器定义、模型关系以及常见用法。 1、问题背景 假设我们有以下两个模型: class Jobdtl(models.Model):jo…...

ONVIF协议网络摄像机客户端使用gsoap获取RTSP流地址GStreamer拉流播放

什么是ONVIF协议 ONVIF(开放式网络视频接口论坛)是一个全球性的开放式行业论坛,旨在促进开发和使用基于物理IP的安全产品接口的全球开放标准。 ONVIF规范的目标是建立一个网络视频框架协议,使不同厂商生产的网络视频产品完全互通。…...

40分钟学 Go 语言高并发:Go程序性能优化方法论

Go程序性能优化方法论 一、性能指标概述 指标类型关键指标重要程度优化目标CPU相关CPU使用率、线程数、上下文切换⭐⭐⭐⭐⭐降低CPU使用率,减少上下文切换内存相关内存使用量、GC频率、对象分配⭐⭐⭐⭐⭐减少内存分配,优化GC延迟指标响应时间、处理延…...

MySQL基础(语句)知识复习 (除索引和视图)

1.客户端和数据库操作 1.登录客户端界面:mysql -uroot -p 2.查看当前的数据库版本:select version(); 3.显示所有数据库:show databases;, 4.创建数据库:create [IF NOT EXISTS] database 库名 character set 字符…...

【sqlcipher】pc端sqflite使用过程中遇到的问题

在flutter中使用sqlcipher时 Mac上如果通过flutter带的文件管理api(即File的delete()方法)删除数据库文件,再创建同名的数据文件的话,必现readonly问题, 这里需要注意的一点是 DatabaseFactory 在Mac上直接使用全局的…...

Vue 实现无线滚动效果

目录 1.Element-plus官网中的Infinite Scroll组件说明 2.滚动条设置 3.滚动到底部的函数调用 1.Element-plus官网中的Infinite Scroll组件说明 官网链接如下所示: Infinite Scroll 无限滚动 | Element Plus 首先查看该代码,发现这个组件使用了一个…...

【CSS in Depth 2 精译_062】第 10 章 CSS 中的容器查询(@container)概述 + 10.1 容器查询的一个简单示例

当前内容所在位置(可进入专栏查看其他译好的章节内容) 【第十章 CSS 容器查询】 ✔️ 10.1 容器查询的一个简单示例 ✔️ 10.1.1 容器尺寸查询的用法 ✔️ 10.2 深入理解容器10.3 与容器相关的单位10.4 容器样式查询的用法10.5 本章小结 文章目录 第 10…...

conda手动初始化

问题:环境中存在conda但是conda无法使用 方法: 进入到anaconda目录下, 进入bin目录, 然后执行 source activate要想启动时自动进入conda环境, 需要在 ~/.bashrc中添加如下命令 # >>> conda initialize >>> # !! Contents within this block are managed by …...

hhdb数据库介绍(10-28)

管理 管理菜单主要囊括对业务数据进行管理的功能,例如对数据的备份恢复或执行业务表的DDL语句等操作。 数据对象 数据对象功能可以帮助用户通过列表实时查看当前已存在的数据对象,了解业务数据的整体情况。提供了对数据对象的筛选、统计、关联、详情等…...

Spring Boot自定义启动banner

在启动 Springboot 应用时,默认情况下会在控制台打印出 Springboot 相关的banner信息。 自定义banner 如果你想自定义一个独特的启动banner,该怎么做呢?Springboot 允许我们通过自定义启动banner来替换默认的banner。只需要在 resources 目…...

c语言——数组名该如何理解呢?

一般情况下,数组名表示首元素地址,以下2种除外: ①、sizeof(数组名) 表示整个数组 ※只有数组名的情况 sizeof(数组名i) 就不能表示整个数组 ②、&数组名 表示整个数组,取的是整个数…...

前端 如何用 div 标签实现 步骤审批

在前端实现一个步骤审批流程,通常是通过 div 标签和 CSS 来构建一个可视化的流程图,结合 JavaScript 控制审批的状态变化。你可以使用 div 标签创建每一个步骤节点,通过不同的样式(如颜色、边框等)表示审批的不同状态&…...

QT工程,它该怎么学?

在现代软件开发中,QT因其强大的跨平台能力和友好的用户界面设计工具,成为开发者学习和应用的热门选择。特别是在Linux系统下,如何安装、配置QT开发环境,以及创建和管理QT工程是入门QT开发的关键环节。本文将从安装QT开发环境开始&…...

第426场周赛:仅含置位位的最小整数、识别数组中的最大异常值、连接两棵树后最大目标节点数目 Ⅰ、连接两棵树后最大目标节点数目 Ⅱ

Q1、仅含置位位的最小整数 1、题目描述 给你一个正整数 n。 返回 大于等于 n 且二进制表示仅包含 置位 位的 最小 整数 x 。 置位 位指的是二进制表示中值为 1 的位。 2、解题思路 我们需要找到一个整数 x,使得: x ≥ nx 的二进制表示中仅包含置位…...

23种设计模式之外观模式

目录 1. 简介2. 代码2.1 SelectFoodService (选择食品)2.2 PayService (支付服务)2.3 TakeService (制作服务)2.4 OrderService (下单服务)2.5 Food (食品)2.6 TackingSystem (外观类)2.7 Test (测试类) 3. 优缺点3. 总结 1. 简介…...

【智商检测——DP】

题目 代码 #include <bits/stdc.h> using namespace std; const int N 1e510, M 110; int f[N][M]; int main() {int n, k;cin >> n >> k;for(int i 1; i < n; i){int x;cin >> x;f[i][0] __gcd(f[i-1][0], x);for(int j 1; j < min(i, k)…...

LeetCode-430. 扁平化多级双向链表-题解

题目链接 430. 扁平化多级双向链表 - 力扣&#xff08;LeetCode&#xff09; 题目介绍 你将得到一个双链表&#xff0c;节点包含一个“下一个”指针、一个“前一个”指针和一个额外的“子指针”。这个子指针可能指向一个单独的双向链表&#xff0c;并且这些链表也包含类似的特殊…...

【CSS】一篇掌握CSS

不是因为有了希望才去坚持,而是坚持了才有了希望 目录 一.导入方式 1.行内样式 2.内部样式 3.外部样式(常用) 二.选择器 1.基本选择器(常用) 1.1标签选择器 1.2类选择器 1.3id选择器 2.层次选择器 2.1后代选择器 2.2子选择器 2.3相邻兄弟选择器 2.4通用兄弟选择器…...

华为仓颉编程环境搭建

1、仓颉介绍 摘自华为官方&#xff1a;仓颉编程语言作为一款面向全场景应用开发的现代编程语言&#xff0c;通过现代语言特性的集成、全方位的编译优化和运行时实现、以及开箱即用的 IDE 工具链支持&#xff0c;为开发者打造友好开发体验和卓越程序性能。 其具体特性表现为&am…...

手机实时提取SIM卡打电话的信令声音-蓝牙电话如何适配eSIM卡的手机

手机实时提取SIM卡打电话的信令声音 --蓝牙电话如何适配eSIM卡的手机 一、前言 蓝牙电话的海外战略中&#xff0c;由于海外智能手机市场中政策的差异性&#xff0c;对内置eSIM卡的手机进行支持是非常合理的需求。Android系列手机中&#xff0c;无论是更换通信运营商&#xf…...

三种方式(oss、本地、minio)图片的上传下载

一、OSS 1、前期准备 1.1 注册阿里云账号&#xff0c;开启对象存储oss功能&#xff0c;创建一个bucket&#xff08;百度教程多的是&#xff0c;跟着创建一个就行&#xff0c;创建时注意存储类型是标准存储&#xff0c;读写权限是公共读&#xff09; 有的在创建桶时读写属性是…...

使用pyQT完成简单登录界面

import sysfrom PyQt6.QtGui import QMovie,QPixmap from PyQt6.QtWidgets import QApplication, QWidget, QLabel, QPushButton,QLineEdit#封装我的窗口类 class MyWidget(QWidget):#构造函数def __init__(self):#初始化父类super().__init__()# 设置窗口大小self.resize(330,…...

Postgres数据库自动化分区

一.创建自动化分区配置表并插入数据 -- Table: managerdb.par_info-- DROP TABLE IF EXISTS managerdb.par_info;CREATE TABLE IF NOT EXISTS managerdb.par_info (table_schema character varying(255) COLLATE pg_catalog."default" NOT NULL,table_name characte…...

【技术介绍】C++编程语言中的瑰宝

C&#xff0c;这门源于C语言并在其基础上进行大幅增强的编程语言&#xff0c;自诞生以来便以其独特的魅力和强大的功能吸引了无数编程者的目光。它不仅是计算机科学领域的一颗璀璨明珠&#xff0c;更是现代软件开发中不可或缺的重要工具。 解析【前言】 C的命名&#xff0c;寓…...

nginx反向代理

目录 环境准备 启动HTTP服务 配置Nginx 访问 部署 1.配置nginx 2.自动化脚本 3.执行脚本 4.使用ansible 什么是反向代理呢&#xff0c;参考nginx反向代理&#xff0c;业务部署过长中&#xff0c;常遇到的场景如下&#xff0c;通过访问域名/ip地址&#xff0c;后面接入网…...

分层图最短路

常见情形&#xff1a; 对于边有k次操作的题。。 整体思想&#xff1a; 分层图最短路可以视作是dijkstra的一个扩展&#xff0c;通常用于处理N小于10000&#xff0c;或者是k不大的情形。整体有点类似于拆点。将一个点拆成k个点处理。层与层之间互不影响。 好了我就说这么多&…...

FRU文件

FRU&#xff08;Field Replaceable Unit&#xff09;源文件的格式通常遵循IPMI FRU Information Storage Definition标准。在实际应用中&#xff0c;FRU源文件可以是JSON格式的&#xff0c;这种格式允许用户指定所有的FRU信息字段。以下是FRU源文件的JSON格式的一些关键点&…...

兔子繁衍问题

7-2 兔子繁衍问题 分数 15 全屏浏览 切换布局 作者 徐镜春 单位 浙江大学 一对兔子&#xff0c;从出生后第3个月起每个月都生一对兔子。小兔子长到第3个月后每个月又生一对兔子。假如兔子都不死&#xff0c;请问第1个月出生的一对兔子&#xff0c;至少需要繁衍到第几个月时兔…...

飞凌嵌入式受邀亮相OpenHarmony人才生态大会2024

2024年11月27日&#xff0c;OpenHarmony人才生态大会2024在武汉洲际酒店举行。在这场汇聚了行业精英、技术大咖及生态伙伴的年度盛会上&#xff0c;飞凌嵌入式作为OpenHarmony社区的重要成员受邀出席&#xff0c;并展示了其在OpenHarmony 4.1系统适配方面的最新成果。 在大会的…...

Resrful控制器

Linux Debian 包管理器 apt DebianUbuntuKali红帽子 包管理器dnf或者yum RHELFedroaCentos Stream RHEL上游版本&#xff0c;就是什么新的内容、特性会在这个上面进行测试 运行 运行页面--dotnet blog.dll配置管理 server{listen 80;server_name m.域名;location / {proxy_p…...

Python练习(2)

重复元素判定续。利用集合的无重复性来编写一个程序如果有一个元素出现了不止一次则返回true但不要改变原来列表的值&#xff1a; 一&#xff1a; def has_duplicates(lst): # 使用集合来存储已经见过的元素 seen set() for item in lst: if item in seen: # 如果元素已经在…...

Qt清空文件夹下的内容

Qt清空文件夹下的内容 你可以使用 QDir 类来清空文件夹下的所有内容。以下是一个示例&#xff0c;展示了如何删除指定文件夹中的所有文件和子文件夹&#xff1a; #include <QCoreApplication> #include <QDir> #include <QFileInfoList> #include <QDeb…...

如何手动设置ubuntu服务器的ip、子网掩码、网关、DNS

在 Ubuntu 服务器上手动设置 IP 地址、子网掩码、网关和 DNS&#xff0c;通常有两种方式&#xff1a;使用传统的 ifconfig 命令和配置文件&#xff0c;或者使用现代的 netplan 配置方式&#xff08;对于 Ubuntu 17.10 及以后版本&#xff0c;netplan 是默认的网络配置工具&…...

单片机状态机实现多个按键同时检测单击、多击、长按等操作

1.背景 在之前有个项目需要一个或多个按键检测&#xff1a;单击、双击、长按等操作 于是写了一份基于状态机的按键检测&#xff0c;分享一下思路 2.实现效果 单击翻转绿灯电平 双击翻转红灯电平 长按反转红绿灯电平 实现状态机检测按键单击&#xff0c;双击&#xff0c;长…...

graph rag都能做哪些事情

从提供的项目目录结构看&#xff0c;系统具备高复杂度和模块化的设计&#xff0c;可能用于大规模数据处理、知识图谱构建、自然语言处理等方面。以下是一些推理出的核心能力和应用场景&#xff1a; 1. 核心模块能力&#xff1a; API 层 (api) 主要用于对外接口的定义和服务调…...

Linux 用户和用户组管理

Linux 用户和用户组管理 Linux系统是一个多用户多任务的分时操作系统&#xff0c;任何一个要使用系统资源的用户&#xff0c;都必须首先向系统管理员申请一个账号&#xff0c;然后以这个账号的身份进入系统。 用户的账号一方面可以帮助系统管理员对使用系统的用户进行跟踪&…...

Python酷库之旅-第三方库Pandas(251)

目录 一、用法精讲 1186、pandas.tseries.offsets.BusinessMonthEnd.is_year_start方法 1186-1、语法 1186-2、参数 1186-3、功能 1186-4、返回值 1186-5、说明 1186-6、用法 1186-6-1、数据准备 1186-6-2、代码示例 1186-6-3、结果输出 1187、pandas.tseries.offs…...

利用Ubuntu批量下载modis图像(New)

由于最近modis原来批量下载的代码不再直接给出&#xff0c;因此&#xff0c;再次梳理如何利用Ubuntu下载modis数据。 之前的下载代码为十分长&#xff0c;现在只给出一部分&#xff0c;需要自己再补充另一部分。之前的为&#xff1a; 感谢郭师兄的指导&#xff08;https://blo…...

Redis分布式锁

一、全局ID生成器 1.1 概念 全局ID生成器&#xff0c;是一种在分布式系统下用来生成全局唯一ID的工具。具有以下特点&#xff1a; &#xff08;&#xff11;&#xff09;唯一性&#xff1b;&#xff08;&#xff12;&#xff09;高可用&#xff1b;&#xff08;&#xff13;&…...