c++两个vector合并_干货 | 名企高频考点之C++如何避免vector的动态扩容,为何是2倍(下)...

本文深入探讨了C++标准模板库STL中vector的扩容机制,特别是在Windows和Linux环境下不同的实现方式。Windows环境下,由于堆内存管理使用空闲双向链表和快速链表,vector通常选择1.5倍扩容,以便利用已释放的空间。而在Linux下,通过glibc的ptmalloc管理内存,采用bin和伙伴系统,倾向于2倍扩容,这与Linux内核的内存分配策略相吻合。文章分析了这两种扩容策略背后的考量,并提出了可能的原因。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

205c042cb28e36242eeb15494a9f99fc.png

点击蓝字关注我哦

a8302f067348c213904535bf0fceb637.png以下是本期干货视频视频后还附有文字版本哦e00e9abd41c801d10e49989e04d3494c.pnga1814a1b8cac13ed4b55ca5d49fd7666.png

▼《名企高频考点-C++如何避免vector的动态扩容,为何是2倍(下)》▼

ps:请在WiFi环境下打开,如果有钱任性请随意

01

前情回顾

上文中主要讨论了vector扩容机制、为什么选择以倍数方式扩容,为什么一般选择倍数小于2倍。由于STL并没有规定vector具体的扩容方式,选择以1.5倍方式还是以2倍方式扩容,我觉得与实现STL操作系统的堆内存管理方式有关系,因此各个版本的STL在实现时可能会考虑所在系统的堆管理方式,以下原因仅为个人见解,欢迎一起讨论。

02

vs为什么选择1.5倍方式扩容

在windows中,已释放的或未被申请的堆空间使用堆表进行管理,最重要的堆表有两种:空闲双向链表FreeList(以下简称空表)和快速单向链表Lookaside(以下简称快表)。

空表

按照内存块大小的不同,空表总共被分为128条,free[1]~free[127]每个空表中管理的都是8的整数倍的堆内存块,逐次递增,将空闲堆块按照大小不同链入不同的空表中,来方便堆管理系统高效检索指定大小的堆空间。free[0]管理大于1024byte小于512K的堆块,这些堆块按照大小在free[0]中依次排列下去。

b1604a7d135b59b8d9d170d05f054f03.png

快表

快表是windows用来加速堆块分配而采用的一种堆表,称为快表是因为:该单向链表中永远不会发生堆块合并,快表也有128条,与空表不同的是每条快表最多只有4个节点

ff5740b51e7e60bc9d78b6e28d6bb80b.png

经过反复的申请与释放操作后,堆区很可能变得"千疮百孔",产生很多碎片,为了合理有效的利用内存,堆管理系统还要能够进行堆块合并。当堆管理系统发现两个空闲堆块批次相邻的时候,就会进行堆块合并操作

dd75a0257b16c944376a96b68571d92c.png

windows中堆管理系统会时时对释放的堆块进行合并,因此:vs下的vector扩容机制选择使用1.5倍方式进行扩容,这样多次扩容之后,就可以使用之前已经释放的空间了

03

linux为什么选择2倍方式扩容

linux下主要使用glibc中ptmalloc来进行空间申请的。如果malloc的空间小于128KB,其内部通过brk()来扩张,如果大于128KB且arena中没有足够的空间时,通过mmap将内存空间映射到进程地址空间。每次申请出来的小块内存,对应一个chunk,根据作用不同,chunk(内存块的意思)总共分为4类:

Allocated chunk:即分配给用户且未释放的内存块

Free chunk:即用户已经释放的内存块

Top chunk:一个arena顶部的chunk叫做Top chunk,它不属于bin,当所有bin中都没有可用的chunk时,切割Top chunk来满足用户内存申请。

Last Remainder chunk:当需要分配一个较小的k字节的chunk但是small bins中找不到满足要求的,且Last Remainder chunk的大小N能满足要求,那么Last Remainder chunk将被切割。

b96a442d12858cd8c356666b8d729373.png

f308ec6cc39297d6c4123747d1d202c3.png

651166e30700d875c7032163e56feb4e.png

free()之后的内存不会立即返回给系统,而是被表示为一个Free Chunk,Free Chunk之间通过双向链表链接,被管理在bin中,目的就是为了提高下一次分配的速度,当用户进行下一次分配的时,会首先试图在bins管理的空闲chunk中挑选一块给用户,这样就避免了频繁的系统调用,降低内存开销。bin使用空闲链表的方式来管理空闲的chunk,可分为:

10个fast bins,存储在fastbinsY中

10个LIFO的单链表,相邻空闲chunk不会被合并,主要用于提高小块内存分配和释放效率

8b23c651bb2afdc467d5bbec2ea8821b.png

1个unsorted bin,存储在bin[1]中

大小超过fast bins阈值的chunk被释放时会添加到这里,目的复用最近释放的chunk,从而消除寻找合适bin的时间开销,从而提高内存分配和释放的效率。

f95bfcb47007e578e1f1867688ec089d.png

62个small bins,存储在bin[2]到bin[63]

小于0x200(0x400-64位系统)Byte的chunk叫做small chunk,chunk的大小从16Byte开始,每次递增8字节,small bin是就存储这些小chunk,small bin是62个双向循环链表,与fast bin是不同的是:small bins是FILO的,且相邻空闲的chunk会被合并

chunk大小:0x10~0x1f0Byte范围(0x20~0x3f0---64位系统),相邻bin存放的chunk大小相差0x8(0x10)Byte

63个large bins,存储在bin[64]到bin[126]

大于等于0x200(0x400---64为系统)Byte的chunk叫做large chunk,存放在large bins中。large bins是63个双向循环链表,插入和删除可以发生在任意位置,相邻空闲chunk也会被合并。chunk大小比较复杂:

1、前32个bins:从0x200Byte开始每次加0x40Byte2、接下来的16个bins:每次加0x200Byte3、接下来8个bins:每次加0x1000Byte4、接下来4个bins:每次加0x8000Byte5、接下来2个bins:每次加0x4000Byte6、最后一个bin:只有一个chunk,大小和large bins剩余的大小相同

注意:同一个bin中的chunks不是相同大小的,按大小降序排列,在分配取出时遵循best-fit原则,即取出满足大小最小的chunk

以上为linux中管理释放内存块的方式,即在对chunk进行管理时,也会进行合并,既然会合并按道理讲应该按照1.5倍方式扩容才比较合适,多次申请释放之后可以利用之前被释放的空间,但为什么SGI-STL的vector选择2倍方式扩容?我猜想:Linux中引入伙伴系统为内核提供了一种用于分配一些连续的页而建立的一种高效的分配策略,对固定分区和动态分区方式的折中,固定分区存在内部碎片,动态分区存在外部碎片,而且动态分区回收时的合并以及分配是的切分是比较耗时的。伙伴系统是将整个内存区域构建成基本大小basicSize的1倍、2倍、4倍、8倍、16倍等,即要求内存空间分区链均对应2的整次幂倍大小的空间,整齐划一,有规律的而不是乱糟糟的。在分配和释放空间时,可以通过log2(request/basicSize)向上取整的哈希算法快速找到对应内存块。

48b8bdbfd01cf1a623426253e9193f18.png

a742d823bbbc0f1717663a5bcac37c01.png

ba783cfcf34566a230e3baaff290d45c.png

d44d5b225ceedbc68b1196dbffddd7a8.png

30beec31f1b1547518bc1dd7536f2702.png

7ffd21a3d257f2abf7081c5d3e57a38d.png

9724e718fbf553ff2f5b55fcdc04a315.png

c2ae7eb43bfd5a08a350c0f5be78a41a.png

a01320ecd4dbe246a73be89e4d05f932.png

通过伙伴系统管理空闲分区的了解,可以看到在伙伴系统中的每条空闲分区链中挂的都是2i的页面大小,通过哈希思想进行空间分配与合并,非常高效。估计可能是这个原因SGI-STL选择以2倍方式进行扩容。

04

总结

本节课我们主要讨论了vs为什么选择以1.5倍方式扩容,linux选择以2倍方式扩容,通过本节和上节课的讨论,相信大家对vector扩容机制相关问题已经非常熟悉,希望大家在面试时能得心应手,拿到心仪的offer,谢谢。

作者:时亮益

审稿:王海斌编辑:啊琛琛

641ad6611d6c6b481ca1a497d1f59fe7.png

点亮"在看",点亮"offer"

b208e5a440f90c91cd8eb942bba4327b.gif

                             原创不易,点个赞吧6eb82de8116a373a8fc14613788a01e5.png~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值