c++应用程序:
c++ memory primitives
new(表达式)调用::operator new()(操作符),然后new()(操作符)调用malloc()
array new, array delete
创建时,数组大小是多少,就调用几次构造函数。分配空间时,会多分配一个cookie,用来记载此次分配的长度。每次分配空间是由上往下(低地址到高地址)。
释放内存时,delete后的指针一定要带[], 不然就只会释放一个区域。释放空间时,顺序与分配空间相反,是由下往上(高地址到低地址)。
placement new:允许我们将object建构于已经分配的内存中
重载
重载全局的operator new
重载类内部的operator new
重载的类内部的new(),若参数不唯一,则第一参数的类型必须是size_t,其余参数以new所指定的placement arguments为初值。
这里也可以重载class member operator delete(), 写出多个版本。但是它们绝不会被delete调用。只有当new所调用的clor(构造函数)抛出exception,才会调用这些重载版的operator delete()。目的是清扫未能完全创建成功的object所占用的内存。
allocator
分配内存的途径
1.降低malloc的调用频率,从而减少耗费的时间
2.一次申请很多空间,然后用类中的指针对空间进行分割,这样做可以节省下cookie的空间(每次malloc时都会产生cookie)。
3.设计一个类allocator,该类中存在两个函数,allocate和deallocate。当其他的类需要分配和释放空间时,在类中声明一个allocator对象即可。
new handler:
当operator new没能力分配申请的memory,会抛出一个std::bad_alloc exception.
抛出异常之前会调用一个可由client指定的handler。handler是将异常处理交给client,让client来处理。
handler的作用:
让更多的memory可用
abort()或exit()
=default(使用默认版本), =delete(不需要这个函数)
具有默认版本的函数:拷贝构造函数、拷贝赋值函数,拷贝析构函数
VC6标准分配器之实现
没有任何内存管理的操作,只是简单调用malloc和free
BC5标准分配器之实现
没有任何内存管理操作,只是简单调用malloc和free
G2.9标准分配器之实现
std::allocator没有任何内存管理操作,只是简单调用malloc和free
std::alloc(也就是G4.9的_pool_alloc)
情景一:累计申请量为0,pool大小为0,这时开始申请32bytes的空间。
做法:向pool中在注入32202+RoundUp(0>>4)=1280的空间,并切出20块大小为32bytes的空间,同时分给一块给客户。然后留下640备用。
情景二:累计量为1280,pool的大小为640,此时要申请64bytes的空间。
做法:将pool中的备用部分(640)切割成十份,并分给一块给客户。
情景三:累计量为1280,pool的大小为0,此时要申请96bytes的空间。
做法:向pool中注入96202+RoundUp(1280>>4)的空间,并切除20块大小为96bytes的空间,同时分给一块给客户。留下2000作为备用。
情景四:累计量为5200,pool的大小为2000,此时要申请88bytes的空间。
做法:将pool中备用部分(2000)切割出20份,并分一块给客户,同时剩下240作为备用空间。
情景五:累积量为5200,pool的大小为240,此时要申请三块88bytes的空间。
做法:因为之前已经切割出大小为88bytes的空间,所以直接从88bytes的链表上连续分配三个88bytes的空间给客户。
情景六:累计量为5200,pool的大小为240,此时要申请大小为8bytes的空间。
做法:pool中有余量,从pool中备用部分中切出20块大小为8bytes的空间,并分一块给客户,然后将剩下的80作为备用。
情景七:累积量为5200,pool为80,此时申请104bytes的空间。
做法:pool中有余量但是不够分配,所以先将余量分配给存放大小为80bytes的链表,然后向pool中注入104202+RoundUp(5200>>4)的空间,切出20份大小为104的空间,并分配一份给客户,然后将剩下的2408留作备用。
情景八:累积量为9688,pool为2408,此时申请112的空间。
做法:从pool中切出20个112大小的空间,并分配一个给客户,留下168备用。
情景九:累积量为9688,pool为168,此时申请48的空间。
做法:从pool中切出三个大小为48的空间,分配一个给客户,留下24备用。
情景十:累积量为9688,pool为24,此时申请72的空间。
做法:无可用空间、pool容量不足,则先将备用的24分配给大小为24的链表。然后向系统申请空间,(此时为了表现空间耗尽,设定总空间一共有10000bytes),所以此时不能额外分配空间,则只能从与之相近的80的链表中取出一块,切出72给客户,并将剩下的8作为备用。
情景十一:累积量为9688,pool为8,此时申请72的空间。
做法:与情景十相同,先将pool中的备用部分8bytes分配给大小为8bytes的链表,然后从与之相近的88的链表取出一块(不是从80中取,是因为之前80的链表中只有一个空余,现在已经用完了,所以只能从88中取),切出72分配给客户,剩下16备用。
情景十二:累积量为9688,pool为16,此时申请120的空间。
做法:无与其相近的链表空间,又不能组合小的空间给客户。所以只能先将剩余的16分配给管理大小为16的链表,然后无能为力。
注:pool中切割出的数量(不算备用部分)总是在1-20之间。
RoundUp: 累积量除以16,目的是为了加速空间的分配,并上调到8的倍数。
图解如下:
vc6的内存管理
一个组中,分为64个链表,除了最后一个外,其他的都管理着固定大小的空间链表,最后一个则管理大于1k的空间链表。
第一次内存申请,首先计算出区块的大小(请求分配的内存、cookie、一些指针、分配的内存的上下界定区) ,然后计算出该区块最适合由哪个链表分配,接着查看该链表是否有空余空间,如果有则直接分配,如果没有则找到与之相距最近的有空余空间的链表分配。
当分配时,发现当前组中,比最适合分配的那条链表和这条链表靠后的所有链表都没有空余空间,则开启下一个组进行分配。
释放内存时,将该内存挂在管理它大小的链表上。
合并空余内存,当释放内存时,先找到它的上cookie确定该块的大小,然后移动到与它相邻的下一个块的上cookie,然后查看其cookie中最后一位是否为0,如果是0则说明该块也是空闲块,则与其合并,然后找到合并后的空闲块的上cookie,向上找到与其相邻的上一个空间的下cookie,如果最后一位为0,则说明该区块也为空闲块,然后与该区块合并。最后根据该区块的大小放在管理其大小的链表上。
判断指针p属于哪个header中:header上有头尾指针,看p指针是否在该header的头尾指针之间。
判断指针p属于哪个group中:指针剪掉头然后除以32k,就知道是哪个group中。
分为32个group的原因:有利于回收空间,如果只有一个group,则这个group的空间会非常的大,等这个空间全部被释放需要等很久,但是如果建立多个group,每个group会变小,从而单个group的空间全部被释放的几率会变大。
如何判断全回收:每一个组的最上方有一个变量cntEntnes变量,只要malloc就++,free就–,所以如果该变量为0,则说明该group全部空间都被释放,所以该组此时可以回收。