无锁内存池并发问题求助:双释放与重复分配异常
内存池问题定位与排查建议
一、析构函数Double Free问题排查
常见根因及验证方向
内存管理逻辑混淆
固定尺寸内存池的标准设计是预先批量申请内存页,内部维护分配/空闲状态,析构时统一释放所有预申请内存。如果你的deallocate函数直接调用free()/delete将内存归还系统,而析构函数又遍历所有内存页再次执行释放,必然触发double free。- 对照ASAN报告的两次free调用栈,确认第一次释放是否来自
deallocate、第二次来自析构函数。 - 检查
deallocate实现:是否仅将page标记为空闲并放回空闲链表,而非直接调用系统释放函数。
- 对照ASAN报告的两次free调用栈,确认第一次释放是否来自
管理结构异常
- 若维护了空闲链表,检查
deallocate是否存在重复将同一page加入链表的逻辑(比如未校验page当前状态就插入),导致析构时遍历链表多次释放同一地址。 - 检查内存页边界计算:是否存在地址重叠,导致释放时误操作已释放的相邻内存块。
- 若维护了空闲链表,检查
利用ASAN精准定位
提取ASAN报告中两次free的调用栈信息,对比触发路径,直接锁定代码逻辑错误。示例报告片段参考:==12345==ERROR: AddressSanitizer: double-free on address 0x602000000010 at pc 0x7f1234567890 #0 0x7f123456788f in free (/lib/x86_64-linux-gnu/libc.so.6+0x8988f) #1 0x55f123456789 in MemoryPool::~MemoryPool() memory_pool.cpp:42 ==12345==PREVIOUS FREE OF THIS ADDRESS: #0 0x7f123456788f in free (/lib/x86_64-linux-gnu/libc.so.6+0x8988f) #1 0x55f123456678 in MemoryPool::deallocate(Page*) memory_pool.cpp:28
二、并发控制失效(重复分配同一Page)问题排查
核心排查点
共享资源未加锁保护
空闲链表、内存页状态标记属于共享资源,必须在多线程访问时用互斥锁(如std::mutex)覆盖所有修改流程:- 检查
allocate函数中,从空闲链表取page、标记page为已用的操作是否全程处于锁保护范围内。正确逻辑示例:std::mutex mtx; std::list<Page*> free_list; Page* allocate() { std::lock_guard<std::mutex> lock(mtx); if (free_list.empty()) { auto new_pages = allocate_new_pages(); free_list.insert(free_list.end(), new_pages.begin(), new_pages.end()); } Page* page = free_list.front(); free_list.pop_front(); page->used = true; return page; } - 若仅在部分步骤加锁(如申请新page时未锁),会导致多线程同时修改空闲链表,出现重复分配。
- 检查
状态标记非原子操作
若page占用状态用普通bool变量标记,多线程下读写会存在数据竞争,导致线程读取到过期的"空闲"状态:- 将
bool used;替换为std::atomic<bool> used;,确保状态读写原子性。 - 即使使用原子变量,分配时仍需结合锁保护空闲链表修改,避免多个线程同时取到同一空闲page。
- 将
锁实现或粒度错误
- 检查是否使用了非线程安全的锁实现(如自定义自旋锁存在逻辑漏洞)。
- 若内存池拆分多个子池,确认每个子池的锁是否独立,避免锁竞争导致逻辑错误。
内容的提问来源于stack exchange,提问作者ONE NO
相关产品推荐
相关产品推荐

