文章目录
- 前言
- Code
- MemoryPool
- Test
- 简单讲解
- 应用方式
- 参数
- MemoryPool();
- ~MemoryPool();
- void* allocate();
- void deallocate(void* p);
- 测试效果
- END
前言
内存池_百度百科 (baidu.com)
(Memory Pool)是一种内存分配方式,又被称为固定大小区块规划(fixed-size-blocks allocation)。通常我们习惯直接使用new、malloc等API申请分配内存,这样做的缺点在于:由于所申请内存块的大小不定,当频繁使用时会造成大量的内存碎片并进而降低性能。
内存池的实现方式多种多样,而本文仅实现一个简单的内存池,主要运用到嵌入式指针。
嵌入式指针,指的在数据单元中,用一部分空间保存某一块空间的地址信息。实现方式多种多样。
本示例的代码量非常少,但能够满足最基本的内存池需要的操作。
Code
MemoryPool
#include <cassert>
#include <cstdint>namespace lotus {
template <typename Type, size_t Element_Count>
class MemoryPool {using UNIT_TYPE = uint8_t;static constexpr size_t BLOCK_SIZE = sizeof(Type);private:void* m_buffer = nullptr;void* m_freeHead = nullptr;public:MemoryPool() {static_assert(sizeof(Type) >= sizeof(void*), "size is to small");m_buffer = malloc(BLOCK_SIZE * Element_Count);m_freeHead = nullptr;for (size_t i = 0; i < Element_Count; i += 1) {void* index = (UNIT_TYPE*)m_buffer + (i * BLOCK_SIZE);*(void**)(index) = m_freeHead;m_freeHead = index;}}~MemoryPool() {if (m_buffer) {free(m_buffer);m_buffer = nullptr;}if (m_freeHead) {m_freeHead = nullptr;}}public:void* allocate() {assert(m_freeHead);void* res = m_freeHead;m_freeHead = (void*)(*(void**)m_freeHead);return res;}void deallocate(void* p) noexcept {*(void**)p = m_freeHead;m_freeHead = p;}
};
} // namespace lotus
Test
#include <iostream>namespace lotus {
template <typename Type, size_t Element_Count>
class MemoryPool {// ...
}
}int main() {using Tp = long long;lotus::MemoryPool<Tp, 100U> pool;{Tp* p1 = (Tp*)pool.allocate();*p1 = 123;printf("p = %p\t val = %lld\n", p1, *p1);Tp* p2 = (Tp*)pool.allocate();*p2 = 123456;printf("p = %p\t val = %lld\n", p2, *p2);pool.deallocate(p1);printf("after free p1\n");Tp* p3 = (Tp*)pool.allocate();printf("p = %p\t val = %lld\n", p3, *p3);}{printf(">> show near allocate()\n");for (int i = 0; i < 10; i += 1) {Tp* p = (Tp*)pool.allocate();printf("p = %p\t pval = %x\n", p, *p);}}
}
简单讲解
应用方式
将数据类型和类型单元数量作为模板参数确定
每次void* allocate()
获取一个单元空间。
每次void deallocate(void* p)
释放一个空间。请保证传入的是void* allocate()
中获取的。
简单解释一下本文使用嵌入式指针的方式:
// p是一个指针,信息是一个地址值
void* p;
// pp = (void**)(p) 将p的信息强转为一个二级指针
// *pp 解引用,表示pp这块地址`的内容`
*(void**)(p);
参数
template <typename Type, size_t Element_Count>
// 数据类型
Type
// 最大申请个数
Element_Count// 保证每个内存单元式8字节
using UNIT_TYPE = uint8_t;
// type的大小作为一个数据块
static constexpr size_t BLOCK_SIZE = sizeof(Type);// 总内存指针
void* m_buffer = nullptr;
// 头指针,时刻指向可分配出的内存地址
void* m_freeHead = nullptr;
MemoryPool();
MemoryPool() {// 确保目前元素大小比指针大小大static_assert(sizeof(Type) >= sizeof(void*), "size is to small");// 一口气申请大内存m_buffer = malloc(BLOCK_SIZE * Element_Count);m_freeHead = nullptr;for (size_t i = 0; i < Element_Count; i += 1) {// 将大内存分块void* index = (UNIT_TYPE*)m_buffer + (i * BLOCK_SIZE);// !核心!嵌入式指针// 总体表现为每后一个空间都保存前一个空间的地址信息*(void**)(index) = m_freeHead;m_freeHead = index;}
}
~MemoryPool();
~MemoryPool() {// 释放内存if (m_buffer) {free(m_buffer);m_buffer = nullptr;}// 仅起视图代理作用,不掌管内存if (m_freeHead) {m_freeHead = nullptr;}
}
void* allocate();
void* allocate() {assert(m_freeHead);// 分配出去的地址void* res = m_freeHead;// 借助嵌入式指针,将head指向下一个空间m_freeHead = (void*)(*(void**)m_freeHead);return res;
}
void deallocate(void* p);
void deallocate(void* p) noexcept {// 嵌入式指针赋值// p的空间的地址保存head的信息*(void**)p = m_freeHead;// head更新为pm_freeHead = p;
}
测试效果
g++ (x86_64-posix-seh-rev0, Built by MinGW-W64 project) 7.3.0
p = 0000000000751d98 val = 123
p = 0000000000751d90 val = 123456
after free p1
p = 0000000000751d98 val = 7675272
>> show near allocate()
p = 0000000000751d88 pval = 751d80
p = 0000000000751d80 pval = 751d78
p = 0000000000751d78 pval = 751d70
p = 0000000000751d70 pval = 751d68
p = 0000000000751d68 pval = 751d60
p = 0000000000751d60 pval = 751d58
p = 0000000000751d58 pval = 751d50
p = 0000000000751d50 pval = 751d48
p = 0000000000751d48 pval = 751d40
p = 0000000000751d40 pval = 751d38
第一部分可以看出,将p1归还后,再申请p3,获得的还是p1的地址。
第一部分可以看出,申请出空间的内容就是下一个空间的地址,如pval = 751d78的下一次申请为p = 0000000000751d78