C++内存管理机制(侯捷)笔记3

C++内存管理机制(侯捷)

本文是学习笔记,仅供个人学习使用。如有侵权,请联系删除。

参考链接

Youtube: 侯捷-C++内存管理机制

Github课程视频、PPT和源代码: https://github.com/ZachL1/Bilibili-plus

第三讲:malloc和free

文章目录

  • C++内存管理机制(侯捷)
    • 32 VC6和VC10的malloc比较
    • 33 VC6内存分配(1)
    • 34 VC6内存分配(2)
    • 35 VC6内存分配(3)
    • 36 VC6内存分配(4)
    • 37 VC6内存分配(5)
    • 38 SBH行为分析 分配+释放之连续动作图解(1)
    • 39 SBH行为分析 分配+释放之连续动作图解(2)
    • 40 SBH行为分析 分配+释放之连续动作图解(3)
    • 41 SBH行为分析 分配+释放之连续动作图解(4)
    • 42 VC6内存管理free(p)(上)
    • 43 VC6内存管理总结(上)
    • 44 VC6内存管理总结(下)
    • 后记

32 VC6和VC10的malloc比较

VC6 内存分配

SBH:Small Block Heap

下图是call stack,调用栈,从下往上看。

mainCRTStartup函数是CRT(C Run Time,C标准库)提供的入口点函数 ,调用一系列函数,后面才是调用main函数。

_heap_alloc_base函数,它里面当size小于一个阈值_sbh_thrshold时调用__sbh_alloc_block函数,否则调用HeapAlloc函数,后者是操作系统提供的内存分配函数。

在这里插入图片描述

在 VC6(Visual C++ 6.0)的内存分配机制中,SBH(Small Block Heap)是用于管理小块内存的一部分。这是一个专门用于分配和释放相对较小内存块的堆管理机制,通常用于提高小对象的内存分配效率。

在调用栈中,从下往上看,mainCRTStartup 函数是 CRT(C Runtime)提供的入口点函数。CRT 是 C++ 程序运行时环境的一部分,负责初始化和管理程序的运行时状态。

mainCRTStartup 函数会执行一系列的初始化操作,包括初始化全局变量、调用构造函数等。在这个过程中,可能会涉及到内存分配操作,其中就包括 SBH 的管理。

在 VC6 中,SBH 通常使用一些数据结构(例如内存池、free list 等)来管理小块内存。这有助于减少内存碎片,并提高小对象的分配和释放效率。

整个调用栈的过程可能是这样的:

  1. mainCRTStartup 函数初始化 CRT 环境。
  2. 在初始化过程中,可能会涉及到 SBH 的初始化或使用。
  3. 然后执行 main 函数,开始程序的主要逻辑。

总的来说,VC6 的内存分配机制在运行时可能会使用 SBH 等机制来管理小块内存,以提高性能和效率。这些机制通常是底层的、对开发者透明的,但在整个程序运行的过程中发挥着重要的作用。

VC10内存分配

下图中黑色覆盖的函数表示VC10不再使用,对于_heap_alloc_base函数,它里面直接调用HeapAlloc函数,不再对小块内存进行管理,统统交给操作系统来做。对于VC10版本,它的SHB等小块内存的管理都被包装到HeapAlloc里面来了。

在这里插入图片描述

SBH开始 _heap_init__sbh_heap_init函数

_heap_init里面调用HeapCreate来分配一块大小为4096的堆空间,命名为_crtheap,后面CRT的动作都要从这一块内存中来拿。

_heap_init里面调用__sbh_heap_init,里面时HeapAlloc,从_crtheap中拿内存,准备好16个header。

所以 _heap_init的作用就是准备好16个header。

在这里插入图片描述

看一下header的结构

typedef unsigned int BITVEC;
typedef struct tagHeader
{
    BITVEC bitvEntryHi; // 32位
    BITVEC bitbEntryLo;  // 32位
    BITVEC bitvCommit;  // 32位
    void* pHeapData;
    struct tagRegion* pRegion;
}
HEADER, *PHEADER;

