在STL中考虑到小型区块所可能造成的内存碎片问题,SGI STL设计了双层级配置器,第一级配置器直接使用malloc()和free();第二级配置器则视情况采用不同的策略:当配置区块超过128bytes 时,则视之为足够大,便调用第一级配置器;当配置区块小于128bytes时,则视之为过小,为了降低额外负担,便采用复杂的内存池的方式来整理,而不再求助于第一级配置器。
每次配置器需要向系统要内存的时候,都不是按客户需求向系统申请的,而是一次性向系统要了比需求更多的内存,放在内存池里,有一个free_start和 free_end指示剩余的空间(也就是说内存池剩余的空间都是连续的,因此每次重新向system heap要空间的时候,都会把原先内存池里没用完的空间分配给合适的free list。)当free-list中没有可用区块了的时候,会首先从内存池里要内存,同样,也不是以按客户需求要多少块的,而是一次可能会要上20块,如 果内存池内空间允许的话,可能会得到20个特定大小的内存,如果内存池给不了那么多,那么就只好尽力给出;如果连一个都给不出,那么就要开始向系统即 system heap要空间了。换算的标准是bytes_to_get=2*total_bytes+ROUND_UP(heap_size>>4)。这个时候使用的是malloc,如果没成功,就尝试着从大块一点的freelist那里要一个来还给内存池,如果还是不行,那么会调用第一级空间配置器的 malloc::allocate,看看out-of-memory机制能做点什么。
假设我们向系统要x大小的内存,
(1)x大于128byte,用第一级配置器直接向系统malloc,至于不成功的处理,过程仿照new,通过set_new_handler来处理,直到成功返回相应大小的内存或者是抛出异常或者是干脆结束运行;
(2)x小于128byte,用第二级配置器向内存池相应的free_list要内存,如果该freelist上面没有空闲块了,
(2.1)从内存池里面要内存,缺省是获得20个节点,如果内存池中剩余的空间不能完全满足需求量,但足够供应一个(含)以上的区块,则应尽力满足需求。
(2.2) 如果一个都不能够满足的话,则从系统的heap里面要内存给到内存池,换算的标准是 bytes_to_get=2*total_bytes+ROUND_UP(heap_size>>4),申请的大小为需求量的两倍加上一个 附加值,如果内存池中还有剩余的内存,则将残余零头分配给适当的free list,这时使用的是系统的malloc,如果要不到:
(2.3)从比较大的freelist那里要内存到内存池,如果还是要不到:
(2.4) 从系统的heap里面要内存给到内存池,换算标准跟2.2一样,但是这时候使用的是第一级配置器的allocate,主要是看看能不能通过 out_of_memory机制得到一点内存。所以,freelist总是从内存池里要内存的,而内存池可能从freelist也可能从系统heap那里 要内存。
SGI STL的alloc的主要开销就在于管理这些小内存,管理这些小内存的主要开销就在于,每次freelist上的内存块用完了,需要重新要空间,然后建立 起这个list来。freelist上的内存,会一直保留着直到程序退出才还给系统。但这不会产生内存泄漏,一来是管理的都是小内存,二来是,占用的内存 只会是整个程序运行过程中小内存占用量最大的那一刻所占用的内存。