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

【学习笔记】数据结构(八)

动态存储管理

文章目录

  • 动态存储管理
    • 8.1 概述
    • 8.2 可利用空间表及分配方法
    • 8.3 边界标识法
      • 8.3.1 可利用空间表的结构
      • 8.3.2 分配算法
      • 8.3.3 回收算法
    • 8.4 伙伴系统
      • 8.4.1 可利用空间表的结构
      • 8.4.2 分配算法
      • 8.4.3 回收算法
    • 8.5 无用单元收集 - 垃圾回收机制
    • 8.6 存储紧缩 - 内存碎片化处理

8.1 概述

在刚开工时,整个内存区是一个“空闲块“(在 编译程序中称之为"堆")。

随着用户进入系统,先后提出存储请求,系统则依次进行分配。

在系统运行的初期,整个内存区基本上分隔成两大部分:低地址区包含若干占用块; 高地址区(即分配后的剩余部分)是一个空闲块

在这里插入图片描述

8.2 可利用空间表及分配方法

可利用空间表可以有下列 3种不同的结构形式:

  • 第一种情况,系统运行期间所有用户请求分配的存储量大小相同。

    • 在系统开始运行时将归它使用的内存区按所需大小分割成若干大小相同的块,然后用指针链接成一个可利用空间表

    • 由于表中结点大小相同,则分配时无需查找, 只要将第一个结点分配给用户即可;

    • 同样,当用户释放内存时,系统只要将用户释放的空 闲块插入在表头即可。

    • 可见,这种情况下的可利用空间表实质上是一个链栈。

  • 第二种情况,系统运行期间用户请求分配的存储量有若干种大小的规格。

    • 是建立若干个可利用空间表,同一链表中的结点大小相同

    • eg:用户将请求分配2个字、4个字或8个字的内存块

      • 系统建立 3个结 点大小分别为3个字、5 个字和 9 个字的链表,它们的表头指针分别为 av2、av4 和 av8。

      • 每个结点中的第一个字设有链域(link)【存储指向同一链表中下一结点的指针】, 标志域(tag)【tag - 区别结点为空闲块或占用块】和结点类型域 (type)【type-区别 3 种大小不同的结点】。

      • 而结点中的值域是其大小分别为 2 、4 和 8 个字的连续空间。

    • 分配和回收的方法在很大程度上和第一种情况类似

      • 当结点大小和请求分配的量相同的链表为空时,需查询结点较大的链表,并从中取出 一个结点,将其中一部分内存分配给用户,而将剩余部分插入到相应大小的链表中。

      • 回收时,也只要将释放的空闲块插人到相应大小的链表的表头中去即可。

      • 当结点与请求相符的链表和结点更大的链表均为空时, 分配不能进行,而实际上内存空间并不一定不存在所需大小的连续空间,只是由于在系统 运行过程中,频繁出现小块的分配和回收,使得大结点链表中的空闲块被分隔成小块后插 入在小结点的链表中,此时若要使系统能继续运行,就必须重新组织内存,即执行“存储紧缩”的操作

  • 第三种情况,系统在运行期间分配给用户的内存块的大小不固定,可以随请求而变。

    • 系统刚开始工作时,整个内存空间是一个空闲块,即可利用空间表中只有一个大小为整个内存区的结点

    • 标志域链域之外,还有一个结点大小域(size),以指示空闲块的存储量

    • 分配:

      • 某用户所需大小为n的内存,而可利用空间表中仅有一块大小为m≥n 的空闲块,则只需将其中大小为 n 的一部分分配给申请分配的用户,同时将剩余大小为m-n 的部分作为一个结点留在链表中即可。

      • 若可利用空间表中有若干个不小于n的空闲块

        • 首次拟合法。从表头指针开始查找可利用空间表,将找到的第一个大小不小于n的空闲块的一部分分配给用户。在回收时,只要将释放的空闲块插人在链表的表头即可。常适用于系统事先不掌握运行期间可能出现的请 求分配和释放的信息的情况。
        • 最佳拟合法。将可利用空间表中一个不小于 n且最接近n 的空闲块的一部分分配给用户。但在回收时,必须将释放的空闲块插入到合适的位置上去。适用于请求分配的内存大小范 围较广的系统
        • 最差拟合法。将可利用空间表中不小于 n且是链表中最大的空闲块的一部分分配给用户。为了节省时间,此时的可利用空间 表的结构应按空闲块的大小自大至小有序。这样,每次分配无需查找,只需从链表中删除 第一个结点,并将其中一部分分配给用户,而剩余部分作为一个新的结点插入到可利用空 间表的适当位置上去。但在回收时,需将释放的空闲块插入到链表的适当位置上去。它适用千请求分配的内存大小范围较窄的系统。

        在这里插入图片描述

8.3 边界标识法

8.3.1 可利用空间表的结构

可以解决系统中内存碎片过多而无法使用的问题。

本身是 双向循环链表,每个内存块结点都有指向前驱和后继结点的指针域。

  • space 域 表示为该内存块的大小,它的大小通过 head 域中的 size 值表示。
  • head 域中包含有 4 部分:
    • llink 和 rlink 分别表示指向当前内存块结点的直接前驱和直接后继;
    • tag 值用于标记当前内存块的状态,是占用块(用 1 表示)还是空闲块(用 0 表示);
    • size 用于记录该内存块的存储大小(包括头部 head 和底部 foot 所占空间)。
  • foot 域中包含有 2 部分:
    • uplink 是指针域,用于指向内存块本身,通过 uplink 就可以获取该内存块所在内存的首地址;
    • tag 同 head 域中的 tag 相同,都是记录内存块状态的。

在这里插入图片描述