在这里插入图片描述

33 VC6内存分配(1)

看一下_ioinit函数,这是跟I/O相关的初始化,里面调用了_malloc_crt进行内存分配,这是CRT进行的第一次内存分配,看下图的右下角,可以看到分配的大小为32 x 8 = 256B,所有的程序一进来都是分配256B。256在十六进制下是0x100,或者写成100H。

在这里插入图片描述

看右侧的define,其实就是调用了_malloc_dbg,这个和malloc稍微有所不同。

_malloc_dbg 是与调试相关的内存分配函数,它是 Microsoft Visual C++ 提供的一种扩展版本,用于在调试模式下进行内存分配,并提供额外的调试信息。与标准的 malloc 函数相比,_malloc_dbg 主要用于在调试期间更容易跟踪内存分配和释放的情况。

以下是一些与 _malloc_dbg 相关的特点:

  1. 调试信息: _malloc_dbg 在分配的内存块中附加了调试信息,包括分配的源代码文件、行号等。这样在调试时,可以更轻松地追踪内存泄漏或其他内存相关的问题。

  2. 调试版本: _malloc_dbg 通常在调试版本的程序中使用,而在发布版本中,可能使用标准的 malloc。这种区分有助于减小发布版本的二进制大小,并提高性能。

  3. 额外的参数: _malloc_dbg 可能接受比标准 malloc 更多的参数,以提供更多的调试信息。例如,可以指定分配的内存块的类型、标志等。

示例用法可能如下所示:

#include <crtdbg.h>

// ...

void* ptr = _malloc_dbg(size, _NORMAL_BLOCK, __FILE__, __LINE__);

在这个例子中,_malloc_dbg 用于分配带有调试信息的内存块。_NORMAL_BLOCK 表示内存块的类型,__FILE____LINE__ 分别表示调用该函数的源文件和行号。这有助于在调试期间追踪内存的分配和释放情况。

_nh_malloc_dbg这里没有要讲的内容,

继续往里看,调用_heap_alloc_dbg,里面右下角计算blockSize的时候,_CrtMemBlockHeader是一个结构体,可以称为debug header,nSize就是上文提到的256B,后面的nNoManLandSize是4。

右侧的图显示了debug模式下申请nsize=256B大小内存,额外附加了一些东西,debug header和NoMansLand,这是为调试器设计的。

blockSize计算完毕之后,开始调用_heap_alloc_base分配内存空间。

是要要的内存nsize部分加上调试所加的部分,这个整体称为block,就是下图右侧所示的整体,由灰色、深绿色、浅绿色共同构成。

在这里插入图片描述

下页还是_heap_alloc_dbg函数的内容,里面多了两根指针,_pFirstBlock_pLastBlock两根指针指向block链表的头尾。

malloc分配的内存块都用链表串起来。

右下角的memset是给特定地方填入特定的值

在这里插入图片描述

34 VC6内存分配(2)

调用_heap_alloc_base分配内存,小于阈值的内存交给sbh服务,大于阈值的内存交给操作系统HeapAlloc来服务。

这里_Sbh_threshold的值是1016,这是因为还没有加cookie(大小为8),两者加起来是1024B。

在这里插入图片描述

继续往下,调用_sbh_alloc_block函数,里面的动作是这样的:

在前面block的大小基础上,上下添加cookie(体现在2*sizeof(int),以及下图右侧上下两块红色的地方0x131),后面涉及到的(BYTE_PER_PARA-1) 是进行向上调整ROUND_UP,调整到16的倍数。

下面对下图的cookie计算给出具体的步骤:

首先是_ioinit首次需要的内存256B(0x100,浅绿色的部分),然后是调试器加的debug header大小为 9 x 4 = 36B(0x24,灰色部分和深绿色部分(4个0xfd)),再加上下两个cookie大小4 x 2 = 8B(0x8),所有的加起来:0x100 + 0x24 + 0x8 = 0x12C,向上调整到16的倍数,变成0x130,所以cookie应该填的值是0x130。那为什么图中显示的是0x131呢?因为调整为16的倍数之后,这个十六进制数的二进制表示后四位全是零,采用最后一位另作他用,如果最后一位是1表示这块内存分配出去,如果最后一位是0表示这块内存还在sbh手上。这里是分配出去的内存,所以最后一位是1,因此cookie 里面填的值是0x131。

在这里插入图片描述

35 VC6内存分配(3)

前面介绍的函数都是确定该分配内存的大小,下面介绍_sbh_alloc_new_region

16个header,每一个header负责1MB的内存。

header有两个指针,一个指向真正的内存,另一个指向管理中心(region),下面橙色框圈出来的就是new region,具体细节如下

typedef struct tagRegion
{
	int indGroupUse; // 一个整数
	char cntRegionSize[64]; // 64个char
    // 下面两者合并,共有32组,每组64bits,用来管理哪些区块在链表中存在与否等细节
	BITVEC bitvGroupHi[32];  // unsigned int
	BITVEC bitvGroupLo[32];
	struct tagGroup grpHeadList[32];  // 32个group
}
REGION, *REGION;

每一个group是64个ListHead

typedef struct tagGroup
{
	int cntEntries; // 记录累计分配出去的区块
	struct tagListHead listHead[64];  // 64个ListHead
}
GROUP, *PGROUP;

ListHead是什么,里面有两根指针,双向链表

typedef struct tagListHead
{
	struct tagEntry* pEntryNext;
	struct tagEntry* pEntryPrev;
}
LISTHEAD, *PLISTHEAD;	

一个region的大小大概有16K左右。为了管理右侧的虚拟地址空间,它的成本是region(管理中心)的大小,16K。

在这里插入图片描述

36 VC6内存分配(4)

如何从1MB内存中切出一块

现在进入到_sbh_alloc_new_group这个函数的分析

将右侧的虚拟内存空间(大小为1MB),分成32块。每一块大小为1MB / 32 = 32KB。

然后这每一块再细分为8个page,每个page大小为32KB / 8 = 4KB,如下图page1, page2, …, page8所示

第一块由group0进行管理。group0里面有64条链表。SBH中用链表把第一块的8个page串起来,挂在group0里面64根链表的最后一根上。

每一个group管理一块。

在这里插入图片描述

_ioinit第一次来要内存的时候,就从group0的page1挖一块给它。后面又有要内存的时候,就一直往后挖,如果page1到page8都被分配出去了,之后还是要内存,就到group1中去处理。

SBH向操作系统要内存的时候,一开始并不是1MB,而是一个块32KB。

看一下page1到page8是怎么被切割出来的

这32KB(8个page)是一次性从操作系统分配过来的。每一个page的偏移值地方设置为0xffffffff也就是-1(下图中黄色的部分),设置为-1的作用是合并的时候做分隔符(栅栏),分隔符(栅栏)之内的合并在一起。

下图中红色的部分,有三个小块,下面两个红色的小块是两个指针,将8个page串起来,上面的一个红色小块是记录可用空间的大小,这里是4080(由4KB = 4096B,4096减去两个黄色的部分(栅栏,分隔符)8B,剩下4088B,但是要下调到16的倍数,变成4080B,剩余的放到保留区),这上下两块4080是cookie,记录自己这一块的大小。

64条链表负责不同大小的区块,分别是16B, 32B, 64B,…, 每次增加16B,一直到最后一根链表,最后一根应该负责64 x 16 = 1024B的区块分配。另外最后一根链表还有一个任务,就是所有大于1024B的区块都由它负责,当切分完之后如果剩下的空间小于1024B,就要挂载到对应区块大小的那根链表上。

这64根链表上面还有一个整数cntEntries,表示分配的累积量,分配出去一个区块就+1,回收回来一个区块就-1.

在这里插入图片描述

37 VC6内存分配(5)

下面分析一下第一个page怎么切分

4080 = 0xff0

