STL-内存池

背景

1无论是C的malloc还是C++封装malloc的new,在向堆申请空间,都是没有规章的动态申请内存,即不关心已申请的内存块与自己的相对位置,这也就会导致内存的浪费,尤其是在一些频繁申请内存的地方,OS需要重新分配资源调度,运行程序运行效率
在这里插入图片描述

mallco和new缺点

  1. 空间申请与释放需要用户自己管理,容易造成内存泄漏
  2. 频繁向系统申请小块内存块,容易造成内存碎片
  3. 频繁向系统申请小块内存,影响程序运行效率
  4. 直接使用malloc与new进行申请,每块空间前有额外空间浪费
  5. 申请空间失败怎么应对
  6. 代码结构比较混乱,代码复用率不高
  7. 未考虑线程安全问题

SGI-STL空间配置器

基于malloc和new的不足,STL设计一块高效的内存管理机制专门 用于解决频繁申请小块内存导致的问题即空间配置器。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CZc0NXJZ-1666017408720)(./STL--%E5%86%85%E5%AD%98%E6%B1%A0.assets/image-20221017204852928-1666010934418-5.png)]

内存池

内存池就是:先申请一块比较大的内存块已做备用,当需要内存时,直接到内存池通过空间配置器进行管理,当池中空间不够时,再向内存中去取,当用户不用时,直接还回内存池即可。避免了频繁向系统申请小块内存所造成的效率低、内存碎片以及额外浪费的问题

alloc工作原理

首先alloc会向堆申请一大块内存(大小基于不同编译器)用于给容器分配资源。如果一次性申请的内存大于128字节,会采用一级空间配置器(其实就是mallco和new),如果一次申请内存小于128字节会采用二级空间配置器—这是内存池的精华

一级空间配置器

本质上就是malloc和new

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VwscAyF4-1666017408726)(./STL--%E5%86%85%E5%AD%98%E6%B1%A0.assets/image-20221017202939597-1666009781389-3.png)]

二级空间配置器

  1. 采用哈希桶的逻辑进行管理,但是不同的是:不同桶中的元素大小是不一样的。

  2. 向内存池申请小块内存块,通过链表的头插,头删高效的实现内存的申请与释放,

  3. 二级空间配置器采用8字节对齐。

     union obj {
            union obj * free_list_link;
            char client_data[1];    /* The client sees this.        */
      };
    
    1. 对于桶中的元素,二级空间配置器将前4字节(32位)/8字节(64位)的内存存放next指针,头删后next指针无效了,内存块会覆盖这个内存,实现对应字节内存块的申请,当释放内存块时,通过头插,前4/8字节继续存放next指针,这就实现了next空间的2用,且不会产生任何影响
    2. 这也就是为什么采用8字节对齐而不是4字节对齐,已64位下系统考虑
  4. 每个桶都有其对应的内存块大小,以8开始,以8为差到128的等比数列

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-89t7Xwrk-1666017408730)(./STL--%E5%86%85%E5%AD%98%E6%B1%A0.assets/image-20221017214627824-1666014388781-9.png)]

源码分析

请添加图片描述

空间配置器的再次封装

template<class T, class Alloc>
class simple_alloc {

public:
    static T *allocate(size_t n)
                { return 0 == n? 0 : (T*) Alloc::allocate(n * sizeof (T)); }
    static T *allocate(void)
                { return (T*) Alloc::allocate(sizeof (T)); }
    static void deallocate(T *p, size_t n)
                { if (0 != n) Alloc::deallocate(p, n * sizeof (T)); }
    static void deallocate(T *p)
                { Alloc::deallocate(p, sizeof (T)); }
};

对象的构造与释放

一切为了效率考虑,SGI-STL决定将空间申请释放和对象的构造析构两个过程分离开,因为有些对象的构造不需要调用析构函数,销毁时不需要调用析构函数,将该过程分离开可以提高程序的性能.

static void * allocate(size_t n)
  {
    obj * __VOLATILE * my_free_list;
    obj * __RESTRICT result;

    if (n > (size_t) __MAX_BYTES) {
        return(malloc_alloc::allocate(n));
    }
    my_free_list = free_list + FREELIST_INDEX(n);
    // Acquire the lock here with a constructor call.
    // This ensures that it is released in exit or during stack
    // unwinding.
#       ifndef _NOTHREADS
        /*REFERENCED*/
        lock lock_instance;
#       endif
    result = *my_free_list;
    if (result == 0) {
        void *r = refill(ROUND_UP(n));
        return r;
    }
    *my_free_list = result -> free_list_link;
    return (result);
 
  };

  /* p may not be 0 */
  static void deallocate(void *p, size_t n)
  {
    obj *q = (obj *)p;
    obj * __VOLATILE * my_free_list;

    if (n > (size_t) __MAX_BYTES) {
        malloc_alloc::deallocate(p, n);
        return;
    }
    my_free_list = free_list + FREELIST_INDEX(n);
    // acquire lock
#       ifndef _NOTHREADS
        /*REFERENCED*/
        lock lock_instance;
#       endif /* _NOTHREADS */
    q -> free_list_link = *my_free_list;
    *my_free_list = q;
    // lock is released here
  }

外碎片问题

malloc/new直接到堆频繁申请小块导致的空间浪费称为外碎片

内碎片问题

二级空间配置的桶元素大小是固定的,产生的空间浪费称为内碎片问题,在有规章的二级空间配置器控制下,这种浪费是可接受,无伤大雅的。

STL—list的空间配置器

一般通过结点 实现的容器都是通过二级空间配置器实现频繁的小块内存的控制。

请添加图片描述

STL—vector的空间配置器

  1. vetctor不同于list那种通过结点单独申请空间

  2. vector通过申请连续的空间,这意味当发生扩容时,会改变再哈希桶的位置。当扩容超出128字节时,会通过深拷贝有一级空间配置器控制,同时旧空间会回收到内存池。

  3. 源码这里就不介绍了和list相似

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值