- 简易版内存池实现
内存池类设计:
// 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);
}
};