一种特定环境使用的简易内存池

整个内存池代码就一百多行, 代码内容也非常简单, windows环境下, 消耗大约为malloc/free1/15左右(未做严谨测试, 数据仅供参考)

项目github地址

为什么需要使用内存池

  • 减少内存碎片
  • 提高申请和释放内存的效率

功能需求

  • 功能足够简单, 尽可能不使用第三方库
  • 每次申请的内存都是固定大小, 随用随删
  • 多线程环境下为每个线程绑定一个内存池, 避免使用锁

设计思路

  • 提前申请一大块内存, 划分为N份, bitset记录每一份内存有没有被使用
  • 移动offset检查可用的内存块, 循环重复这个过程

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
#include <vector>
using uint64 = unsigned long long;
#define bit_size 64
// 比使用std::bitset更快一点
class bit {
public:
bit(size_t len) {
this->_len = len;
this->_bitset.resize(len / bit_size, 0);
if (len % bit_size != 0) {
this->_bitset.push_back(0);
}
}
bit(const bit&) = delete;
bit& operator=(const bit&) = delete;
bool get(size_t offset) {
assert(offset < this->_len);
uint64 index = offset / bit_size;
uint64 mod = offset % bit_size;
uint64 mask = (uint64)1 << mod;
uint64 result = this->_bitset[index] & mask;
return result != 0;
}
void set(size_t offset, bool value) {
assert(offset < this->_len);
uint64 index = offset / bit_size;
uint64 mod = offset % bit_size;
if (!value) {
uint64 mask = ~(1 << mod);
this->_bitset[index] = this->_bitset[index] & mask;
}
else {
uint64 mask = (uint64)1 << mod;
this->_bitset[index] = this->_bitset[index] | mask;
}
}
private:
std::vector<uint64> _bitset;
size_t _len;
};
#define failed_skip 3
class memory_wheel {
public:
memory_wheel(size_t class_sizeof, size_t chunk_size) {
this->_chunk_size = chunk_size;
this->_class_sizeof = class_sizeof;
this->_offset = 0;
this->_failed = 0;
this->_using = new bit(chunk_size);
this->_buffer = malloc(chunk_size * class_sizeof);
this->_start = (uintptr_t)this->_buffer;
this->_over = this->_start + chunk_size * class_sizeof;
}

~memory_wheel() {
free(this->_buffer);
delete this->_using;
this->_offset = 0;
this->_buffer = nullptr;
this->_failed = 0;
}

memory_wheel(const memory_wheel&) = delete;
memory_wheel& operator=(const memory_wheel&) = delete;

void* palloc() {
// 一整块内存都被使用了, 回到起点
if (this->_offset >= this->_chunk_size) {
this->_offset = 0;
}
// 这一块内存是否被使用
if (!this->_using->get(this->_offset)) {
// 标记内存被占用
this->_using->set(this->_offset, 1);
// 分配一块_class_sizeof大小的内存
uintptr_t addr = (uintptr_t)this->_buffer;
void* p = (void*)(addr + this->_class_sizeof * this->_offset++);
memset(p, 0, this->_class_sizeof);
return p;
}
// _offset所在的这一块内存被占用了很久都没有释放
if (this->_failed++ >= failed_skip) {
// 跳过他
++this->_offset;
// 重置失败次数
this->_failed = 0;
}
void* p = malloc(this->_class_sizeof);
if (!p)
return nullptr;
memset(p, 0, this->_class_sizeof);
return p;
}

void recycle(void* p) {
if (!p)
return;
uintptr_t addr = (uintptr_t)p;
if (addr >= this->_start && addr <= (this->_over - this->_class_sizeof)) {
auto diff = addr - this->_start;
auto mod = diff % this->_class_sizeof;
assert(mod == 0);
size_t offset = diff / this->_class_sizeof;
this->_using->set(offset, 0);
}
else {
free(p);
}
}
private:
size_t _class_sizeof;
size_t _chunk_size;
size_t _offset;
void* _buffer;
int _failed;
uintptr_t _start;
uintptr_t _over;
bit* _using;
};

Usage

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class TestClass {
public:
TestClass(int v1, int v2) {
this->v1 = v1;
this->v2 = v2;
this->v3 = 0;
this->b = false;
}
TestClass(int v1, int v2, unsigned long long v3) {
this->v1 = v1;
this->v2 = v2;
this->v3 = v3;
this->b = false;
}
bool b;
int v1;
int v2;
unsigned long long v3;
};
#define PLACEMENT_NEW(ptr, _type, ...) new(ptr) _type { __VA_ARGS__ }
#define NEW(pool, _type, ...) PLACEMENT_NEW((pool)->palloc(), _type, __VA_ARGS__)
int main(int argc, char* argv[]) {
// 预分配512*sizeof(TestClass)大小的内存
memory_wheel wheel(sizeof(TestClass), 512);
// new TestClass
TestClass* obj1 = NEW(&wheel, TestClass, 1, 2);
// delete TestClass
wheel.recycle(obj1);
TestClass* obj2 = NEW(&wheel, TestClass, 50, 200, 999);
wheel.recycle(obj2);
}