typedef struct WORD{  // WORD: 内存字类型union{ // head 和 foot 分别是结点的第一个字和最后的字struct WORD *llink;    //头部域,指向直接前驱struct WORD *uplink;   //底部域,指向结点本身};int tag;//标记域,0表示为空闲块;1表示为占用块,头部和尾部均有。int size;///头部域,记录内存块的存储大小struct WORD *rlink;///头部域,指向直接后继OtherType other;//内存块可能包含的其它的部分
}WORD,head,foot,*Space; //* Space, 可利用空间指针类型# define FootLoc(p) p+p->size-1 //指向 p 所指结点的底部/*表中不设表头结点,表头指针 pav 可以指向表中任一结点,即任何一个结点都
可看成是链表中的第一个结点;表头指针为空,则表明可利用空间表为空。 */

8.3.2 分配算法

分配算法:假设我们采用首次拟合法进行分配,则只要从表头指针pav所 指结点起,在可利用空间表中进行查找,找到第一个容量不小于请求分配的存储量(n)的 空闲块时,即可进行分配。

注意:

  • 待分配的空闲块的容量为 m个字(包括头部和底部),若每次分配只是从中分配n个字给用户,则需要选定一个适当的常量e, 当m-n≤e时,就将容量为m 的空闲 块整块分配给用户;反之,只分配其中 n个字的内存块。
  • 在每次分配之后,令指针 pav指向刚进行过分配的结点的后继结点,防止造成存储量小的结点密集在 头指针 pav 所指结点附近,从而会增加查询较大空闲块的时间。

在这里插入图片描述

Space AllocBoundTag(Space* pav, int n) {Space p, f;int e = 10;//设定常量 e 的值// 查找不小于n的空闲块for (p = (*pav); p && p->size < n && p->rlink != (*pav); p = p->rlink);if (!p || p->size < n) {return NULL; // 找不到,返回空指针}// p 指向找到的空闲块else {//指针f指向p空闲块的foot域f = FootLoc(p);// *pav 指向 P 结点的后继结点(*pav) = p->rlink;//整块分配,不保留 <= e的剩余量if (p->size - n <= e) {if ((*pav) == p) {pav = NULL; // 可利用空间表变为空表}else {//全部分配用户,即从可利用空间表中删除 p 空闲块(*pav)->llink = p->llink;  p->llink->rlink = (*pav);}// 修改分配结点的头部和底部标志p->tag = f->tag = 1;}// 分配该块的后 n个字else {//更改分配块foot域的信息f->tag = 1; // 修改分配块的底部标志p->size -= n; // 置剩余块大小f = FootLoc(p);// f指针指向剩余空闲块 p 的底部f->tag = 0;   f->uplink = p; // 设置剩余块底部p = f + 1; // 指向分配块头部p->tag = 1;   p->size = n; // 设置分配块头部}return p;}
}

8.3.3 回收算法

回收算法:当用户释放占用块时,系统需立即回收以备新的请求产生时进行再分配。

  • 若释放块的左、右邻区均为占用块,则处理最为简单,只要将此新的空闲块作为一个 结点插入到可利用空闲表中即可;
  • 若只有左邻区是空闲块,则应与左邻区合并成一个结点;
  • 若只有右邻区是空闲块,则应与右邻区合并成一个结点;
  • 若左、右邻区都是空闲块,则 应将 3 块合起来成为一个结点留在可利用空间表中

判断当前空闲块左右两侧是否为空闲块的方法

  • 假设用户释放的内存区的头部地址为P,则与其低地址紧邻的内存区的底部地址为 p-1;

    与其高地址紧邻的内存区的头部地址为 p+p->size;

  • 若(p-1)->tag=0;则表明其左邻为空闲块,若(p+p->size)-> tag=0; 则表明其右邻为空闲块。

1️⃣空闲块两侧是占用块

直接插入

//设定p指针指向的为用户释放的空闲块
p->tag=0;
//f指针指向p空闲块的foot域
Space f=FootLoc(p);
f->uplink=p;
f->tag=0;
//如果pav指针不存在,证明可利用空间表为空,此时设置p为头指针,并重新建立双向循环链表
if (!pav) {pav=p->llink=p->rlink=p;
}else{//否则,在p空闲块插入到pav指向的空闲块的左侧Space q=pav->llink;p->rlink=pav;p->llink=q;q->rlink=pav->llink=p;pav=p;
}

2️⃣空闲块左侧是空闲块

更改左侧空闲块中的 size 的大小,并重新设置左侧空闲块的 foot 域即可

在这里插入图片描述

//常量 n 表示当前空闲块的存储大小
int n=p->size;
Space s=(p-1)->uplink;//p-1 为当前块的左侧块的foot域,foot域中的uplink指向的就是左侧块的首地址,s指针代表的是当前块的左侧存储块
s->size+=n;//设置左侧存储块的存储容量
Space f=p+n-1;//f指针指向的是空闲块 p 的foot域
f->uplink=s;//这是foot域的uplink指针重新指向合并后的存储空间的首地址
f->tag=0;//设置foot域的tag标记为空闲块

3️⃣空闲块右侧是空闲块

将用户释放掉的存储块替换掉右侧的空闲块,同时更改存储块的 size 和右侧空闲块的 uplink 指针的指向

在这里插入图片描述

Space t=p+p->size;//t指针指向右侧空闲块的首地址
p->tag=0;//初始化head域的tag值为0
//找到t的前驱结点,用当前释放的空闲块替换右侧空闲块
Space q=t->llink;
p->llink=q; q->rlink=p;
//找到t的后继结点,用当前释放的空闲块替换右侧空闲块
Space q1=t->rlink;
p->rlink=q1; q1->llink=p;
//更新释放块的size的值
p->size+=t->size;
//更改合并后的foot域的uplink指针的指向
Space f=FootLoc(t);
f->uplink=p;

