前言
本篇文章仅学习用,并无实际应用场景。
内存管理是为了将有限的空闲有效地利用起来,虽然标题说的是内存分配,实际上也包含回收和内存碎片整理的功能,当然也只是简单模拟一下。
原理
1. 内存块
类型不同,那占用空间就可能不同。为了解决不同占用空间的对象之间不会存在冲突的内存地址,为每个对象的内存都划分为一个内存块。一段连续地址的空闲空间也划分为一个内存块,这样占用空间和空闲空间都统一以内存块来管理。
typedef char* address_type;
typedef char** address_ptr;
struct MemoryBlock
{
address_ptr addr;
unsigned long len;
};
2. 内存池
内存池的空间是有限的,且不能增加不能减少;内存池不是只分配一种类型的对象,而是可以分配多种类型的对象。为了方便,将空闲的内存块和占用的内存块分别放到两个链表来管理。
链表的结点结构体定义:
struct MemoryBlockNode
{
MemoryBlock value;
MemoryBlockNode *next;
MemoryBlockNode *prev;
};
3. 分配
在申请某种类型的内存分配时,假设该类型的内存长度为X,先判断总空闲空间是否足够,如果足够则减少总空闲空间,然后遍历空闲块链表,找到第一个足够空间的内存块,如果这个内存块的长度刚好是这个类型的占用长度,则直接把这个内存块从空闲链表中移除,添加到占用块链表中;否则创建一个标志其所占用内存长度和起始地址的内存块,并添加到占用块链表,而这个空闲的内存块的起始地址往后移动X个字节,其长度也相应减小X。这里有一个问题:总空闲空间足够,但没有一个单独的内存块长度达到X的。这时就需要碎片整理,挤出一大块空间,然后再进行分配。
为了防止分配出去的地址失效,分配空间时将内存块的结点信息封装到另一个类MemoryItemPtr中,这样分配空间返回时就返回这个类的对象,方便碎片整理时对已分配的空间进行重定位。
4. 回收
回收空间时,所传入的参数就是分配时返回的变量。从参数中获得内存块的结点,然后直接从占用块链表中移除该结点,并添加到空闲块链表中。最后相应增加总空闲空间。
5. 碎片整理
碎片整理就是把所有占用的空间和所有空闲的空间都各自紧凑地排在一起,使得空闲的空间全部排到总内存空间的末尾,然后再把所有空闲的空间整合成一个内存块。
源码:
编译环境:C++03
#ifndef MEMORY_POOL_H
#define MEMORY_POOL_H
#ifdef _DEBUG
#include <iostream>
#endif
#if __cplusplus >= 201103L
#include <type_traits>
#endif
#if __cplusplus >= 201103L
#define null nullptr
#else
#ifndef NULL
#define NULL 0
#endif
#define null NULL
#endif
#if __cplusplus >= 201402L
#define CXX14_CONSTEXPR constexpr
#else
#define CXX14_CONSTEXPR
#endif
typedef char* address_type;
typedef char** address_ptr;
struct MemoryBlock
{
address_ptr addr;
unsigned long len;
};
struct MemoryBlockNode
{
MemoryBlock value;
MemoryBlockNode *next;
MemoryBlockNode *prev;
};
template<typename _Tp>
struct MemoryItemPtr
{
typedef _Tp value_type;
typedef _Tp* pointer;
typedef _Tp& refrence;
typedef const _Tp& const_refrence;
typedef const _Tp* const_pointer;
typedef MemoryBlockNode* node_ptr;
MemoryItemPtr(node_ptr __node = null)
: _M_node(__node)
{
if(valid())
{ ::new(native()) value_type(); }
}
#if __cplusplus >= 201103L
template<typename ... _Args>
MemoryItemPtr(node_ptr __node, _Args && ... __args)
: _M_node(__node)
{
if(valid())
{ ::new(native()) value_type(std::forward<_Args>(__args)...); }
}
#else
MemoryItemPtr(node_ptr __node, const_refrence __copy)
: _M_node(__node)
{
if(valid())
{ ::new(native()) value_type(__copy); }
}
#endif
refrence operator*()
{ return *native(); }
const_refrence operator*() const
{ return *native(); }
pointer operator->()
{ return native(); }
const_pointer operator->() const
{ return native(); }
bool valid() const
{ return null != _M_node && null != _M_node->value.addr && null != *_M_node->value.addr; }
pointer native()
{ return reinterpret_cast<value_type*>(*_M_node->value.addr); }
const_pointer native() const
{ return reinterpret_cast<value_type*>(*_M_node->value.addr); }
operator bool() const
{ return valid(); }
unsigned long size() const
{ return sizeof(value_type); }
node_ptr _M_node;
};
template<unsigned long _Size>
class MemoryPool
{
public:
typedef MemoryBlockNode node_type;
typedef MemoryBlockNode* node_ptr;
typedef MemoryBlock block_type;
typedef MemoryBlock* block_ptr;
public:
MemoryPool()
: _M_free_head(new node_type), _M_alloc_head(null), _M_free_size(_Size)
{
_M_free_head->next = null;
_M_free_head->prev = null;
_M_free_head->value.addr = new address_type;
*_M_free_head->value.addr = &_M_mem[0];
_M_free_head->value.len = _Size;
}
~MemoryPool()
{
_M_clear(_M_free_head);
_M_clear(_M_alloc_head);
}
unsigned long free_size() const
{ return _M_free_size; }
unsigned long CXX14_CONSTEXPR total_size() const
{ return _Size; }
// 分配内存
template<typename _Tp>
MemoryItemPtr<_Tp> alloc()
{ return MemoryItemPtr<_Tp>(_M_alloc(_M_size<_Tp>())); }
#if __cplusplus >= 201103L
template<typename _Tp, typename ... _Args>
MemoryItemPtr<_Tp> alloc(_Args && ... __args)
{ return MemoryItemPtr<_Tp>(_M_alloc(_M_size<_Tp>()), std::forward<_Args>(__args)...); }
#else
template<typename _Tp>
MemoryItemPtr<_Tp> alloc(const _Tp &__copy)
{ return MemoryItemPtr<_Tp>(_M_alloc(_M_size<_Tp>()), __copy); }
#endif
// 回收内存
template<typename _Tp>
void free(MemoryItemPtr<_Tp> __ptr)
{
if(!__ptr)
{
return;
}
node_ptr __cur = __ptr._M_node;
node_ptr __next = __cur->next;
node_ptr __prev = __cur->prev;
__ptr->~_Tp();
// 先从占用列表中删除
if(__cur == _M_alloc_head)
{
_M_alloc_head = __next;
if(null != _M_alloc_head)
{
_M_alloc_head->prev = null;
}
}
else
{
if(null != __prev)
{
__prev->next = __next;
}
if(null != __next)
{
__next->prev = __prev;
}
}
_M_free_size += _M_size<_Tp>();
if(null != _M_free_head)
{
if(*_M_free_head->value.addr + _M_free_head->value.len == *__cur->value.addr)
{
_M_free_head->value.len += __cur->value.len;
delete __cur->value.addr;
delete __cur;
}
else if(*__cur->value.addr + __cur->value.len == *_M_free_head->value.addr)
{
_M_free_head->value.len += __cur->value.len;
*_M_free_head->value.addr = *__cur->value.addr;
delete __cur->value.addr;
delete __cur;
}
else
{
__cur->next = _M_free_head;
_M_free_head->prev = __cur;
__cur->prev = null;
_M_free_head = __cur;
}
}
else
{
_M_free_head = __cur;
_M_free_head->next = null;
_M_free_head->prev = null;
}
}
// 清理内存碎片
void clean_up()
{
_M_sort(_M_free_head);
_M_sort(_M_alloc_head);
// 整合连续的空闲区域块
node_ptr __tmp = _M_free_head;
while(null != __tmp)
{
node_ptr __next = __tmp->next;
if(null == __next)
{
break;
}
if(*__next->value.addr == *__tmp->value.addr + __tmp->value.len)
{
__tmp->value.len += __next->value.len;
delete __next->value.addr;
__tmp->next = __next->next;
if(null != __next->next)
{
__next->next->prev = __tmp;
}
delete __next;
}
else
{
__tmp = __tmp->next;
}
}
// 将占用的空间往前挪
node_ptr __free_node = _M_free_head, __next;
node_ptr __alloc_node = _M_alloc_head;
while(null != __free_node && null != __alloc_node)
{
if(*__free_node->value.addr > *__alloc_node->value.addr)
{
__alloc_node = __alloc_node->next;
continue;
}
// 空闲块往后挪
_M_memcpy(*__free_node->value.addr, *__alloc_node->value.addr, __alloc_node->value.len);
*__alloc_node->value.addr = *__free_node->value.addr;
*__free_node->value.addr += __alloc_node->value.len;
// 当前空闲块和下一个空闲块连续的话就整合一起
__next = __free_node->next;
if(null != __next && *__free_node->value.addr + __free_node->value.len == *__next->value.addr)
{
__free_node->value.len += __next->value.len;
__free_node->next = __next->next;
if(null != __next->next)
{
__next->next->prev = __free_node;
}
delete __next->value.addr;
delete __next;
}
__alloc_node = __alloc_node->next;
}
_M_clear(_M_free_head->next);
_M_free_head->next = null;
_M_free_head->prev = null;
_M_free_head->value.len = free_size();
}
// 清空所有内存占用(不调用析构函数)
void clear()
{
_M_clear(_M_alloc_head);
_M_clear(_M_free_head->next);
_M_alloc_head = null;
*_M_free_head->value.addr = &_M_mem[0];
_M_free_head->value.len = _Size;
_M_free_head->next = null;
_M_free_head->prev = null;
_M_free_size = _Size;
}
#ifdef _DEBUG
void print_free_block_list() const
{
std::cout << "free block list: " << std::endl;
_M_print_block_list(_M_free_head);
}
void print_alloc_block_list() const
{
std::cout << "alloc block list: " << std::endl;
_M_print_block_list(_M_alloc_head);
}
#endif
private:
#ifdef _DEBUG
void _M_print_block_list(node_ptr __head) const
{
node_ptr __node = __head;
while(null != __node)
{
std::cout << " " << reinterpret_cast<void *>(*__node->value.addr) << " ~ " << reinterpret_cast<void *>((*__node->value.addr) + __node->value.len - 1) << std::endl;
__node = __node->next;
}
}
#endif
template<typename _Tp>
unsigned long CXX14_CONSTEXPR _M_size() const
{ return static_cast<unsigned long>(sizeof(_Tp)); }
node_ptr _M_alloc(unsigned long __size)
{
// 判断空间是否足够
if(free_size() < __size)
{
return null;
}
node_ptr __tmp = _M_alloc_node(__size);
// 如果没有一块足够大的内存块
if(null == __tmp)
{
clean_up(); // 整理内存碎片
if(null == (__tmp = _M_alloc_node(__size)))
{
return null;
}
}
if(null == _M_alloc_head)
{
_M_alloc_head = __tmp;
}
else
{
_M_alloc_head->prev = __tmp;
__tmp->next = _M_alloc_head;
__tmp->prev = null;
_M_alloc_head = __tmp;
}
_M_free_size -= __size;
return __tmp;
}
node_ptr _M_alloc_node(unsigned long __size)
{
node_ptr __tmp = _M_free_head;
while(null != __tmp)
{
if(__tmp->value.len == __size)
{
if(null == __tmp->prev)
{
_M_free_head = __tmp->next;
}
break;
}
if(__tmp->value.len > __size)
{
node_ptr __prev = __tmp->prev;
node_ptr __node = new node_type;
__node->value.addr = new address_type;
*__node->value.addr = *__tmp->value.addr + __size;
__node->value.len = __tmp->value.len - __size;
__node->next = __tmp->next;
__node->prev = __prev;
if(null == __prev)
{
_M_free_head = __node;
}
else
{
__prev->next = __node;
}
__tmp->value.len = __size;
break;
}
}
return __tmp;
}
void _M_memcpy(char *__dst, const char *__src, unsigned long __len) const
{
while(__len-- > 0)
{ *__dst++ = *__src++; }
}
void _M_sort(node_ptr __head) const
{
if(null == __head)
{
return;
}
node_ptr __cur = __head;
while(null != __cur)
{
node_ptr __min_node = __cur, __tmp = __cur->next;
while(null != __tmp)
{
if(*__tmp->value.addr < *__min_node->value.addr)
{
__min_node = __tmp;
}
__tmp = __tmp->next;
}
if(__cur != __min_node)
{
block_type __s = __cur->value;
__cur->value = __min_node->value;
__min_node->value = __s;
}
__cur = __cur->next;
}
}
void _M_clear(node_ptr __head) const
{
node_ptr __node = __head;
while(null != __node)
{
node_ptr __d = __node;
__node = __node->next;
delete __d->value.addr;
delete __d;
}
}
private:
node_ptr _M_free_head; // 空闲的内存块
node_ptr _M_alloc_head; // 已占用的内存块
unsigned long _M_free_size; // 剩余的空间大小
char _M_mem[_Size]; // 总内存空间
};
#endif // MEMORY_POOL_H
测试代码:
#include <iostream>
#include <map>
#include "MemoryPool.h"
static MemoryPool<64> memory_pool;
typedef std::map<int, int> test_map_type;
struct TS
{
double value;
TS()
{
std::cout << "TS::TS()" << std::endl;
}
~TS()
{
std::cout << "TS::~TS()" << std::endl;
}
};
static void print_block_list(int index)
{
#ifdef _DEBUG
std::cout << std::endl;
std::cout << "*************** mem block list " << index << " ***********************" << std::endl;
memory_pool.print_alloc_block_list();
memory_pool.print_free_block_list();
std::cout << "*************** mem block list " << index << " ***********************" << std::endl;
std::cout << std::endl;
#endif
}
static void func()
{
print_block_list(0);
MemoryItemPtr<int> a = memory_pool.alloc<int>(10);
std::cout << "0000 free size: " << memory_pool.free_size() << std::endl;
std::cout << "a address: " << a.native() << std::endl;
std::cout << "a value: " << *a << std::endl;
MemoryItemPtr<test_map_type> b;
b = memory_pool.alloc<test_map_type>();
if(b)
{
std::cout << "success to alloc memory" << std::endl;
std::cout << "b address: " << b.native() << std::endl;
}
std::cout << "11111 free size: " << memory_pool.free_size() << std::endl;
MemoryItemPtr<test_map_type> bb = memory_pool.alloc<test_map_type>();
if(!bb)
{
std::cout << "memory is not enough! failed to alloc" << std::endl;
}
MemoryItemPtr<TS> c = memory_pool.alloc<TS>();
c->value = 100;
std::cout << "c value: " << c->value << std::endl;
std::cout << "c address: " << c.native() << std::endl;
std::cout << "------ free size: " << memory_pool.free_size() << std::endl;
print_block_list(1);
memory_pool.free(b);
std::cout << "22222 free size: " << memory_pool.free_size() << std::endl;
print_block_list(2);
MemoryItemPtr<TS> d = memory_pool.alloc<TS>();
std::cout << "3333 free size: " << memory_pool.free_size() << std::endl;
std::cout << "d address: " << d.native() << std::endl;
print_block_list(3);
memory_pool.clean_up();
std::cout << "c address after clean_up: " << c.native() << std::endl;
std::cout << "d address after clean_up: " << d.native() << std::endl;
d->value = 2000;
std::cout << "d value: " << d->value << std::endl;
print_block_list(4);
memory_pool.free<TS>(c);
std::cout << "44444 free size: " << memory_pool.free_size() << std::endl;
print_block_list(5);
}
int main()
{
func();
std::cout << ">>>>>> final free size: " << memory_pool.free_size() << std::endl;
memory_pool.clear();
std::cout << "free size after clear: " << memory_pool.free_size() << std::endl;
system("pause");
return 0;
}
测试输出结果: