dl
dl_memory_pool.h
浏览该文件的文档.
1
15#pragma once
16
17#include <list>
18#include <cassert>
19#include <functional>
20#include <cstdlib>
21
22#include "io/dl_log.h"
23#include "math/dl_math.h"
24
25#ifdef NDEBUG
26constexpr bool OUTPUT_DEBUG = false;
27constexpr bool OUTPUT_ADD = false;
28constexpr bool OUTPUT_MOVE = false;
29constexpr bool OUTPUT_STACK = false;
30#else
31constexpr bool OUTPUT_DEBUG = true;
32constexpr bool OUTPUT_ADD = true;
33constexpr bool OUTPUT_MOVE = false;
34constexpr bool OUTPUT_STACK = false;
35#endif
36
37namespace dl
38{
39
41{
42 uint32_t _count;
43};
44
45
47template<typename T>
48class GC
49{
50 template<typename TT>
51 friend class PoolList;
52public:
53 GC() :_p(nullptr), _ref(nullptr) {}
54 GC(const GC<T>& b) : _p(b._p), _ref(b._ref) { if (_ref)++_ref->_count; }
55 operator bool() const
56 {
57 return _p != nullptr;
58 }
59
61 {
62 return _p;
63 }
64
66 {
67 return *_p;
68 }
69
71 {
72 //如果相同,则什么都不做
73 if (_p == b._p)
74 {
75 return *this;
76 }
77
78 //不同,且有原对象,减少1次对原对象的引用
79 if (_p)
80 {
81 assert(_ref && _ref->_count > 0);
82 --_ref->_count;
83 _check_dtor();
84 }
85
86 //复制,再增加b的一次引用(b可能为空)
87 _p = b._p;
88 _ref = b._ref;
89 if (_ref)
90 ++_ref->_count;
91
92 return *this;
93 }
94
96 void CancelRef()
97 {
98 if (_p)
99 {
100 assert(_ref && _ref->_count > 0);
101 --_ref->_count;
102 _check_dtor();
103 }
104 }
105
106 size_t GetRefCount()
107 {
108 if (_p)
109 {
110 assert(_ref && _ref->_count > 0);
111 return _ref->_count;
112 }
113 return 0;
114 }
115
118 {
119 return _p;
120 }
121 const T* GetPointer() const
122 {
123 return _p;
124 }
125
127 {
128 // 如果退出时崩溃到这里,说明有GC<T>后于PoolList析构,需要使其退出主函数之前析构
129 // 简单说就是 不要使用gc全局变量,因为可能晚于GC系统析构
130
131 // Text包含Sprite,如果Sprite先全局析构,再全局析构Text也会出问题
132 // 此问题已解决,先使用一下gcSprite,PoolList<Sprite>就会先于PoolList<Text>析构
133 if (_p)
134 {
135 assert(_ref && _ref->_count > 0);
136 --_ref->_count;
137 _check_dtor();
138 }
139 }
140
141private:
142 GcRefInfo* _ref;
143 T* _p;
144
145 void _check_dtor()
146 {
147 if (_ref->_count == 0)
148 {//显式析构
149 _p->~T();
150 _p = nullptr;
151 _ref = nullptr;
152 }
153 }
154};
155
156template<typename T>
157class PoolList
158{
159 friend class MemoryPool;
160public:
162 void SetCallbackDelete(std::function<void()> func)
163 {
164 _cbDelete = func;
165 }
166 constexpr unsigned GetNodeSize()
167 {
168 return sizeof(T) + sizeof(GcRefInfo);
169 }
170private:
171 PoolList() {}
172 // 取一个位置
173 template<typename... Args>
174 GC<T> GetGC(Args&& ...args)
175 {
176 void* p = nullptr;
177 // 如果没有就新建内存
178 if (_listFree.empty())
179 _add_free();
180
181 //
182 if (_listFree.empty())
183 {
184 log("PoolArray::GetGC: 失败!");
185 return GC<T>();
186 }
187
188 p = _listFree.back();
189 _listFree.pop_back();
190 _listUsed.push_back(p);
191
192 GC<T> ret;
193 ret._p = new (p) T(args...);
194 ret._ref = new ((unsigned char*)p + sizeof(T)) GcRefInfo;
195 ret._ref->_count = 1;
196
197 return ret;
198 }
199
200
201 ~PoolList()
202 {
203 // 不能使用全局的GC变量
204
205 // Used列表的内存强行回收
206 for (auto& iter : _listUsed)
207 free(iter);// _aligned_free(iter);
208
209 for (auto& iter : _listFree)
210 free(iter);// _aligned_free(iter);
211
212 if (OUTPUT_DEBUG)
213 {
214 log_easy(fmt::format("释放 {}({}byte)内存,(已使用{}+空闲{})个\n",
215 typeid(T).name(), GetNodeSize(), _listUsed.size(), _listFree.size()));
216 }
217
218 if(_cbDelete)
219 _cbDelete();
220 }
221private:
222 // 可使用的节点内存
223 std::vector<void*> _listFree;
224 std::list<void*> _listUsed;
225 // 回调
226 std::function<void()> _cbDelete;
227
228 // 批量new
229 void _add_free()
230 {
231 //先检查used转移到free
232 size_t count = 0;
233 if (_listUsed.size() > 10000)//100m (sizeof(GcRefInfo) + sizeof(T)) > 1024 * 1024 * 100
234 {
235 for (auto iter = _listUsed.begin(); iter != _listUsed.end();)
236 {
237 void* p = (unsigned char*)*iter + sizeof(T);
238 if (((GcRefInfo*)p)->_count == 0)
239 {
240 _listFree.push_back(*iter);
241 _listUsed.erase(iter++);
242 ++count;
243 }
244 else
245 ++iter;
246 }
248 {
249 LogStackTrack l{ OUTPUT_STACK };
250 log_test("转移 {}({}byte)内存,({})个",
251 typeid(T).name(), GetNodeSize(), count);
252 }
253 }
254
255
256 //转移过少
257 if (count <= _listUsed.size() / 2)
258 {
259 //
260 size_t num = _listUsed.size() + 10;
261 for (size_t i = 0; i != num; ++i)
262 {
263 unsigned MEM_SIZE = GetNodeSize();// Math::CeilToPow2(GetNodeSize());
264 void* p = malloc(MEM_SIZE);// _aligned_malloc(MEM_SIZE, 16);
265
266 if (p == nullptr)
267 {
268 log("malloc失败!");
269 return;
270 }
271 _listFree.push_back(p);
272 }
274 {
275 LogStackTrack l{ OUTPUT_STACK };
276 log_test("分配 {}({}byte)内存,({})个",
277 typeid(T).name(), GetNodeSize(), _listFree.size());
278 }
279 }
280
281 }
282};
283
284
285
287class MemoryPool
288{
289public:
294 template<typename T, typename... Args>
295 static GC<T> GetGC(Args&& ...args)
296 {
297 return GetPoolList<T>().GetGC(args...);
298 }
299
300 template<typename T>
302 {
303 static PoolList<T> pool_list;
304 return pool_list;
305 }
306private:
307 MemoryPool() = delete;
308};
309
315template<typename T, typename... Args>
316auto get_gc(Args&& ...args)
317{
318 return MemoryPool::GetGC<T>(args...);
319}
320
321
322}
引用计数对象,通过get_gc函数创建
T * GetPointer()
直接获取指针,注意生命周期
const T * GetPointer() const
size_t GetRefCount()
GC< T > & operator=(const GC< T > &b)
GC(const GC< T > &b)
void CancelRef()
取消引用对象
static GC< T > GetGC(Args &&...args)
定义一个GC变量(引用计数内存池)
static PoolList< T > & GetPoolList()
内存池数据
constexpr unsigned GetNodeSize()
friend class MemoryPool
void SetCallbackDelete(std::function< void()> func)
设置删除回调
日志系统
#define log_test(...)
数学相关的通用算法
constexpr bool OUTPUT_ADD
constexpr bool OUTPUT_STACK
constexpr bool OUTPUT_MOVE
constexpr bool OUTPUT_DEBUG
auto get_gc(Args &&...args)
定义一个GC变量(引用计数内存池)
void log_easy(std::string_view str)
输出日志
void log(std::string_view str, DL_SOURCE_LOCATION)
以当前设置输出