4️⃣空闲块两侧是空闲块

将 3 个空闲块合并为一个更大的块:更新左侧空闲块的 size 的值,同时在可利用空间表中摘除右侧空闲块,最后更新合并后的大的空闲块的 foot 域。

在这里插入图片描述

int n=p->size;
Space s=(p-1)->uplink;//找到释放内存块物理位置相邻的低地址的空闲块
Space t=p+p->size;//找到物理位置相邻的高地址处的空闲块
s->size+=n+t->size;//更新左侧空闲块的size的值
//从可利用空间表中摘除右侧空闲块
Space q=t->llink;
Space q1=t->rlink;
q->rlink=q1;
q1->llink=q;
//更新合并后的空闲块的uplink指针的指向
Space f=FootLoc(t);
f->uplink=s;

8.4 伙伴系统

在伙伴系统中,无论是占用块或空闲块,其大小均为 2的k 次幂(k 为某个正整数)例如;当用户申请n个字的内存区时,分配的占用块大小为 2k 个字(2k-1 < n ≤ 2k)

伙伴系统的优点是算法简单、速度快;缺点是由于只归并伙伴而容易产生碎片。

8.4.1 可利用空间表的结构

将所有大小相同的空闲块建于一张子表中。 每个子表是一个双重链表,双重链表中的结点结构:

​ head 为结点头部,是一个由 4 个域组成的记录,

  • ​ llink域和 rlink域分别指向同一链表中的前驱和后继结点;

  • ​ tag域为值 取"0""1"的标志域;

  • ​ kval 域的值为 2 的幂次 k;

  • ​ space 是一个大小为 2k-1 个字的连续内存空间(仍假设 head 占一个字的空间)。

在这里插入图片描述

# define m 16
typedef struct WORD_b {struct WORD_b* llink; // 指向前驱结点int tag;// 块标志,0:空闲,l: 占用。int kval;// 块大小,值为 2 的幂次 Kstruct WORD_b* rlink;// 头部域,指向后继结点OtherType other; // 字的其他部分
}WORD_b, head; // WORD : 内存字类型,结点的第一个字也称为 headtypedef struct HeadNode {int nodesize; // 该链表的空闲块的大小WORD_b* first; // 该链表的表头指针
}FreeList[m+1]; // 表头向量类型

8.4.2 分配算法

在分配时经常需要将一个大的空闲块分裂成两个大小相等 的存储区,这两个由同一大块分裂出来的小块就称之“互为伙伴"

例题:

时间事件请求大小分配情况空闲情况
Start初始状态--210
T1A请求150B28(A)29, 28
T2B请求100B28(A), 27(B)29, 27
T3C请求50B28(A), 27(B), 26©29, 26
T4B释放-28(A), 26©29, 27, 26
T5C释放-28(A)29, 28
T6A释放--210

思路:

初始状态为210大小的空闲块

  • 为A进程分配150B (27 < 150 ≤ 28 ==> 2k-1 < 150 ≤ 2k, k = 8)分区时,首先计算应该分配在哪个k区 k=8
  • 接着,在2k的空闲分区链中查找
    • 找到,将空闲分区分配给进程A
    • 未找到,表示大小为2k的空闲分区已耗尽,需要在大小为2k+1的空闲分区链中继续查找。
      • 若存在大小为2k+1的空闲分区,则将其等分为两个分区,这两个分区称为一对伙伴,其中一个用于分配,另一个加入大小为2k的空闲分区链。
      • 若不存在大小为2k+1的空闲分区,则继续查找2k+2的空闲分区链,
        • 若找到则需要进行两次分割,分割称两个2k+1的分区,一个2k+1大小的分区加入到对应的空闲分区链中; 另一个分区继续分割,分割成两个2k的分区,一个加入对应分区链中,一个分给进程。
        • 若仍然不存在,则继续查找,以此类推

在这里插入图片描述

WORD_b* AllocBuddy(FreeList* avail, int n)
{//avail[O .. m]为可利用空间表,n 为申请分配量,若有不小于 n 的空闲块,int k;for (k = 0; k <= m && ((*avail)[k].nodesize < n + 1 || !(*avail)[k].first); k++){// 查找满足分配要求的子表continue;}if (k > m) return NULL; // 分配失败,返回 NULLelse {// 进行分配WORD_b* pa = (*avail)[k].first; //指向可分配子表的第一个结点WORD_b* pre = pa->llink; //指向前驱WORD_b* suc = pa->rlink; //指向后继if (pa == suc) (*avail)[k].first = NULL; //分配后该子表变成空表else { // 从子表删去*pa结点/*pre->rlink = suc;suc->llink = pre;*/(*avail)[k].first = suc;}int i;for (i = 1; (*avail)[k-i].nodesize >= n + 1; ++i){WORD_b* pi = pa + (int)pow(2,k-i);pi->rlink = pi;pi->llink = pi;pi->tag = 0;pi->kval = k - i;(*avail)[k - i].first = pi;}// 将剩余块插入相应子表pa->tag = 1;pa->kval = k - (--i);return pa;}
}

8.4.3 回收算法

首先判别其伙伴是否为空闲块,

  • 若否,则只要将释放的空闲块简单插入在相应子表中即可;
  • 若是,则需在相应子表中找到其伙伴并删除之,然后再判 别合并后的空闲块的伙伴是否是空闲块。依此重复,直到归并所得空闲块的伙伴不是空闲块时,再插入到相应的子表中去。

在这里插入图片描述

例如,当大小为28,起始地址为 512 的伙伴块的起始地址的计算方式为:
由于 512 取余 29=0,所以,512+ 28=768,即如果该存储块回收时,只需要查看起始地址为 768 的存储块的状态,如果是空闲块则两者合并,反之直接将回收的释放块链接到大小为 28 的链表中。

8.5 无用单元收集 - 垃圾回收机制

问题描述:

内存在使用完成后没有向系统发出释放的指令,导致存储空间既没有被使用也没有被回收,变为了无用单元或者会产生悬挂访问

  • 无用单元 :是指那些用户不再使用而系统没有回收的结构和变量。

    例如,在C语言中,用户可以通过 malloc 和 free 两个功能函数来动态申请和释放存储空间,当用户使用 malloc 申请的空间使用完成后,没有使用 free 函数进行释放,那么该空间就会成为无用单元。

  • 悬挂访问 : 假设使用 malloc 申请了一块存储空间,有 多个指针同时指向这块空间当其中一个指针完成使命后,私自将该存储空间使用 free 释放掉,导致 其他指针处于悬空状态,如果 释放掉的空间被再分配后,再通过之前的指针访问,就会造成错误,数据结构中称这种访问为悬挂访问。

解决方法:

  1. **使用访问计数器:**每个申请的存储空间设置一个计数域,这个计数域 记录的是 指向该存储空间的指针数目,只有当计数域的值为 0 时,该存储空间才会被释放。
  2. **收集无用单元(中断回收机制):**在程序运行时, 所有的存储空间无论是处于使用还是空闲的状态,一律不回收,当系统中的可利用空间表为空时,将程序中断,对当前 不再使用状态的存储空间一律回收,全部链接成一个新的可利用空间表后,程序继续执行。

注意:

​ 实际上,无用单元收集本身都是很 费时间 的,所以无用单元的收集 不适用于实时处理的情况中使用

➡️ 解决方法2 - 中断回收机制 详细内容

  • 中断回收机制步骤:

    • 对所有当前正在使用的 存储空间加上被占用的标记(对于广义表来说,可以在每个结点结构的基础上,添加一个 mark 的标志域。)在初始状态下,所有的存储空间全部标志为 0表示未被占用,被占用时程序标记为 1
    • 依次遍历所有的存储空间,将所有标记为 0 的存储空间链接成一个新的可利用空间表。
  • 步骤一的三种标志算法

    • 递归算法:加标志的操作实质上是遍历广义表,将广义表中所有结点的标志域赋值"1"。

      • 遍历(加标志)算法的递归定义:
        • 若列表为空,则无需遍历;
        • 若是一个数据元素,则标志元素结点;
        • 反之,则列表非空,首先标志表结点;然后分别遍历表头和表尾。
      • 缺点:需要一个较大的实现 递归用的栈的辅助内存,这部分内存不能用于动态分配。
    • 非递归算法:程序中附设栈(或队列)实现广义表的遍历。

      • 从广义表的存储结构 来看,表中有两种结点:

        • 一种是元素结点,结点中没有指针域;
        • 另一种是表结点,结点中包含两个指针域:表头指针和表尾指针
      • 方法:

        • 深度优先搜索遍历 - 为实现这个遍历需附设一个栈

          ① 当表非空时,在对表结点加标志后,

          ② 先顺表头指针逐层向下对表头加标志,同时将同层非空且未加标志的表尾指针依次入栈,

          ③ 直到表头为空表或为元素结点时停止,然后退栈取出上一层的表尾指针。

          ④ 反复上述进行 过程,直到栈空为止

        • 广度优先搜索遍历(列表按层次遍历)- 为实现这个遍历需附设一个队列

    • 利用表结点本身的指针域标记遍历路径的算法

      添加三个指针,p 指针指向当前遍历的结点t 指针永远指向 p 的父结点q 指向 p 结点的表头或者表尾结点。在遍历过程遵循以下原则:

      1. 当 q 指针指向 p 的表头结点时,可能出现 3 种情况:
        1. p 结点的表头结点只是一个元素结点,没有表头或者表尾: 只需要对该结点打上标记后令 q 指向 p 的表尾
        2. p 结点的表头结点是空表或者是已经做过标记的子表: 只需要直接令 q 指针指向 p 结点的表尾即可
        3. p 结点的表头是未添加标记的子表: 需先遍历表头子表,即p应赋q的值,t相应往下移动改赋p 的值。为了记 下 t指针移动的路径,以便在 p退回原结点时同时能找到 p的母表结点(即使t退回到原来的值),则在修改这个指针的值之前,应先记下 t移动的路径,即令 p所指结点的 hp域 的值为 t,且 tag 域的值为"0"。
      2. 当 q 指针指向 p 的表尾结点时,可能出现 2 种情况:
        1. p 的表尾为未加标志的子表,则需遍历表尾的子表,同样 p、t指针要作相应的移动。为了记下当前表结点的母表 结点,同样要在改动p、t指针的值之前先记下路径;即令 p所指结点的 tp域的值改为 t, 然后令t赋值p,p 赋值 q;
        2. p 的表尾为“空”或是已加上标志的子表,此时表明 p所指的表已加上标志,则 p应退回到其母表结点即 t所指结点,相应地t也应后退一步,即退到 t 结点的母表结点。

      在这里插入图片描述

  • 中断回收机制优缺点

    优点:不需要附加存储,使动态分配的 可利用空间得到充分利用,

    缺点:但是由于在算法中,几乎是每个表结点的指针域的值都要作两次改变,因此时间上的开销相当大,而且一旦发生中断,整个系统瘫痪,无法重新启动运行。

8.6 存储紧缩 - 内存碎片化处理

  • 无论是边界标识法还是伙伴系统,都是将空闲的存储空间链接成一个链表,即可利用空间表,对存储空间进行分配和回收。

    另外一种动态内存管理的方法,使用这种方式在整个内存管理过程中,**不管哪个时刻,可利用空间都是一个地址连续的存储区,在编译程序中称之为"堆 " **,每次分配都是从这个可利用空间中划出一块。

  • 分配算法

    在 分配内存空间时,每次都从可利用空间中选择最低(或者最高)的地址进行分配。具体的实现办法为:设置一个指针(称为堆指针),每次用户申请存储空间时,都是堆的最低(或者最高)地址进行分配。假设当用户申请 N 个单位的存储空间时,堆指针向高地址(或者低地址)移动 N 个存储单位,这 N 个存储单位即为分配给用户使用的空闲块,空闲块的起始地址为堆指针移动之前所在的地址。

    在这里插入图片描述

  • 回收算法

    由于系统的可利用空间 始终是一个地址连续的存储块,因此回收时必须将所释放的空闲块合并到整个堆上去才 能重新使用,这就是“存储紧缩"的任务

    做法:

    • 一种是一旦用户释放所占空间就立即进行回收紧缩
    • 另外一种是在程序执行过程中不立即回收用户释放的存储块,而是等到可利用空间不够分配或者堆指针指向了可利用存储区的最高地址时才进行存储紧缩。具体的实现过程是:
      • 计算占用块的新地址。从最低地址始巡查整个存储空间,对每一个占用块找到 它在紧缩后的新地址。为此,需设立两个指针随巡查向前移动,这两个指针分别指示占用 块在紧缩之前和之后的原地址和新地址。因此,在每个占用块的第一个存储单位中,除了 设立长度域(存储该占用块的大小)和标志域(存储区别该存储块是占用块或空闲块的标 志)之外,还需设立一个新地址域,以存储占用块在紧缩后应有的新地址,即建立一张新、 旧地址的对照表
      • 修改用户的初始变量表,以便在存储紧缩后用户程序能继续正常运行。
      • 检查每个占用块中存储的数据。若有指向其他存储块的指针,则需作相应修改。
      • 将所有占用块迁移到新地址去——传送数据。
      • 将堆指针赋以新值(即紧缩后的空闲存储区的 最低地址)。
  • 注意:存储紧缩较之无用单元收集更为复杂,是一个系统的操作,如果不是非不得已不建议使用。

参考:

教材:严蔚敏《数据结构》(C语言版).pdf

博客:

边界标识法管理动态内存

无用单元收集(垃圾回收机制)

内存紧缩(内存碎片化处理)

视频

10分钟速通伙伴系统算法!

相关文章:

【学习笔记】数据结构(八)

动态存储管理 文章目录 动态存储管理8.1 概述8.2 可利用空间表及分配方法8.3 边界标识法8.3.1 可利用空间表的结构8.3.2 分配算法8.3.3 回收算法 8.4 伙伴系统8.4.1 可利用空间表的结构8.4.2 分配算法8.4.3 回收算法 8.5 无用单元收集 - 垃圾回收机制8.6 存储紧缩 - 内存碎片化…...

maven-resources-production:ratel-fast: java.lang.IndexOutOfBoundsException

Maven生产环境中遇到java.lang.IndexOutOfBoundsException的问题&#xff0c;尝试了重启电脑、重启IDEA等常规方法无效&#xff0c;最终通过直接重建工程解决了问题。 Rebuild Project 再启动OK...

建投数据与腾讯云数据库TDSQL完成产品兼容性互认证

近日&#xff0c;经与腾讯云联合测试&#xff0c;建投数据自主研发的人力资源信息管理系统V3.0、招聘管理系统V3.0、绩效管理系统V2.0、培训管理系统V3.0通过腾讯云数据库TDSQL的技术认证&#xff0c;符合腾讯企业标准的要求&#xff0c;产品兼容性良好&#xff0c;性能卓越。 …...

后端-添加购物车和查看购物车

...

【HarmonyOS NEXT】Web 组件的基础用法以及 H5 侧与原生侧的双向数据通讯

关键词&#xff1a;鸿蒙、ArkTs、Web组件、通讯、数据 官方文档Web组件用法介绍&#xff1a;文档中心 Web 组件加载沙箱中页面可参考我的另一篇文章&#xff1a;【HarmonyOS NEXT】 如何将rawfile中文件复制到沙箱中_鸿蒙rawfile 复制到沙箱-CSDN博客 目录 如何在鸿蒙应用中加…...

7-2 排序

输入一批未排序的数据&#xff0c;数量不超过30个&#xff0c;请使用选择法或者冒泡法对其排序&#xff0c;并按照规定的要求输出。 输入格式: 先输入待排序的整形数的个数&#xff1b;然后输入所有的待排序的数据。 输出格式: 在一行中按照由大到小的顺序输出排序好的数据…...

Java通过反射破坏单例模式

有个第三方工具类&#xff0c;不支持多例模式。但是又不能直接改第三方工具类的代码&#xff0c;因此可以通过反射破坏第三方工具类的单例。 第三方工具类反编译如下 可以看到构造函数进行了私有化&#xff0c;不允许外部new&#xff0c;只能通过newInstance进行实例化。并且…...

FFmpeg第一话:FFmpeg 简介与环境搭建

FFmpeg 探索之旅 一、FFmpeg 简介与环境搭建 二、FFmpeg 解码详解 第一话&#xff1a;FFmpeg 简介与环境搭建 FFmpeg 探索之旅一、前言二、FFmpeg 是什么&#xff1f;三、简单介绍其历史背景四、为什么用 C学习 FFmpeg&#xff1f;&#xff08;一&#xff09;高性能优势&#…...

