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

glibc内存管理ptmalloc

1、前言

今天想谈谈ptmalloc如何为应用程序分配释放内存的,基于以下几点原因才聊它:

  1. C/C++ 70%的问题是内存问题。了解一点分配器原理对解决应用程序内存问题肯定有帮助。
  2. C++也在用ptmalloc. 当你在C++中new一个对象时,底层还是依赖glibc中的ptmalloc.
  3. 虽然市面上还有jemalloc/tcmalloc, 但ptmalloc被glibc内嵌,用的最广.

2、初识ptmalloc

ptmalloc是glibc(GNU C库)中使用的内存分配器,它基于dlmalloc(Doug Lea’s Malloc)的设计。ptmalloc的主要目标是为多线程应用程序提供高效的内存分配和释放,其名称中的“pt”代表“pthreads”,即POSIX线程库。

ptmalloc所有版本的源代码可在Index of /gnu/glibc下载,其中2.26为了增强多线程情况下的性能引入了tcache, 不过为了使讲解简单,我们还是以2.26之前的版本2.17来分析其原理。

相信很多程序员都思考过一个问题而且也知道答案:

free函数释放内存时只有一个参数 -- 要释放内存的指针,那要释放多大的内存哪???

答案就是这块内存前面的8个字节(64bit下,之后默认都是64bit)存了这块内存大小,让我们看个例子快速入门一下:

void *p1= malloc(10);
memset(p1,'a',10);

 就像大大小小不同种类的卡车都有个车斗拉东西,ptmalloc的卡车叫malloc chunk用来拉内存, 它有自重,0x602000~0x602010便是自重(大小:0x10字节,有个特例,以后再说),对用户来说浪费掉了不能用来拉东西,还剩0x20-0x10=0x10个字节可以放用户数据,比我们申请的10个字节多一点点,这是故意预留的。

ptmalloc本质就是一个内存缓冲池,缓存的最小单位就是malloc chunk. 之后我们提到chunk或者malloc chunk都是一个东西。即使free的内存实际可能并没有free,而是被ptmalloc管理了起来。

3、卡车的定义 malloc_chunk

通过上面的例子我们看到了卡车的一个零部件:size(卡车大小)。那它有其它零部件吗?让我们看下它的庐山真面目:

struct malloc_chunk
{INTERNAL_SIZE_T prev_size; /* 上一个malloc_chunk的size(如果它是free状态).  */INTERNAL_SIZE_T size;      /*  本malloc_chunk的size,包括16个字节的自重 */struct malloc_chunk *fd; /* 用来构造双向链表 -- 仅仅用在free的chunk上. */struct malloc_chunk *bk; /* 用来构造双向链表 -- 仅仅用在free的chunk上. *//* 以下两个字段用在large chunk上,以后介绍.  */struct malloc_chunk *fd_nextsize; struct malloc_chunk *bk_nextsize;
};

 正如代码组织所暗示,两两一组, prev_size & size, fd & bk, fd_nextsize & bk_nextsize. 

后两组只有在本chunk free的状态下才有意义,不然就存储用户数据。

3.1、prev_size & size 物理相邻的chunk

prev_size表示上个chunk的大小,只有上个chunk是free时才有此意义。size表示本chunk大小,但是最后三位从最不重要的位开始由特别意义分别表示:

  1. 物理上上个chunk在用(非free, PREV_INUSE)
  2. 本chunk是由mmap()分配的(IS_MMAPPED)
  3. 本chunk是属于非主arena的(NON_MAIN_ARENA)

我们着重讲下第一个PREV_INUSE:

所有的chunk在物理上都是相邻的,就像火车车厢一样连在一起,

不过与直觉相反的是:不是自己表示自己空不空,而是由下一个chunk表示上一个chunk空不空(原因在下面的总结中)。

下面看个例子理解下PREV_INUSE、prev_size:

int main(int argc, char* argv[])
{void *p1= malloc(192); //192是故意的,较小的数字free后会到fastbin中//PREV_INUSE依然保持1,那样达不到演示PREV_INUSE的效果memset(p1,'a',192);    //原因在fastbin中介绍void *p2= malloc(20);memset(p2,'b',20);free(p1);
}

调试到free但还没free,通过下图理解下p2所在malloc chunk前两个字段的意义。