上面_ioinit第一次要的内存是256B(0x110),然后加上各种debug header和其他,总共是0x130,所以给出去的内存是0x130,cookie记录的值是0x131。

剩下的大小为0xff0 - 0x130 = 0xec0

下图左侧红色的地址0x007d0ed0是传出去的指针,指向的是客户要的0x130大小(加上各种debug header等)的内存。然后0x130内部还要调整指针,指向实际要的大小0x100大小的位置,就是下图中间的纯绿色(fill 0xcd)的位置。这就是_ioinit获得的空间的位置。

下图右侧的_NORMAL_BLOCK_CRT_BLOCK指的是不同类型的block,_NORMAL_BLOCK是main函数里面具体用的block,它在main函数结束的时候应该全部被归还,否则就是内存泄漏;而_CRT_BLOCK在main函数运行结束之后还会存在,它会由CRT进行释放。

在这里插入图片描述

38 SBH行为分析 分配+释放之连续动作图解(1)

首次需求是由ioinit.c第81行代码发出,申请100H的空间,加上各种debug header,它的区块大小变成130H(十进制是304),应该由64条链表中的第304 / 16 - 1 = 18号链表进行供应(不同链表区块大小是16的倍数)。但是前63条链表都为空,只有最后一条(#63)有空间。下面就是以最后一条链表(#63)来讲解。

SBH面对这样的需求,它在初始化的时候已经有16个header,现在0号header来进行处理。

1.它首先分配1MB的地址空间,这个动作是由VirtualAlloc去拿到的

p = VirtualAlloc(0, 1MB, MEM_RESERVE, ...)

这段代码使用了 VirtualAlloc 函数,该函数是 Windows API 提供的用于虚拟内存操作的函数之一。在这里,VirtualAlloc 用于分配 1MB 的地址空间,并且使用 MEM_RESERVE 标志表示要保留这个地址空间,而不分配物理内存。

让我们解释一下这个调用:

p = VirtualAlloc(0, 1MB, MEM_RESERVE, ...);
  • 0:表示欲分配或保留的内存区域的起始地址。在这里,设置为 0,表示让系统决定分配的地址。
  • 1MB:表示要分配或保留的内存区域的大小,这里是 1MB。
  • MEM_RESERVE:表示要保留而不是分配物理内存。这样做可以预留地址空间,但只有在访问这些地址空间时才会分配物理内存。
  • ...:其他参数,这里没有提供具体的细节。

所以,这个调用的目的是在虚拟地址空间中保留 1MB 的地址区域,但实际上并没有分配物理内存。这样的操作通常用于预留地址空间,以便在需要时再分配实际的物理内存。

2.其次,header0有另外一根指针分配出region,这个动作是由HeapAlloc进行

HeapAlloc(_crtheap, sizeof(REGION));

这个region里面就是上文介绍的,里面有一些bit,还有32个group,每个group有64条链表。

上述动作准备好之后,要从虚拟地址空间中分配32KB(被分成8个page,每个page大小为4KB),8个page由指针串起来,这次内存分配是用VirtualAlloc进行的

VirtualAlloc(addr, 32KB, MEM_COMMIT, ...) // MEM_COMMIT表示真的分配内存

万事俱备,开始在page1上分配刚开始的需求:申请的100h,区块大小130h,十进制大小4080剩下的大小为0xff0 - 0x130 = 0xec0,这部分还在SBH控制之中,130h被分配出去,所以cookie记为131h。

在这里插入图片描述

下面红色方框中是32组64bits,64bits分别对应64根链表的状态,哪一条链表有挂区块,对应的bit就设置为1。32组表示的是32个group

在这里插入图片描述

第二次需求,这个需求是CRT里面谁发出来的需求呢?是上面讲的call stack中的__crtGetEnvironmentStringsA()发出的。

这次需求是且分出240H的大小(包含各种debug header,调整16的边界等之后的大小),这个240h的区块应该由哪条链表提供服务呢?240h = 576d(d表示十进制),576 / 16 -1 = 35, 所以由#35号链表提供服务。然后去检查64bits中35号对应的bit,看看是否挂有区块,这里的情况是#35链表是空的。 然后退而求其次逐渐遍历更大容量的链表,这里只能找到最大的那条链表,这里最后一条是#63(从0开始编号)。

和前面一样,检查#63链表发现它有8个page,page1还有空间可用。从这里切出240h的大小,经过两次切割之后,page1还剩c80h大小,ec0h - 240h = c80h

在这里插入图片描述

第三次分配70h的大小

首先先检查应该是几号链表服务刚刚好?这里是 70h = 112D, 112 / 16 - 1 = 6, 应该由6号链表服务,但是它是空的,往上寻找只发现最后一个链表有区块。

page1继续分配空间,这次分配之后还剩下c80h - 70h = c10h

在这里插入图片描述

39 SBH行为分析 分配+释放之连续动作图解(2)

上面是分析内存分配的情况,下面分析一下内存回收的阶段。

下图是第15次的动作,它前面有14次内存分配,这次是内存释放(回收),右上角可以看到cntEntries由14变成13,内存释放会-1.

这次释放的是大小为240h的区块,这一块应该回收到64根链表中的哪一根呢?240h = 576D, 576 / 16 - 1 = 35,所以应该还到#35号链表。由于分配出去的cookie为241h,现在将其变为240h,就表示回收回来,在SBH的掌控之下。然后64bits中第35号bit需要由0变成1

在这里插入图片描述

40 SBH行为分析 分配+释放之连续动作图解(3)

下图是第16次的动作,还是内存分配的动作

这次分配的是b0h,应该由哪条链表来服务呢?b0h = 176D, 176 / 16 - 1 = 10,所以应该由#10号链表服务,但是它是空的。此时需要向右寻求拥有更大区块的链表的帮助,这里从#10号往右逐个查找,发现上次回收了回来第#35号链表,它是可用的,所以这次应该由#35号链表提供服务。

上次刚回收回来240h,分配出去b0h,这块空间还剩多大?240h - b0h = 190h

这里的190h,应该挂到哪条链表呢? 190h = 400D, 400 / 16 - 1= 24,所以应该挂到#24号链表。此时64bits中的24号bit需要变成1,表示该号链表有区块可分配。

在这里插入图片描述

一直进行下去,不断的进行内存分配和回收。

group1共有32B,

下面的第一行表示的就是group1的64条链表的使用情况:

02000014 00000000H // 共64bits,表示的是第几号链表时候有区块

展开成二进制,发现有3个链表挂有区块,有可用空间供分配。

现在要分配的大小为230h,上面的group1中的可用链表都不能满足它的需求

现在用的是group2,对于group2中,230h应该由几号链表来服务呢? 230h = 560D, 560 / 16 - 1 = 34,理想的状况是由34号链表服务,它检查下面的表示链表状态的64bits,

这里第二行

00000000 00000001H // 表示只有最后一条链表有可用空间供分配

表示只有最后一条链表有可用空间供分配,最后一条链表的编号为#63, 每个大小page还是4080D = ff0H。

现在ff0H分配出去230H,还剩 ff0h - 230h = dc0h,dc0h应该挂在哪个链表上呢?dc0h = 3520D,表示空间大小为3520B,比前63条链表的区块(小于1024B)还要大,它只能还挂在#63号链表上。

在这里插入图片描述

41 SBH行为分析 分配+释放之连续动作图解(4)

VC6内存管理:区块的合并

如果回收的是相邻的空间,是不是应该合并呢?

这里的上cookie表示的是上面的cookie,下cookie表示的是下面的cookie。

下图左侧第一张图中灰色部分表示待收回的区块300h,它的上下两部分为白色,表示已经回收过来的区块,可以合并。

首先往下看,怎么往下看呢?

指针找到自己的cookie大小,这里是300h,指针移动300h,就到了下面一个区块的cookie位置,看最后1bit是否是为0,如果为0,表示可以和下面的区块合并。

现在发现,下方区块为free,也为300h,合并之,合并为600h,如第二张图中间灰色部分所示。

总之,往下合并,用的就是上cookie,根据上cookie的大小,指针移动cookie个大小,就可以找到下一个区块的位置。

其次往上看,怎么往上看呢?

指针还在自己cookie的位置,往上移动4字节,就找到上方区块的下cookie,判断最后1bit是否为0,若为0,就表示可以和上面的区块合并。

现在发现,上方区块也为free,大小也为300h,合并之。

总之,往上合并,用的是上面区块的下cookie,根据这个值,往上跳cookie个大小,找到上面区块的起始位置。如果没有下cookie,就不能往上合并。

三个300h合并大小为900h,然后去找900h应该挂在几号链表上,900h = 2304D, 2304大于1024,所以它应该挂在最后一个链表#63上,它用来处理大于1024B的区块。

在这里插入图片描述

42 VC6内存管理free(p)(上)

free回收,SBH要确定落在哪个header(共16个header)指定的1MB空间中,然后确定是这个header中的哪个group,然后确定这个group中的64条链表中的哪个链表。

在这里插入图片描述

43 VC6内存管理总结(上)

分成16个header,每个header管理1MB的虚拟空间,这个虚拟空间分成32个group(每个group管理大小为32KB的空间),每个group里有64个链表。

这里的管理是分段管理(一段是32KB),分段的时候便于一段全部回收,然后还给操作系统。

如何判断全回收?

因为每个group中都有一个cntEntries,统计分配和回收的区块数量,当它为0的时候,意味着这个group全回收,这一段32KB就可以还给操作系统。

typedef struct tagGroup
{
	int cntEntries; // 记录累计分配出去的区块
	struct tagListHead listHead[64];  // 64个ListHead
}
GROUP, *PGROUP;

在这里插入图片描述

cntEntries = 0的时候,这些区块是什么样子呢?它们已经进行了合并,合并到初始状态,初始状态是什么呢?就是8个page分别挂载4080B那个状态,如下图所示,然后挂在#63号链表上。

在这里插入图片描述

不急着还给操作系统,有一个defering,延缓归还的操作。

有一个全回收的group时,先暂存,当有第二个全回收的group时,才释放前面那个group。

在这里插入图片描述

44 VC6内存管理总结(下)

释放所有的内存块,SBH系统的面貌就是初始状态,如前面所述。

在这里插入图片描述

第二讲讲的是GNU C++的分配器,这里的第三讲涉及的是VC的malloc函数,可以把它们混在一起吗?其实GNU C++的malloc实现差不多。

这里再系统化一遍。

allocator要内存,底部还是向malloc要内存。

allocator设计成16个链表的目的不是提升分配的速度,而是为了去除malloc的cookie开销,减少malloc的次数,每一次malloc要一大块内存,然后切分成相等的区块,这样就可以去除每一小块的cookie。

在这里插入图片描述

从操作系统的API(这里是windows系统,比如HeapAlloc, VirtualAlloc),到CRT的malloc设计,再到std::allocator的底部实现,都有类似的链表管理结构。

在这里插入图片描述

后记

截至2024年1月12日16点18分,花费1天的时间,完成《C++内存管理机制》第三讲的学习,这里学习完VC6的malloc底层实现逻辑。从C++标准库的allocator分配器到CRT(CRT 指的是 C Runtime Library,是 C 语言运行时库) 的malloc实现,对底层的内存分配结构更清楚。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

波动,热传导,扩散方程建立

数学物理方程是从自然科学的各个领域和工程技术领域中导出的偏微分方程和积分方程.在这些以偏微分方程为基础的数学模型中&#xff0c;二阶线性偏微分方程中的三个典型方程与定解条件的建立、解法及其应用&#xff0e;描述振动和波动过程的波动方程、描述输运过程的热传导&…

【笔记】Blender4.0建模入门-1、2

Blender入门 ——邵发 1.1 课程介绍 Blender&#xff0c;一款3D建模软件&#xff0c;小乔、免费、全流程 常见的3D建模软件&#xff1a; - 3DsMax/Maya/Blender/Cinema4D/ZBrush...游戏影视 - Proe/Solidworks/Inventor/UG...工业建模 - SketchUp/Rhino/Revit...建筑设计 …

想要简化重复订单吗?不妨考虑一揽子采购订单

企业想提高采购流程效率&#xff0c;简化大批量采购是一个很好的开始。财务、会计和采购部门通过系统化订购大量物品&#xff08;如纸张、打印机墨水和墨粉、清洁用品、纸制品和其他易重复采购的消耗品&#xff09;可以节省时间和金钱。借助正确的采购订单&#xff08;PO&#…

俩万字详解C++STL期末复习知识点(C++STL课本源码私信可得)

邸老师复习建议 复习注意事项 1 不考死记硬背的题&#xff0c;比如名词解释。 2 选择题重点考核宏观性、综合性的问题&#xff0c;比如&#xff1a;把电话通讯录存入容器&#xff0c;该选哪一个容器&#xff1f; 3 选择题重点考核理解性的问题&#xff0c;比如&#xff0c;…

C语言——(printf和scanf介绍)

一.printf 1.基本用法 printf&#xff08;&#xff09;的作用是将参数文本输出的屏幕。如下&#xff1b; 2.占位符 printf&#xff08;&#xff09;可以在输出文本中指定占位符 &#xff0c;“占位符”&#xff0c;也就是这个位置可以用其他值代入。 如&#xff1a; …

竞赛保研 基于深度学习的视频多目标跟踪实现

文章目录 1 前言2 先上成果3 多目标跟踪的两种方法3.1 方法13.2 方法2 4 Tracking By Detecting的跟踪过程4.1 存在的问题4.2 基于轨迹预测的跟踪方式 5 训练代码6 最后 1 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 基于深度学习的视频多目标跟踪实现 …

小魔推行业玩法:生活美容怎么做短视频矩阵?

如今每个实体老板都想让自己生意做的更好&#xff0c;那就需要有更多获取流量的方式&#xff0c;获得大量的同城曝光&#xff1b;在市场内卷的状况下&#xff0c;通过短视频来做门店引流无疑是绝佳的方式&#xff0c;让更多同城的用户知晓自己的门店&#xff0c;这个时候通过小…

HarmonyOS4.0系统性深入开发18公共事件简介

公共事件简介 HarmonyOS通过CES&#xff08;Common Event Service&#xff0c;公共事件服务&#xff09;为应用程序提供订阅、发布、退订公共事件的能力。 公共事件从系统角度可分为&#xff1a;系统公共事件和自定义公共事件。 系统公共事件&#xff1a;CES内部定义的公共事…

对比学习2024最新SOTA&应用方案分享,附14篇必读论文和代码

同学们发现没有&#xff0c;对比学习在我们的日常工作生活中已经很常见了&#xff0c;比如推荐系统任务&#xff0c;为用户推荐相似的商品或预测用户的购买行为&#xff1b;又比如图像检索&#xff0c;为用户找相似图片或识别不同物体。另外还有语音识别、人脸识别、NLP&#x…

43-函数的声明定义,函数表达式定义,函数的调用,声明提升,参数,形参,实参

1.函数声明定义 function 函数名(){} 2.函数表达式定义 匿名式函数表达式 var 函数名 function(){} 命名式函数表达式 var 函数名 function 函数关键字(){} 3.函数的调用 var fn function f(){alert("aaa");}//函数名&#xff08;&#xff09;//函数想调用几次…

京东年度数据报告-2023全年度游戏本十大热门品牌销量(销额)榜单

同笔记本市场类似&#xff0c;2023年度游戏本市场的整体销售也呈下滑态势。根据鲸参谋电商数据分析平台的相关数据显示&#xff0c;京东平台上游戏本的年度销量累计超过350万&#xff0c;同比下滑约6%&#xff1b;销售额将近270亿&#xff0c;同比下滑约11%。 鲸参谋综合了京东…

7、防写一个shell 命令解释器

1、代码部分 #include <stdio.h> #include <stdlib.h> #include <string.h>#define MAX_INPUT_LENGTH 100void parse_command(char *command) {// 用于存储解析后的命令和参数char cmd[MAX_INPUT_LENGTH];char args[MAX_INPUT_LENGTH];// 将输入的命令拷贝到…

Java Swing 图书借阅系统 窗体项目 期末课程设计 窗体设计

视频教程&#xff1a; 【课程设计】图书借阅系统 功能描述&#xff1a; 图书管理系统有三个角色&#xff0c;系统管理员、图书管理员、借阅者&#xff1b; 系统管理员可以添加借阅用户&#xff1b; ​图书管理员可以添加图书&#xff0c;操作图书借阅和归还&#xff1b; 借…

JDK安装与配置教程来啦

1.从Oracle公司官网下载JDK安装文件。 官网地址为&#xff1a; http://www.oracle.com/technetwork/java/javase/downloads/index.html 目前最新版本是JDK21,下面就以JDK21举例。 2.需要登录Oracle账户&#xff0c;没有的注册一下就行了。 3.在确认安装的盘符&#xff08;例…

Exception in thread “main“ java.lang.ArrayIndexOutOfBoundsException(数组创建问题)

数组在Java中使用还是比较多的&#xff0c;通过索引去数组中寻值&#xff0c;也可以通过数组索引去赋值 问题描述&#xff1a; 我们在直接使用未被new的数组时就会出现这种情况&#xff0c; 这边简单创建一个运行类 public class a {public static void main(String[] args)…

基于LVGL编写的windows串口工具: LCOM

LCOM: Serial Port Tools based on LVGL (PC Software) 一直以来我都想用LVGL做一个真正意义上的PC软件&#xff0c;来验证或者表达LVGL出色的特性&#xff0c;现在我用LCOM做到了&#xff01; LCOM 是一个基于LVGL编写的串口工具&#xff0c;界面简洁&#xff0c;功能出色&a…

更快更稳的4K响应鼠标,小手玩家也能用,雷柏VT9PRO mini

雷柏今年推出了不少新品&#xff0c;特别是一系列支持4K回报率的鼠标&#xff0c;凭借敏捷的响应速度&#xff0c;获得了非常好的评价。不过之前雷柏出的4K鼠标都多适合中大手&#xff0c;对小手用户不友好&#xff0c;而且配色较少&#xff0c;都是黑白色的基础款&#xff0c;…

数字集成系统设计——物理设计

目录 一、布局规划 1.1 规划 1.2 I/O单元 1.3 电源网络 1.3.1 要求 1.3.2 网络架构 1.3.3 混合信号芯片示例 1.4 布局 二、时钟分布 2.1 时钟偏斜 2.2 时钟分布网络 2.3 时钟树综合 2.4 时钟树收敛 三、布线与参数提取 3.1 布线(Routing) 3.2 布线规则示例 …

这是一家有图书馆的公司

Tubi 技术团队的同事们&#xff0c;除了爱写书、翻译书&#xff0c;也很爱读书。应广大爱书人号召&#xff0c;Tubi 中国团队从 2018 年 7 月开始兴建 Tubi 小书架&#xff0c;迄今已建成了一个有着17 种类别、近 500 本藏书、100 位常驻借阅成员、以音视频开发、广告推荐和团队…

什么是云服务器ECS及其优势、购买、使用方式和部署建议

阿里云服务器ECS英文全程Elastic Compute Service&#xff0c;云服务器ECS是一种安全可靠、弹性可伸缩的云计算服务&#xff0c;阿里云提供多种云服务器ECS实例规格&#xff0c;如经济型e实例、通用算力型u1、ECS计算型c7、通用型g7、GPU实例等&#xff0c;阿里云百科aliyunbai…