C++并发编程: std::atomic对指针进行操作

std::atomic 对指针进行运算的用途 std::atomic 提供了一种在多线程环境中安全地操作指针的方法。这对于实现线程安全的指针管理、动态内存分配、链表操作等场景非常有用。通过使用std::atomic对指针进行运算&#xff0c;可以确保指针操作的原子性和多线程安全性。 常见用途 …...

工业大数据分析算法实战-day08

文章目录 day08模型评价聚类算法基于距离的聚类基于层次的聚类基于密度的聚类基于分布的聚类聚类结果的评价 day08 今天是第8天&#xff0c;昨日阐述了概率图模型和集成学习的分类&#xff0c;主要讲解了有向图和无向图&#xff0c;生成式模型和判断式模型&#xff0c;以及集成…...

在 C# 中实现的目录基础操作

前言 在开发应用程序过程中&#xff0c;对操作系统上的文件夹存储文件和子文件夹操作是常见的需求。.NET中的Directory类提供了处理文件目录的功能。本文介绍如何读取文件夹的属性、获取文件夹的大小及文件个数、创建文件夹、遍历文件夹中的所有文件、移动文件夹和删除文件夹等…...

​Python 标识符是啥?​

Python 的标识符就是我们写代码时用来给变量、函数、类等取名字的东西。 你写的 my_variable 是个标识符&#xff0c; 定义的 add_numbers 函数名也是个标识符&#xff0c; 甚至你写的 Cat 类名&#xff0c;也是标识符。 一句话总结&#xff1a;标识符就是代码里给“东西”起…...

WEB开发: 全栈工程师起步 - Python Flask +SQLite的管理系统实现

一、前言 罗马不是一天建成的。 每个全栈工程师都是从HELLO WORLD 起步的。 之前我们分别用NODE.JS 、ASP.NET Core 这两个框架实现过基于WebServer的全栈工程师入门教程。 今天我们用更简单的来实现&#xff1a; Python。 我们将用Python来实现一个学生管理应用&#xff0…...

将 Matplotlib 图形转换为 PIL 图像并返回

将 Matplotlib 图形转换为 PIL 图像并返回 前言完整代码Matplotlib 中 fig 和 ax 的关系示例&#xff1a; 问题分析常见错误及解决方案总结 前言 Matplotlib 是 Python 里最流行的图表展示库&#xff0c;PIL (Python Imaging Library)则是一个强大的图像处理库。在开发过程中&…...

F5中获取客户端ip地址(client ip)

当F5设备对其原始设置上的所有IP地址使用NAT时&#xff0c;连接到poo成员&#xff08;nodes、backend servers&#xff09;的出站连接将是NAT IP地址。 pool 成员&#xff08;nodes、backend servers&#xff09;将无法看到真实的客户端 ip地址&#xff0c;因为看到的是F5上的…...

点焊机器人维修-ABB-KUKA-FANUC-YASKAWA

在正式启用点焊机器人之前&#xff0c;一项至关重要的预备步骤便是进行焊枪的全面设置操作。以FANUC机器人为例&#xff0c;其焊枪的设置流程涵盖了多个关键环节&#xff0c;如焊枪运动方向的精确规划、焊枪规格的选择以及零点标定的细致执行等。这些设置均须严格依据实际所采用…...

springboot448教学辅助系统(论文+源码)_kaic

摘 要 互联网发展至今&#xff0c;无论是其理论还是技术都已经成熟&#xff0c;而且它广泛参与在社会中的方方面面。它让信息都可以通过网络传播&#xff0c;搭配信息管理工具可以很好地为人们提供服务。针对信息管理混乱&#xff0c;出错率高&#xff0c;信息安全性差&#x…...

网络地址转换(NAT)和端口映射

1. 网络地址转换(NAT) 1.1 NAT的应用场景 &#xff08;1&#xff09;应用场景&#xff1a;允许将私有IP地址映射到公网地址&#xff0c;以减缓IP地址空间的消耗 ①需要连接Internet&#xff0c;但主机没有公网IP地址 ②更换了一个新的ISP&#xff0c;需要重新组织网络时&…...

iClent3D for Cesium 实现无人机巡检飞行效果

作者&#xff1a;gaogy 1、背景 随着地理信息技术的发展&#xff0c;三维地球技术逐渐成为了许多领域中的核心工具&#xff0c;尤其是在城市规划、环境监测、航空航天以及军事领域。三维地图和场景的应用正在帮助人们更加直观地理解空间数据&#xff0c;提供更高效的决策支持。…...

后端- spring cache框架操作缓存数据(底层可以是redis,也可以是caffeine和ehcache)

...

低比特语言模型 是一种利用较少比特数进行语言建模的技术

Vanilla LLM: 基础的全精度语言模型&#xff0c;通常在较高比特数下运作 Vanilla LLM&#xff0c;或称为“基础的全精度语言模型”&#xff0c;是指使用标准的浮点数&#xff08;通常是16位或32位&#xff09;进行训练和推理的语言模型。这些模型依赖于经典的神经网络结构&…...

ES6中的map和set

Set ES6 提供了新的数据结构 Set。它类似于数组&#xff0c;但是成员的值都是唯一的&#xff0c;没有重复的值。 Set本身是一个构造函数&#xff0c;用来生成 Set 数据结构。 以下代码 const s new Set();[2, 3, 5, 4, 5, 2, 2].forEach(x > s.add(x));for (let i of s…...

【WRF安装】WRF编译错误总结1:HDF5库包安装

