C++内存池的实现

  • 简易版内存池实现

内存池类设计:

// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. See the AUTHORS file for names of contributors.
class Arena {

public:
  Arena();
  // 内存池禁止拷贝
  Arena(const Arena &) = delete;
  Arena &operator=(const Arena &) = delete;

  ~Arena();

  // 内存申请,返回指向所申请内存块的指针
  char *Allocate(size_t bytes);

  // 内存统计,记录该对象共申请了多少内存
  size_t MemoryUsage() const { return memory_usage_; }

private:
  char *AllocateNewBlock(size_t block_bytes);

  // 分配状态
  char *alloc_ptr_;
  size_t alloc_bytes_remaining_;

  //分配的内存块地址记录,便于释放内存
  std::vector<char *> blocks_;

  // 内存申请数量统计
  size_t memory_usage_;
};
char *Arena::Allocate(size_t bytes) {

  assert(bytes > 0);
  if (bytes <= alloc_bytes_remaining_) {
    // 内存分配第一步
    char *result = alloc_ptr_;
    alloc_ptr_ += bytes;
    alloc_bytes_remaining_ -= bytes;
    return result;
  }
  
  if (bytes > kBlockSize / 4) {
     // 内存分配第二步
    // 如果大于kBlockSize / 4,那么调用AllocateNewBlock分配, 也即找操作系统分配
    char *result = AllocateNewBlock(bytes);
    return result;
  }

  // 内存分配第三步
  alloc_ptr_ = AllocateNewBlock(kBlockSize);
  alloc_bytes_remaining_ = kBlockSize;

  char *result = alloc_ptr_;
  alloc_ptr_ += bytes;
  alloc_bytes_remaining_ -= bytes;
  return result;
}

// 直接向操作系统申请指定大小的内存
char *Arena::AllocateNewBlock(size_t block_bytes) {
  char *result = new char[block_bytes];
  blocks_.push_back(result);
  memory_usage_ += (block_bytes + sizeof(char *));
  return result;
}

Arena::~Arena() {
  for (size_t i = 0; i < blocks_.size(); i++) {
    delete[] blocks_[i];
  }
}

内存池测试:

void test_allocate_time() {
  int N = 100000;
  char *p;

  clock_t start_time_without_memory_pool;
  clock_t end_time_without_memory_pool;

  start_time_without_memory_pool = clock();
  for (int i = 1; i < N; ++i) {
    p = new char[i]; // 这里没有delete,会造成内存泄漏,不够严谨
  }
  end_time_without_memory_pool = clock();
  std::cout << "Allocate time without memorypool: "
            << (end_time_without_memory_pool - start_time_without_memory_pool)
            << std::endl;

  Arena arena;

  clock_t start_time_with_memory_pool;
  clock_t end_time_with_memory_pool;
  start_time_with_memory_pool = clock();
  for (int i = 1; i < N; ++i) {
    p = arena.Allocate(i);
  }
  end_time_with_memory_pool = clock();
  std::cout << "Allocate time with memorypool: "
            << (end_time_with_memory_pool - start_time_with_memory_pool)
            << std::endl;
}
  • 模版支持多策略内存池实现

1)不用内存池的动态内存使用

class A
 {
     private:
         int data; // 一些数据
     public:
         // 构造操作,初始化内部数据
         A(int data) : data(data) {}
 ​
         // 析构操作
         virtual ~A() {}
 };
int main(int, char **)
 {
     // 创建一个持有对象的指针数组
     A * a[64];
 ​
     // 申请内存,并创建A类型对象
     for (int i = 0; i < 8; ++i)
         a[i] = new A(i);
 ​
     // 删除这些对象,释放内存
     for (int i = 0; i < 8; ++i) {
         delete a[i];
         a[i] = NULL;
    }
 ​
     return 0;
 }

2)使用内存池的设计

class A
 {
     private:
         typedef memory_pool<A, mempool_malloc<A> > MemoryPool;
         static MemoryPool mem;
     private:
         int data; // 一些数据
     public:
         // 构造操作,初始化内部数据
         A(int data) : data(data) {}
 ​
         // 析构操作
         virtual ~A() {}
   
   // new和delete操作符重载 重定向到其他内存管理器,在本例中使用内存池进行内存管理
         void * operator new(unsigned int n)
        {
             return mem.alloc(n);
        }
 ​
         void operator delete(void * p)
        {
             mem.free(p);
        }
 };
 ​
 A::MemoryPool A::mem; // 类成员变量(静态变量)mem的定义

内存池的模版实现:

template <typename T, class Strategy> class memory_pool
 {
     private:
         Strategy s;
     public:
         memory_pool()
        {
             s.init();
        }
 ​
         void * alloc(unsigned int n)
        {
             if (sizeof(T) != n) throw std::bad_alloc();
             return s.allocate();
        }
 ​
         void free(void * p)
        {
             s.deallocate(p);
        }
 };

不同的分配策略:

1)常用的malloc/free

template <typename T> class mempool_malloc
 {
     public:
         void init() {}
 ​
         void * allocate()
        {
             return ::malloc(sizeof(T));
        }
 ​
         void deallocate(void * p)
        {
            ::free(p);
        }
 };

2) 简单数组分配策略