(gdb) p *(mchunkptr)(p2-16)
$2 = {prev_size = 0, size = 33, fd = 0x6262626262626262, bk = 0x6262626262626262, fd_nextsize = 0x62626262,bk_nextsize = 0x20f11}

然后把p1释放,再看下p2 malloc chunk.size:

 

(gdb) p *(mchunkptr)(p2-16)
$3 = {prev_size = 208, size = 32, fd = 0x6262626262626262, bk = 0x6262626262626262, fd_nextsize = 0x62626262,bk_nextsize = 0x20f11}

这里眼尖的同学可能已经注意到prev_size只有在上个chunk free的状态下有意义,如果上个chunk分配给用户了则什么也没存,浪费掉了!ptmalloc的作者早就想到了这一点:prev_size这8个字节还真可以让渡给上个chunk用来存用户数据,我们把上面程序中的192改成194或者200看一看

  

总结一下: 

1. prev size, prev inuse中的prev指的是物理上相邻的两个chunk的前一个(内存地址小的那个)

2. size字段指本chunk自己的size大小,包括自重(overhead)。size的最不重要的三位(bit)是三个flag,其中最后那个位(bit)表示上个chunk是否free,如果上个chunk是free则prev_size有意义且表示上个chunk的size。为什么要在一个chunk里存上一个chunk的size哪?这是为了方便两者都是free时好合并成一个大的chunk, p - prev_size就指向了上一个chunk。

3. malloc_chunk虽然有这么多数据成员,但只有size永远有意义,为了提高负载率其它字段在某种情况下会被用来放用户数据:a. 本chunk假如分配出去了(malloc),则fd、bk、fd_nextsize、bk_nextsize都无意义,可以用来放用户数据; b. 上个chunk假如分配出去了(malloc)且malloc的大小%8<=8, 则本chunk的prev_size字段会被用来存储上个chunk的用户数据。

3.2、fd & bk 逻辑上把free的chunk串起来

ptmalloc中有个bin的概念,正如字面意思就是回收垃圾用的垃圾桶(放free掉的chunk),bin有如下几类:

fastbin - 单向链表,放小chunk,默认为小到0x20大到0x80大小的,以16字节递进。比如main_arena.fastbinsY[0]指向大小为0x20的chunk链表,main_arena.fastbinsY[1]指向大小为0x30的chunk链表...

unsortedbin - 双向链表,临时垃圾桶,里面的chunk的size不一致。

smallbin - 双向链表,也是放小chunk,但上限到0x3F0, 以16字节递进, 共62个smallbin。

largebin - 双向链表,放大chunk, 63个.(largebin本节不具体展开)

main_arena是一个全局变量,它是一个很好的入口去找到这些bin,如下图示:

4、fastbin

fastbin, 正如它的名字,是用的最频繁的bin。当一块小内存被释放后,极大可能会被放到对应大小的fastbin链条上的,以加快下次分配同样大小的内存。

void* p1 = malloc(10);
free(p1);
void* p2 = malloc(10);

上面这段代码,p1 p2应该是相等的。 

fastbin是单链表,fd指向下一个;先释放的在链尾,后释放的放在链头。也就是说那些最近别使用过得内存更容易被再次使用;那些很久以前free掉最近都没用过的内存,有较大的概率被swap out了,重新使用的代价可能较大。

下面看个例子:

int main(int argc, char* argv[])
{void *p1= malloc(10);memset(p1,'a',10);void *p2= malloc(10);memset(p2,'b',10);void *p3= malloc(30);memset(p3,'c',10);void *p4= malloc(30);memset(p4,'d',10);free(p1);free(p2);free(p3);free(p4);getchar();
#链接我自己编译出来的glibc
[mzhai]$ gcc fastbin.c -I/usr/local/glibc-2.17/include -L/usr/local/glibc-2.17/lib -Wl,--rpath=/usr/local/glibc-2.17/lib -Wl,--rpath=/lib64 -Wl,--dynamic-linker=/usr/local/glibc-2.17/lib/ld-2.17.so -g
[mzhai]$ gdb ./a.out

运行到getchar, 看下fastbinsY[0] & [1],

(gdb) p main_arena
$2 = {mutex = 0, flags = 0, fastbinsY = {0x602020, 0x602070, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, top = 0x6020a0,last_remainder = 0x0, bins = {0x7ffff7dd7678 <main_arena+88>, 0x7ffff7dd7678 <main_arena+88>,#chunk大小为0x20的链表. 
(gdb) p /x *(mchunkptr)0x602020
$7 = {prev_size = 0x0, size = 0x21, fd = 0x602000, bk = 0x6262, fd_nextsize = 0x0, bk_nextsize = 0x31}
(gdb) p /x *(mchunkptr)0x602000
$8 = {prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x6161, fd_nextsize = 0x0, bk_nextsize = 0x21}#chunk大小为0x30的链表。
(gdb) p /x *(mchunkptr)0x602070
$9 = {prev_size = 0x0, size = 0x31, fd = 0x602040, bk = 0x6464, fd_nextsize = 0x0, bk_nextsize = 0x0}
(gdb)  p /x *(mchunkptr)0x602040
$10 = {prev_size = 0x0, size = 0x31, fd = 0x0, bk = 0x6363, fd_nextsize = 0x0, bk_nextsize = 0x0}

 

fastbin共有最多10个链表 ,已由arena.fastbinY[10]限制死,默认7个。用户能通过mallopt(M_MXFAST, value)个性化设置链表个数,但意义不大,最多也就10个。

 5、unsortedbin 

双向链表,临时垃圾桶,里面的chunk的size不一致。

是第二常用的bin,free时没进fastbin则大概率要进unsortedbin. 之后的malloc可能会把unsortedbin上的chunk挪到smallbin.

int main(int argc, char* argv[])
{void *p1= malloc(200);memset(p1,'a',200);void *temp1= malloc(10); //把p1 p2隔开,防止合并memset(temp1,'b',10);void *p2= malloc(300);memset(p2,'a',300);void *temp2= malloc(10); //把p2 top 隔开,防止合并memset(temp2,'c',10);free(p1);free(p2);getchar();

调试到getchar, 

(gdb) p main_arena
$1 = {mutex = 0, flags = 1, fastbinsY = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, top = 0x602250,
  last_remainder = 0x0, bins = {0x6020f0, 0x602000

(gdb) p *(mchunkptr)0x6020f0
$4 = {prev_size = 0, size = 321, fd = 0x602000, bk = 0x7ffff7dd7678 <main_arena+88>, fd_nextsize = 0x6161616161616161,
  bk_nextsize = 0x6161616161616161}
(gdb) p *(mchunkptr)0x602000
$5 = {prev_size = 0, size = 209, fd = 0x7ffff7dd7678 <main_arena+88>, bk = 0x6020f0, fd_nextsize = 0x6161616161616161,
  bk_nextsize = 0x6161616161616161}

6、smallbin

 双向链表,也是放小chunk,但上限到0x3F0, 以16字节递进, 共62个smallbin。

smallbin与unsortedbin相似,都是双向链表,不同的是:smallbin每个链上chunk大小相等,这一点与fastbin一致。

free时不会直接往smallbin里扔chunk,而是malloc时把unsortedbin里的chunk整理到对应的smallbin链上。

请看下面的例子:

int main(int argc, char* argv[])
{void *p1= malloc(30);void *temp1= malloc(10);void *p2= malloc(30);void *temp2= malloc(10);void *p3= malloc(30);void *temp3= malloc(10);free(p1);free(p2);free(p3); //fastbin 0x30链表上p3->p2->p1void* large = malloc(1024); //fastbin->unsortedbin->smallbingetchar();

free(p3)后

(gdb) p main_arena
$1 = {mutex = 0, flags = 0, fastbinsY = {0x0, 0x6020a0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, top = 0x6020f0,
  last_remainder = 0x0, bins = {0x7ffff7dd7678 <main_arena+88>, 0x7ffff7dd7678 <main_arena+88>,
    0x7ffff7dd7688 <main_arena+104>, 0x7ffff7dd7688 <main_arena+104>, 0x7ffff7dd7698 <main_arena+120>,

(gdb) p *(mchunkptr)0x6020a0
$2 = {prev_size = 0, size = 49, fd = 0x602050, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0}
(gdb) p *(mchunkptr)0x602050
$3 = {prev_size = 0, size = 49, fd = 0x602000, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0}
(gdb) p *(mchunkptr)0x602000
$4 = {prev_size = 0, size = 49, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0} 

之后要分配1024字节,加上自重共1040个字节,借助这个malloc我们过一下malloc的大体流程:

1. 首先从fastbin中寻找。因为1040大于fastbin的范围(0x20~0x80/0xa0),找不到。

if ((unsigned long)(nb) <= (unsigned long)(get_max_fast ())) 

2. 从smallbin中寻找。1040已超过smallbin的范围。

3315      if (in_smallbin_range(nb))
3355      else {                                                                                                      
3356        idx = largebin_index(nb);                                                                                 
3357        if (have_fastchunks(av))                                                                                  
3358          malloc_consolidate(av); //fastbin -> unsortedbin   //只有要申请的字节数>=1024 且 fastbin不空 才触发 fastbin -> unsortedbin                                                                          //这才是我们的例子中要申请1024字节的原因
3359      }               

(gdb) p main_arena
$7 = {mutex = 1, flags = 1, fastbinsY = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, top = 0x6020f0,
  last_remainder = 0x0, bins = {0x602000, 0x6020a0, 0x7ffff7dd7688 <main_arena+104>, 0x7ffff7dd7688 <main_arena+104>,
    0x7ffff7dd7698 <main_arena+120>, 0x7ffff7dd7698 <main_arena+120>, 0x7ffff7dd76a8 <main_arena+136>,

(gdb) p *(mchunkptr)0x602000
$8 = {prev_size = 0, size = 49, fd = 0x602050, bk = 0x7ffff7dd7678 <main_arena+88>, fd_nextsize = 0x0, bk_nextsize = 0x0}
(gdb) p *(mchunkptr)0x602050
$9 = {prev_size = 0, size = 49, fd =
0x6020a0, bk = 0x602000, fd_nextsize = 0x0, bk_nextsize = 0x0}
(gdb) p *(mchunkptr)0x6020a0
$10 = {prev_size = 0, size = 49, fd =
0x7ffff7dd7678 <main_arena+88>, bk = 0x602050, fd_nextsize = 0x0, bk_nextsize = 0x0}

fastbin上的3个chunk 已被move到 unsortedbin上

3. 整理unsortedbin上的chunk到smallbin/largebin上

3377        while ( (victim = unsorted_chunks(av)->bk) != unsorted_chunks(av)) { 
//从unsortedbin的最后一个chunk开始,unsorted_chunks(av)是那个虚拟chunk3422          /* 把victic从unsortedbin上摘下来 */ 
3423          unsorted_chunks(av)->bk = bck;
3424          bck->fd = unsorted_chunks(av); 3439         /* 把victim放到相应的bin上(smallbin、largebin) */                       
3440                                                        
3441          if (in_smallbin_range(size)) {                
3442            victim_index = smallbin_index(size);        
3443            bck = bin_at(av, victim_index);             
3444            fwd = bck->fd;                              
3445          }                                             
3446          else {                                        
3447            victim_index = largebin_index(size);        
3448            bck = bin_at(av, victim_index);             
3449            fwd = bck->fd;               

4. 搜索largebin

5. 从top chunk割一块下来

 chunk move的过程,fastbin -> unsortedbin -> smallbin/largebin

​​​​​​​

虽然这个例子很好的演示了chunk move的过程,但并没有诠释move的意义:正如malloc_consolidate名字所暗示的,它会合并fastbin中相邻的chunk以减少碎片,要知道fastbin的哲学是期盼同样小的内存申请很快到来,所以它不会合并相邻的chunk,这样时间长了碎片就会很多。看下面这个例子:

int main(int argc, char* argv[])
{void *p1= malloc(30);void *temp1= malloc(10);void *p2= malloc(30);void *temp2= malloc(10);void *p3= malloc(30);void *temp3= malloc(10);free(p1);free(p2);free(p3);void* large = malloc(1024);

(gdb) p main_arena
$1 = {mutex = 0, flags = 1, fastbinsY = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, top = 0x6024d0,
  last_remainder = 0x0, bins = {0x7ffff7dd7678 <main_arena+88>, 0x7ffff7dd7678 <main_arena+88>,
    0x7ffff7dd7688 <main_arena+104>, 0x7ffff7dd7688 <main_arena+104>, 0x7ffff7dd7698 <main_arena+120>,
    0x7ffff7dd7698 <main_arena+120>, 0x7ffff7dd76a8 <main_arena+136>, 0x7ffff7dd76a8 <main_arena+136>,
    0x7ffff7dd76b8 <main_arena+152>, 0x7ffff7dd76b8 <main_arena+152>, 0x7ffff7dd76c8 <main_arena+168>,
    0x7ffff7dd76c8 <main_arena+168>, 0x7ffff7dd76d8 <main_arena+184>, 0x7ffff7dd76d8 <main_arena+184>,
    0x7ffff7dd76e8 <main_arena+200>, 0x7ffff7dd76e8 <main_arena+200>, 0x602000, 0x602000,

(gdb) p *(mchunkptr)0x602000
$2 = {prev_size = 0,
size = 145, fd = 0x7ffff7dd76f8 <main_arena+216>, bk = 0x7ffff7dd76f8 <main_arena+216>,
  fd_nextsize = 0x0, bk_nextsize = 0x0}

三个相邻的小碎片被 合体了!

相关文章:

glibc内存管理ptmalloc

1、前言 今天想谈谈ptmalloc如何为应用程序分配释放内存的&#xff0c;基于以下几点原因才聊它&#xff1a; C/C 70%的问题是内存问题。了解一点分配器原理对解决应用程序内存问题肯定有帮助。C也在用ptmalloc. 当你在C中new一个对象时&#xff0c;底层还是依赖glibc中的ptma…...

HarmonyOS入门学习

HarmonyOS入门学习 前言快速入门ArkTS组件基础组件Image组件Text组件TextInput 文本输入框Buttonslider 滑动组件 页面布局循环控制ForEach循环创建组件 List自定义组件创建自定义组件Builder 自定义函数 状态管理Prop和LinkProvide和ConsumeObjectLink和Observed ArkUI页面路由…...

【Mock|JS】Mock的get传参+获取参数信息

mockjs的get传参 前端请求 const { data } await axios("/video/childcomments", {params: {sort: 1,start: 2,count: 5,childCount: 6,commenIndex: 0,},});后端获取参数 使用正则匹配url /*** # 根据url获取query参数* param {Url} urlStr get请求获取参数 eg:…...

spring cloud gateway k8s优雅启停

通过配置readiness探针和preStop hook&#xff0c;实现优雅启动和停止&#xff08;滚动部署&#xff09; 1. k8s工作负载配置 readinessProbe:httpGet:path: /datetimeport: 8080scheme: HTTPinitialDelaySeconds: 30timeoutSeconds: 1periodSeconds: 30successThreshold: 1fa…...

嵌入式软件面试-linux-中高级问题

Linux系统启动过程&#xff1a; BIOS自检并加载引导程序。引导程序&#xff08;如GRUB&#xff09;加载Linux内核到内存。内核初始化硬件&#xff0c;加载驱动&#xff0c;建立内存管理。加载init进程&#xff08;PID为1&#xff09;&#xff0c;通常是systemd或SysVinit。init…...

css禁用元素指针事件,鼠标穿透,点击下层元素,用`pointer-events:none;`

pointer-events: 对鼠标事件的反应 MDN pointer-events 英文 https://developer.mozilla.org/en-US/docs/Web/CSS/pointer-events 菜鸟教程 CSS pointer-events 属性 https://www.runoob.com/cssref/css3-pr-pointer-events.html 常用取值 auto 和 none pointer-events: aut…...

Eureka的介绍和作用,以及搭建

一、Eureka的介绍和作用 Eureka是Netflix开源的一种服务发现和注册工具&#xff0c;它为分布式系统中的服务提供了可靠的服务发现和故障转移能力。Eureka是Netflix的微服务架构的关键组件之一&#xff0c;它能够实时地监测和管理服务实例的状态和可用性。 在Eureka架构中&…...

shell和linux的关系

Shell 和 Linux 之间存在密切的关系&#xff0c;但它们并不是同一个东西。让我们分别了解一下它们&#xff1a; Linux&#xff1a; Linux 是一个自由和开放源代码的类UNIX操作系统。 Linux 的内核由林纳斯托瓦兹&#xff08;Linus Torvalds&#xff09;于1991年首次发布&…...

数据在内存的存储

整数在内存中的存储 我们来回顾一下&#xff0c;整数在计算机是以补码的形式进行存储的&#xff0c;整数分为正整数和负整数&#xff0c;正整数的原码、反码和补码是一样的&#xff0c;负整数的原码、反码和补码略有不同&#xff08;反码是原码除符号位&#xff0c;其他位按位取…...

JavaScript之ES中的类继承与Promise

类 ES5中的类及继承 //人function Person(name,age){this.name name;this.age age;}Person.prototype.eat function () {console.log(this.name "eat");}//程序员&#xff0c;继承&#xff0c;人function Programmer(name,age,language){//构造函数继承Person.…...

​浅析多模态大模型技术路线梳理

前段时间 ChatGPT 进行了一轮重大更新&#xff1a;多模态上线&#xff0c;能说话&#xff0c;会看图&#xff01;微软发了一篇长达 166 页的 GPT-4V 测评论文&#xff0c;一时间又带起了一阵多模态的热议&#xff0c;随后像是 LLaVA-1.5、CogVLM、MiniGPT-5 等研究工作紧随其后…...

使用 Amazon SageMaker 微调 Llama 2 模型

本篇文章主要介绍如何使用 Amazon SageMaker 进行 Llama 2 模型微调的示例。 这个示例主要包括: Llama 2 总体介绍Llama 2 微调介绍Llama 2 环境设置Llama 2 微调训练 前言 随着生成式 AI 的热度逐渐升高&#xff0c;国内外各种基座大语言竞相出炉&#xff0c;在其基础上衍生出…...

牛客小白月赛86(D剪纸游戏)

题目链接:D-剪纸游戏_牛客小白月赛86 (nowcoder.com) 题目描述: 输入描述: 输入第一行包含两个空格分隔的整数分别代表 n 和 m。 接下来输入 n行&#xff0c;每行包含 m 个字符&#xff0c;代表残缺纸张。 保证&#xff1a; 1≤n,m≤10001 字符仅有 . 和 * 两种字符&#xf…...

MySQL的基础操作与管理

一.MySQL数据库基本操作知识&#xff1a; 1.SQL语句&#xff1a; 关系型数据库&#xff0c;都是使用SQL语句来管理数据库中的数据。 SQL&#xff0c;即结构化查询语言(Structured Query Language) 。 SQL语句用于维护管理数据库&#xff0c;包括数据查询、数据更新、访问控…...

Pytorch 中的forward 函数内部原理

PyTorch中的forward函数是nn.Module类的一部分&#xff0c;它定义了模型的前向传播规则。当你创建一个继承自nn.Module的类时&#xff0c;你实际上是在定义网络的结构。forward函数是这个结构中最关键的部分&#xff0c;因为它指定了数据如何通过网络流动。 单独设计 forward …...

四、C语言中的数组:如何输入与输出二维数组(数组,完)

本章的学习内容如下 四、C语言中的数组&#xff1a;数组的创建与初始化四、C语言中的数组&#xff1a;数组的输入与元素个数C语言—第6次作业—十道代码题掌握一维数组四、C语言中的数组&#xff1a;二维数组 1.二维数组的输入与输出 当我们输入一维数组时需要一个循环来遍历…...

基于python+vue智慧农业小程序flask-django-php-nodejs

传统智慧农业采取了人工的管理方法&#xff0c;但这种管理方法存在着许多弊端&#xff0c;比如效率低下、安全性低以及信息传输的不准确等&#xff0c;同时由于智慧农业中会形成众多的个人文档和信息系统数据&#xff0c;通过人工方法对知识科普、土壤信息、水质信息、购物商城…...

好用的GPTs:指定主题搜索、爬虫、数据清洗、数据分析自动化

好用的GPTs&#xff1a;指定主题搜索、爬虫、数据清洗、数据分析自动化 Scholar&#xff1a;搜索 YOLO小目标医学方面最新论文Scraper&#xff1a;爬虫自动化数据清洗数据分析 点击 Explore GPTs&#xff1a; Scholar&#xff1a;搜索 YOLO小目标医学方面最新论文 搜索 Scho…...

使用Qt自带windeployqt打包QML的exe

1.在开始菜单输入CMD找到对应的Qt开发版本&#xff0c;我的是Qt5.15.2(MinGW 8.1.0 64-bit)。 2.在控制台输入如下字符串&#xff0c;格式为 windeployqt exe绝对路径 --qmldir 工程的绝对路径 如下是我的打包代码。 我需要打包的exe的绝对路径 D:\Prj\Code\Demo\QML\Ana…...

C代码快速傅里叶变换-分类和推理-常微分和偏微分方程

要点 C代码例程函数计算实现&#xff1a; 线性代数方程解&#xff1a;全旋转高斯-乔丹消元&#xff0c;LU分解前向替换和后向替换&#xff0c;对角矩阵处理&#xff0c;任意矩阵奇异值分解&#xff0c;稀疏线性系统循环三对角系统解&#xff0c;将矩阵从完整存储模式转换为行索…...

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…...

HTML 语义化

目录 HTML 语义化HTML5 新特性HTML 语义化的好处语义化标签的使用场景最佳实践 HTML 语义化 HTML5 新特性 标准答案&#xff1a; 语义化标签&#xff1a; <header>&#xff1a;页头<nav>&#xff1a;导航<main>&#xff1a;主要内容<article>&#x…...

7.4.分块查找

一.分块查找的算法思想&#xff1a; 1.实例&#xff1a; 以上述图片的顺序表为例&#xff0c; 该顺序表的数据元素从整体来看是乱序的&#xff0c;但如果把这些数据元素分成一块一块的小区间&#xff0c; 第一个区间[0,1]索引上的数据元素都是小于等于10的&#xff0c; 第二…...

Oracle查询表空间大小

1 查询数据库中所有的表空间以及表空间所占空间的大小 SELECTtablespace_name,sum( bytes ) / 1024 / 1024 FROMdba_data_files GROUP BYtablespace_name; 2 Oracle查询表空间大小及每个表所占空间的大小 SELECTtablespace_name,file_id,file_name,round( bytes / ( 1024 …...

WEB3全栈开发——面试专业技能点P2智能合约开发(Solidity)

一、Solidity合约开发 下面是 Solidity 合约开发 的概念、代码示例及讲解&#xff0c;适合用作学习或写简历项目背景说明。 &#x1f9e0; 一、概念简介&#xff1a;Solidity 合约开发 Solidity 是一种专门为 以太坊&#xff08;Ethereum&#xff09;平台编写智能合约的高级编…...

让AI看见世界:MCP协议与服务器的工作原理

让AI看见世界&#xff1a;MCP协议与服务器的工作原理 MCP&#xff08;Model Context Protocol&#xff09;是一种创新的通信协议&#xff0c;旨在让大型语言模型能够安全、高效地与外部资源进行交互。在AI技术快速发展的今天&#xff0c;MCP正成为连接AI与现实世界的重要桥梁。…...

【JavaWeb】Docker项目部署

引言 之前学习了Linux操作系统的常见命令&#xff0c;在Linux上安装软件&#xff0c;以及如何在Linux上部署一个单体项目&#xff0c;大多数同学都会有相同的感受&#xff0c;那就是麻烦。 核心体现在三点&#xff1a; 命令太多了&#xff0c;记不住 软件安装包名字复杂&…...

RSS 2025|从说明书学习复杂机器人操作任务:NUS邵林团队提出全新机器人装配技能学习框架Manual2Skill

视觉语言模型&#xff08;Vision-Language Models, VLMs&#xff09;&#xff0c;为真实环境中的机器人操作任务提供了极具潜力的解决方案。 尽管 VLMs 取得了显著进展&#xff0c;机器人仍难以胜任复杂的长时程任务&#xff08;如家具装配&#xff09;&#xff0c;主要受限于人…...

消息队列系统设计与实践全解析

文章目录 &#x1f680; 消息队列系统设计与实践全解析&#x1f50d; 一、消息队列选型1.1 业务场景匹配矩阵1.2 吞吐量/延迟/可靠性权衡&#x1f4a1; 权衡决策框架 1.3 运维复杂度评估&#x1f527; 运维成本降低策略 &#x1f3d7;️ 二、典型架构设计2.1 分布式事务最终一致…...

五子棋测试用例

一.项目背景 1.1 项目简介 传统棋类文化的推广 五子棋是一种古老的棋类游戏&#xff0c;有着深厚的文化底蕴。通过将五子棋制作成网页游戏&#xff0c;可以让更多的人了解和接触到这一传统棋类文化。无论是国内还是国外的玩家&#xff0c;都可以通过网页五子棋感受到东方棋类…...