目录 1 HDF5库包安装有误&#xff1a;HDF5 not set in environment. Will configure WRF for use without.HDF5的重新编译 错误原因1&#xff1a;提示 overflow 错误1. 检查系统是否缺少依赖库或工具2. 检查和更新编译器版本3. 检查 ./configure 报错信息4. 检查系统环境变量5.…...

MyBatis常见面试题总结

#{} 和 ${} 的区别是什么&#xff1f; 注&#xff1a;这道题是面试官面试我同事的。 答&#xff1a; ${}是 Properties 文件中的变量占位符&#xff0c;它可以用于标签属性值和 sql 内部&#xff0c;属于原样文本替换&#xff0c;可以替换任意内容&#xff0c;比如${driver}…...

Qt生成随机数的方法

后台接口要求传个流水单号&#xff0c;流水号的格式是&#xff1a;“设备序列号”“设备MAC地址”“20位的随机数”。 具体的是&#xff1a; “设备序列号”&#xff1a;就是烧录工具写入设备的序列编号&#xff0c;这就不多说了&#xff0c;读出来就行&#xff1b; “设备MAC地…...

深入探索Vue.js中的v-if指令:条件渲染的高级技巧

深入探索Vue.js中的v-if指令&#xff1a;条件渲染的高级技巧 引言 在现代Web开发中&#xff0c;根据条件动态地渲染或移除DOM元素是一个常见的需求。Vue.js提供了一种简洁而强大的方法来实现这一目标&#xff0c;即通过v-if指令来根据表达式的值来控制元素的渲染。本文将详细…...

【记录50】uniapp安装uview插件,样式引入失败分析及解决

SassError: Undefined variable: "$u-border-color". 表示样式变量$u-border-color没定义&#xff0c;实际是定义的 首先确保安装了scss/sass 其次&#xff0c;根目录下 app.vue中是否全局引入 <style lang"scss">import /uni_modules/uview-ui/in…...

NTLMv2 离线爆破

攻击者&#xff08;kali&#xff09;&#xff1a;192.168.72.162 受害者&#xff08;administrator&#xff09;&#xff1a;192.168.72.163 因为 NTLM 身份验证是通过计算正确的挑战值得出的&#xff0c;所以如果我们能获取域用户的 NTLM 认证某一服务的 Net-NTLM v2 Hash …...

LabVIEW实现RFID通信

目录 1、RFID通信原理 2、硬件环境部署 3、程序架构 4、前面板设计 5、程序框图设计 6、测试验证 本专栏以LabVIEW为开发平台,讲解物联网通信组网原理与开发方法,覆盖RS232、TCP、MQTT、蓝牙、Wi-Fi、NB-IoT等协议。 结合实际案例,展示如何利用LabVIEW和常用模块实现物联网系…...

【Three.js基础学习】31.Lights Shading

前言 关于灯光如何在着色器中应用&#xff01; 下面将创建三个灯光 分别是点光源&#xff0c;环境光&#xff0c;方向光通过这几种光应用着色器显示对应阴影 学习灯光阴影&#xff0c;着色器的使用 添加三盏灯 点光&#xff0c;方向光&#xff0c;环境光 创建一个环境光 在现…...

Oracle Database 21c Express Edition数据库 和 Sqlplus客户端安装配置

目录 一. 前置条件二. Win10安装配置Oracle数据库2.1 数据库获取2.2 数据库安装2.3 数据库配置确认2.4 数据库访问 三. Win10配置Oracle数据库可对外访问3.1 打开文件和打印机共享3.2 开放1521端口 四. 端口与地址确认4.1 查看监听器的状态4.2 Win10查看1521端口是否被监听4.3 …...

IDEA搭建springboot demo

如下所示创建SpringBootTest18项目&#xff0c;我选的maven&#xff0c;创建完成项目后&#xff0c;maven会自动下载一些依赖库到maven的repository目录中。 创建的项目结构如下图所示 接下来在项目中加入Framework支持&#xff0c;右击项目&#xff0c;弹出的菜单如下图所示&a…...

SQLite Update 语句

SQLite Update 语句 SQLite 的 UPDATE 语句用于更新数据库表中的现有记录。使用 UPDATE 语句&#xff0c;您可以修改一个或多个列的值。本教程将详细介绍如何使用 SQLite UPDATE 语句&#xff0c;包括语法、示例以及一些最佳实践。 语法 SQLite UPDATE 语句的基本语法如下&a…...

node.js的简单示例

Node.js是一个基于Chrome V8引擎的JavaScript运行时环境&#xff0c;用于方便地构建快速、可扩展的网络应用。下面是一个简单的Node.js示例&#xff0c;它创建了一个简单的HTTP服务器&#xff0c;当访问服务器时&#xff0c;它会响应“Hello World” // 引入Node.js的HTTP模块…...

fpga系列 HDL:Quartus II 时序约束 静态时序分析 (STA) test.out.sdc的文件结构

test.out.sdc的文件结构 ## Generated SDC file "test.out.sdc"## Copyright (C) 1991-2013 Altera Corporation ## Your use of Altera Corporations design tools, logic functions ## and other software and tools, and its AMPP partner logic ## functions,…...

Restaurants WebAPI(一)—— clean architecture

文章目录 项目地址一、Restaurants.Domain 核心业务层1.1 Entities实体层1.2 Repositories 数据操作EF的接口二、Restaurants.Infrastructure 基础设施层2.1 Persistence 数据EF CORE配置2.2 Repositories 数据查询实现2.3 Extensions 服务注册三、Restaurants.Application用例…...

A Unified Framework for STAR-RIS Coefficients Optimization

文章目录 AbstractB. A Penalty-based Reformulation of (1) III. OPTIMIZING AUXILIARY VARIABLES φ \boldsymbol φ φ IN P1IV. A CASE STUDY OF P2 ON DOWNLINK STAR-RIS ASSISTED TRANSMISSION SYSTEMA. 优化 x , ρ , w , λ t x, \rho, \mathbf{w}, \lambda^t x,ρ,w…...

rebase ‘A‘ onto ‘master‘ 和 merge ‘master‘ into ‘A‘有什么区别

在Git版本控制系统中&#xff0c;rebase 和 merge 是两种不同的操作&#xff0c;用于合并分支。rebase A onto master 和 merge master into A 虽然最终目的都是将两个分支的更改合并在一起&#xff0c;但它们在处理方式和结果上有所不同。 rebase ‘A’ onto ‘master’ 含义…...

谷歌发布最新视频生成模型 Veo 2:视频生成AI新王牌

谷歌 在当今数字化快速发展的时代&#xff0c;人工智能视频生成技术正不断突破创新。就在12月17日&#xff0c;谷歌推出了一个新的视频模型 Veo 2 。 Veo 2 Veo 2 Veo 2 可以创建各种主题和风格的高质量视频。在谷歌官方由人工评估员判断中&#xff0c;Veo 2 与领先模型相比取得…...

2025erp系统开源免费进销存系统搭建教程/功能介绍/上线即可运营软件平台源码

系统介绍 基于ThinkPHP与LayUI构建的全方位进销存解决方案 本系统集成了采购、销售、零售、多仓库管理、财务管理等核心功能模块&#xff0c;旨在为企业提供一站式进销存管理体验。借助详尽的报表分析和灵活的设置选项&#xff0c;企业可实现精细化管理&#xff0c;提升运营效…...

基于Docker的Minio分布式集群实践

目录 1. 说明 2. 配置表 3. 步骤 3.1 放行服务端口 3.2 docker-compose 编排 4. 入口反向代理与负载均衡配置 4.1 api入口 4.2 管理入口 5. 用例 6. 参考 1. 说明 以多节点的Docker容器方式实现minio存储集群&#xff0c;并配以nginx反向代理及负载均衡作为访问入口。…...

解决node.js的req.body为空的问题

从昨晚一直在试&#xff0c;明明之前用的封装的axios发送请求给其他的后端&#xff08;springboot&#xff09;是可以的&#xff0c;但昨天用了新项目的后端&#xff08;node.js&#xff09;就不行。 之前用了代理&#xff0c;所以浏览器发送的post请求不会被拦截&#xff0c;…...

数据结构期末算法复习:树、查找、排序

一、树 1.二叉链-定义 typedef struct BiTNode{ ElemType data;//数据域 struct BiTNode *lchild ,*rchild;//左、右孩子指针 }BiTNode , *BiTree ;2.查找值为x的结点 BTNode FindNode(BTNode b&#xff0c;ElemType x) { BTNode *p;if (bNULL) return NULL;else if (…...

复习打卡Linux篇

目录 1. Linux常用操作命令 2. vim编辑器 3. 用户权限 4. Linux系统信息查看 1. Linux常用操作命令 基础操作&#xff1a; 命令说明history查看历史执行命令ls查看指定目录下内容ls -a查看所有文件 包括隐藏文件ls -l ll查看文件详细信息&#xff0c;包括权限类型时间大小…...

OpenAI API深度解析:参数、Token、计费与多种调用方式

随着人工智能技术的飞速发展&#xff0c;OpenAI API已成为许多开发者和企业的得力助手。本文将深入探讨OpenAI API的参数、Token、计费方式&#xff0c;以及如何通过Rest API&#xff08;以Postman为例&#xff09;、Java API调用、工具调用等方式实现与OpenAI的交互&#xff0…...

Centos7 部署ZLMediakit

1、拉取代码 #国内用户推荐从同步镜像网站gitee下载 git clone --depth 1 https://gitee.com/xia-chu/ZLMediaKit cd ZLMediaKit #千万不要忘记执行这句命令 git submodule update --init 2、安装编译器 sudo yum -y install gcc 3、安装cmake sudo yum -y install cmake 4…...

python:用 sklearn.metrics 评价 K-Means 聚类模型

sklearn 的 metrics 模块提供的聚类模型评价指标如下&#xff1a; ARI 评价法&#xff08;兰德系数&#xff09;: adjusted_rand_score AMI 评价法&#xff08;相互信息&#xff09;: adjusted_mutual_info_score V-measure 评分 : completeness_score FMI 评价法 : fowlkes_m…...

谁说C比C++快?

看到这个问题&#xff0c;我我得说&#xff1a;这事儿没有那么简单。 1. 先把最大的误区打破 "C永远比C快" —— 某位1990年代的程序员 这种说法就像"自行车永远比汽车省油"一样荒谬。我们来看个例子&#xff1a; // C风格 char* str (char*)malloc(100…...

算法刷题Day23:BM60 括号生成

题目链接 描述&#xff1a;给出n对括号&#xff0c;请编写一个函数来生成所有的由n对括号组成的合法组合。 例如&#xff0c;给出n3&#xff0c;解集为&#xff1a; “((()))”, “(()())”, “(())()”, “()()()”, “()(())” 思路&#xff1a; 回溯左子树不断添加‘&#…...

基于Redis实现令牌桶算法

基于Redis实现令牌桶算法 令牌桶算法算法流程图优点缺点 实现其它限流算法 令牌桶算法 令牌桶是一种用于分组交换和电信网络的算法。它可用于检查数据包形式的数据传输是否符合定义的带宽和突发性限制&#xff08;流量不均匀或变化的衡量标准&#xff09;。它还可以用作调度算…...