malloc和内存池原理

事先声明,两者没有特别的联系。

malloc

结论:
1、当开辟的空间<128k,调用brk函数,主要移动指针 _enddata(此时的_enddata指的是Linux地址空间中堆段的末尾地址,不是数据段的末尾地址)。
2、当开辟的空间大于 128K时,mmap()系统调用函数来在虚拟地址空间中(堆和栈中间,称为“文件映射区域”的地方)找一块空间来开辟。

这两者都是分配虚拟内存,没有分配物理内存。在第一次访问已分配的虚拟地址空间的时候,发生缺页中断,操作系统负责分配物理内存,然后建立虚拟内存和物理内存之间的映射关系。

malloc和内存池原理_第1张图片
并且只有高地址内存释放了,低地址才会释放(<128k的时候)。
释放了之后,如果没有足够的内存,就会合并两个空闲块,如果大了,就切割。
(最后如果>128k,实现内存紧缩)

内存池保存在bin[128]中,每个元素都是个双向链表,每个index表示的是一个范围,bin[1]unsorted_list,用于维护free释放的。
bin[2,63),维护<512字节的。(small_bins)
bin[64,127),维护>512的,同一条链表,按顺序越来越大。(large_bins)

查找合适的内存的时候,使用伙伴算法。
1、<512,定位到smallbins[index],直接返回第一个。
2、>512,定位到largebins[index],找一个最合适的,然后剩下部分放入unsorted_list
3、找unsorted_list,找合适的,否则把他们分割到smalllarge里。
4、如果没找到,就index++从更大的找,直到找到合适的,然后也是拆分。
5、还没有,就使用top_chunk(brk)。

free释放内存时,有两种情况:
chunktop chunk相邻,则和top chunk合并
chunktop chunk不相邻,则直接插入到unsorted_list

mallocnew区别

1、malloc/free只能用于内部数据类型,new/delete可以用于自定义类型,在申请内存空间同时还要调用构造函数进行对象的初始化,以及在对象消亡之前自动调用析构函数。
2、可以认为new操作符是malloc函数加上构造函数执行。new操作符返回的是带类型的对象,而malloc返回的是void*,需要进行类型强制转换。
new -> operator new -> malloc
delete -> operator delete -> free

STL内存池

allocate申请内存,construct调用构造函数。
两级配置器:
第一级配置器:以malloc()free()realloc()等C函数执行实际的内存配置、释放、重新配置等操作,并且能在内存需求不被满足的时候,调用一个指定的函数。
第二级配置器:避免太多小区块造成的内存碎片,小额区块带来的不仅是内存碎片,配置时还有额外的负担。区块越小,额外负担所占比例就越大。

>128,第一级配置器。
<128,第二级配置器,每次配置一大块内存,并维护对应的16个空闲链表(free-list)。下次若有相同大小的内存需求,则直接从free-list中取。如果有小额区块被释放,则由配置器回收到free-list中。

空闲链表

这里的16个空闲链表分别管理大小为8、16、24…120、128的数据块。这里空闲链表节点的设计十分巧妙,这里用了一个联合体既可以表示下一个空闲数据块(存在于空闲链表中)的地址,也可以表示已经被用户使用的数据块(不存在空闲链表中)的地址。
malloc和内存池原理_第2张图片

allocate

首先先要检查申请空间的大小,如果大于128字节就调用第一级配置器,小于128字节就检查对应的空闲链表,如果该空闲链表中有可用数据块,则直接拿来用(拿取空闲链表中的第一个可用数据块,然后把该空闲链表的地址设置为该数据块指向的下一个地址),如果没有可用数据块,则调用refill重新填充空间。

deallocate

首先先要检查释放数据块的大小,如果大于128字节就调用第一级配置器,小于128字节则根据数据块的大小来判断回收后的空间会被插入到哪个空闲链表。
例如回收下面指定位置大小为16字节的数据块,首先数据块的大小判断回收后的数据块应该插入到第二个空闲链表,把该节点指向的下一个地址修改为原链表指向的地址(这里是NULL),然后将原链表指向该节点。

填充

在用allocate配置空间时,如果空闲链表中没有可用数据块,就会调用refill来重新填充空间,新的空间取自内存池。缺省取20个数据块,如果内存池空间不足,那么能取多少个节点就取多少个。

取内存

从内存池取空间给空闲链表用是chunk_alloc的工作:
首先根据end_free-start_free来判断内存池中的剩余空间是否足以调出nobjs个大小为size的数据块出去,如果内存连一个数据块的空间都无法供应,需要用malloc取堆中申请内存。
假如山穷水尽,整个系统的堆空间都不够用了,malloc失败,那么chunk_alloc会从空闲链表中找是否有大的数据块,然后将该数据块的空间分给内存池(这个数据块会从链表中去除)。
如果还不行,就使用第一级配置器,他有out of memory机制,有机会释放内存(客户使用例程)。(否则抛出bad_alloc异常)

你可能感兴趣的:(C++)