template <typename T, unsigned int N> class mempool_linear
 {
     private:
         typedef unsigned int size_type; // convinience
     private:
         uint8_t buf[sizeof(T)*N]; // 用于对象存储的内存空间
         bool state[N]; // true-当前内存块已经被占用 false-当前内存块未被占用
     public:
         void init()
        {
             // 初始化所有内存块为空闲状态(未占用状态)
             for (size_type i = 0; i < N; ++i) state[i] = false;
        }
 ​
         void * allocate()
        {
             // 线性遍历内存块状态数组 去查找一个未被占用的内存块
             // 将该内存块标记为占用状态,并且将该内存块首地址返回
             for (size_type i = 0; i < N; ++i)
                 if (!state[i]) {
                     state[i] = true;
                     return &buf[sizeof(T)*i];
                }
 ​
             // 未找到空闲的内存块 抛出异常
             throw std::bad_alloc();
        }
 ​
         void deallocate(void * p)
        {
             // 搜索比对所有内存块,找到待释放的内存块
             // 将该内存块标记为未占用状态
             // 请注意:这里的查找可以通过指针运算来提高搜索速度,此处是有意没有使用指针运算
             for (size_type i = 0; i < N; ++i)
                 if (&buf[sizeof(T)*i] == p) {
                     state[i] = false;
                     return;
                }
        }
 };

3)堆分配策略

template <typename T, unsigned int N> class mempool_heap
 {
     private:
         typedef unsigned int size_type; // convinience
     private:
         size_type available; // 可用内存块的数量
         T * a[N+1]; // 记录内存块使用情况 首节点未使用
         uint8_t buf[sizeof(T)*N]; // 内存池中内存存放位置 用来存储内存对象
     private:
         inline void swap(size_type i, size_type j)
        {
             T * t = a[i];
             a[i] = a[j];
             a[j] = t;
        }
 ​
         void up()
        {
             for (size_type n = available; n > 1; ) {
                 size_type i = n / 2;
                 if (!(a[i] && a[n]))
                     swap(i, n);
                 n = i;
            }
        }
 ​
         void down()
        {
             size_type i = 1;
             size_type c = 2 * i;
             while (c <= available) {
                 if (c+1 <= available)
                     if (!(a[c] && a[c+1]))
                         ++c;
                 if (!(a[i] && a[c]))
                     swap(i, c);
                 i = c;
                 c = 2 * i;
            }
        }
     public:
         void init()
        {
             // 可用内存数量初始化为N
             available = N;
 ​
             // 首节点未使用
             a[0] = NULL;
 ​
             // 其他内存块时空闲的,初始化对应的a数组元素值
             for (size_type i = 1; i <= N; ++i) {
                 a[i] = reinterpret_cast<T *>(&buf[sizeof(T)*(i-1)]);
            }
        }
 ​
         void * allocate()
        {
             // 如果内存池没有空闲空间 不能分配内存 抛出异常
             if (available <= 0) throw std::bad_alloc();
 ​
             // 第一个可用内存块永远位于索引为1的位置,这是由堆来保证的
             T * t = a[1];
 ​
             // 移动尾部元素到首部
             a[1] = a[available];
 ​
             // 标记该内存块为占用状态
             a[available] = NULL;
 ​
             // 可用内存数量递减
             --available;
 ​
             // 调整堆
             down();
 ​
             // 返回指向申请到的内存的指针
             return t;
        }
 ​
         void deallocate(void * p)
        {
             // 不可用入参 或者 内存池中所有内存块均处于空闲状态
             if (!p || available >= N) return;
           
             // 内存块释放,递增内存池中可用内存块数量
             ++available;
 ​
             // 将被释放的内存节点的地址重新存储到a数组中
             a[available] = reinterpret_cast<T *>(p);
 ​
             // 调整堆
             up();
        }
 };

4)STL版本堆分配策略:

template <typename T, unsigned int N> class mempool_std_heap
 {
     private:
         // 堆节点的状态
         enum State { FREE = 1, TAKEN = 0 };
 ​
         struct Entry {
             State state; // 内存块的状态
             T * p; // 指向内存块的指针
 ​
             // 比较操作符重载 堆调整时使用
             bool operator<(const Entry & other) const
            { return state < other.state; }
        };
 ​
         typedef unsigned int size_type; // convenience
     private:
         size_type available; // 可用内存块的数量
         Entry a[N]; // 存储内存块状态
         uint8_t buf[sizeof(T)*N]; // 内存池中的内存 用于存储对象
     public:
         void init()
        {
             // 将内存池可用内存块的数量初始化为N
             available = N;
 ​
             // 所有内存块初始化为空闲状态,将a的所有节点置为空闲状态并且将其内部指针指向对应内存块
             for (size_type i = 0; i < N; ++i) {
                 a[i].state = FREE;
                 a[i].p = reinterpret_cast<T *>(&buf[sizeof(T)*i]);
            }
 ​
             // 建堆
             std::make_heap(a, a+N);
        }
 ​
         void * allocate()
        {
             // 如果内存中无可用的内存块 抛出异常
             if (available <= 0 || available > N) throw std::bad_alloc();
 ​
             // 第一个可用永远对应a中索引为0的节点
             Entry e = a[0];
 ​
             // 移除堆的第一个节点
             std::pop_heap(a, a+N);
 ​
             // 递减内存中可用内存块的数量
             --available;
 ​
             // 标记这个被申请的内存块 处理该对应的a中的节点
             a[available].state = TAKEN;
             a[available].p = NULL;
 ​
             // 返回指向申请到的内存块的地址
             return e.p;
        }
 ​
         void deallocate(void * ptr)
        {
             // 指针不可用或者内存池中所有内存都是空闲状态
             if (!ptr || available >= N) return;
 ​
             // 释放内存块 处理相关标记
             a[available].state = FREE;
             a[available].p = reinterpret_cast<T *>(ptr);
 ​
             // 递增可用内存块数量
             ++available;
 ​
             // 调整堆
             std::push_heap(a, a+N);
        }
 };

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值