0

0

c++如何实现堆内存池管理_c++ 块预分配与自定义free逻辑【详解】

冰火之心

冰火之心

发布时间:2025-12-26 09:55:02

|

196人浏览过

|

来源于php中文网

原创

new/delete在高频小对象场景变慢,因频繁系统调用、堆管理器锁竞争与内存碎片;内存池通过预分配大块内存+原子空闲链表实现无锁高效分配。

c++如何实现堆内存池管理_c++ 块预分配与自定义free逻辑【详解】

为什么 new / delete 在高频小对象场景下会变慢

频繁调用 newdelete 本质是向操作系统申请/释放页内存(mmap/brk),再经由 libc 的堆管理器(如 ptmalloc)切分、合并、加锁。小对象(比如几十字节的节点)反复分配时,会产生大量元数据开销、锁竞争和内存碎片。实测中,一个每秒百万次的 new Node 可能比池化慢 3–10 倍,且 GC 式压力会让 malloc 内部链表遍历变长。

堆内存池的核心思路是:一次性向系统申请一大块内存(如 64KB),自己维护空闲块链表,alloc 直接取头节点,free 仅把指针插回链表——全程无系统调用、无锁(单线程)或轻量 CAS(多线程)。

如何手写一个线程安全的固定大小块内存池

以 32 字节对象为例,不依赖模板、不封装类,聚焦核心逻辑。关键点在于:块对齐、头部元信息、原子空闲链表操作。

  • malloc 一次申请足够多的连续内存(如 size_t pool_size = 64 * 1024),用 aligned_alloc(alignof(std::max_align_t), pool_size) 确保地址对齐
  • 每个块头部存一个 char* 指针(8 字节),指向下一个空闲块;实际可用内存从该指针后偏移开始(即 block + sizeof(char*)
  • 初始化时,把整块内存切成等长块,串成单向链表:next_ptr = (char**)block; *next_ptr = next_block;
  • 分配时用 std::atomic_load 读取链表头,std::atomic_compare_exchange_weak 原子摘下;释放时同样原子插入头部
// 简化版核心分配逻辑(无错误检查)
static std::atomic free_list{nullptr};

void init_pool() { char pool = static_cast>(aligned_alloc(alignof(std::max_align_t), 65536)); const size_t block_size = 32; char p = pool; for (size_t i = 0; i < 65536 / block_size - 1; ++i) { char next = reinterpret_cast>(p); next = p + block_size; p += block_size; } char last = reinterpret_cast>(p); *last = nullptr; free_list.store(pool, std::memory_order_relaxed); }

void pool_alloc() { char head = free_list.load(std::memory_order_acquire); char next; while (head && !free_list.compare_exchange_weak(head, next = (char**)head, std::memory_order_acq_rel, std::memory_order_acquire)) {} return head; }

void pool_free(void ptr) { if (!ptr) return; char next_ptr = reinterpret_cast>(ptr); char old_head = free_list.load(std::memory_order_acquire); do { next_ptr = old_head; } while (!free_list.compare_exchange_weak(old_head, static_cast>(ptr), std::memory_order_acq_rel, std::memory_order_acquire)); }

如何支持多种对象大小并避免跨块访问

单一大小池只适用于特定场景(如链表节点、事件结构体)。若需多尺寸,不能简单复用同一块内存——否则 free 无法知道该按哪种尺寸回收,且易导致越界写入头部指针。

立即学习C++免费学习笔记(深入)”;

摄图AI
摄图AI

摄图网旗下AI视觉创作平台

下载

常见做法是分桶(bucket):按 2 的幂次划分尺寸档位(如 16B、32B、64B、128B…),每个档位维护独立的 free_list 和预分配池。分配时向上取整到最近档位,free 必须传入原始分配尺寸(或由池记录),否则无法定位所属桶。

  • constexpr 计算档位索引:int bucket = std::bit_width(size_t(size)) - 4(假设最小 16B)
  • 每个桶的池可延迟创建:首次请求某尺寸时才 malloc 一块,避免冷启动浪费
  • 注意:若对象含虚函数或需要构造/析构,pool_alloc 返回的内存未调用构造函数,必须显式 new (ptr) T{...};同理 pool_free 前要手动调用 obj.~T()

自定义 free 逻辑时最容易忽略的三个细节

很多人以为“重载 operator delete 就完事”,但实际落地时这几个点常导致崩溃或泄漏:

  • operator delete 接收的是 void*,但你无法从中还原对象类型或尺寸——除非在分配时额外存储元数据(如前缀加 4 字节 size 字段),否则 free 无法知道该归还给哪个桶
  • 全局重载 operator new/operator delete 会影响所有代码,包括 STL 容器内部(std::vector 的扩容)、第三方库;更安全的做法是仅对特定类重载成员版本:class Node { void* operator new(size_t); void operator delete(void*) noexcept; };
  • 多线程下,如果多个线程同时 free 同一池,而你的链表插入没用原子操作或互斥锁,会破坏 next 指针,造成后续 alloc 返回非法地址——这种 bug 往往偶发且难以复现

真正稳定的池管理,不是“替换 new/delete”,而是明确控制生命周期:对象在哪创建、谁负责销毁、是否允许跨线程传递。一旦引入自定义 free,就必须同步约束使用边界,比如禁止 std::shared_ptr 默认删除器接管池内对象。

相关文章

c++速学教程(入门到精通)
c++速学教程(入门到精通)

c++怎么学习?c++怎么入门?c++在哪学?c++怎么学才快?不用担心,这里为大家提供了c++速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
golang结构体相关大全
golang结构体相关大全

本专题整合了golang结构体相关大全,想了解更多内容,请阅读专题下面的文章。

193

2025.06.09

golang结构体方法
golang结构体方法

本专题整合了golang结构体相关内容,请阅读专题下面的文章了解更多。

184

2025.07.04

string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

311

2023.08.02

int占多少字节
int占多少字节

int占4个字节,意味着一个int变量可以存储范围在-2,147,483,648到2,147,483,647之间的整数值,在某些情况下也可能是2个字节或8个字节,int是一种常用的数据类型,用于表示整数,需要根据具体情况选择合适的数据类型,以确保程序的正确性和性能。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

511

2024.08.29

c++怎么把double转成int
c++怎么把double转成int

本专题整合了 c++ double相关教程,阅读专题下面的文章了解更多详细内容。

46

2025.08.29

C++中int的含义
C++中int的含义

本专题整合了C++中int相关内容,阅读专题下面的文章了解更多详细内容。

179

2025.08.29

javascriptvoid(o)怎么解决
javascriptvoid(o)怎么解决

javascriptvoid(o)的解决办法:1、检查语法错误;2、确保正确的执行环境;3、检查其他代码的冲突;4、使用事件委托;5、使用其他绑定方式;6、检查外部资源等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

171

2023.11.23

java中void的含义
java中void的含义

本专题整合了Java中void的相关内容,阅读专题下面的文章了解更多详细内容。

92

2025.11.27

笔记本电脑卡反应很慢处理方法汇总
笔记本电脑卡反应很慢处理方法汇总

本专题整合了笔记本电脑卡反应慢解决方法,阅读专题下面的文章了解更多详细内容。

1

2025.12.25

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
HTML5/CSS3/JavaScript/ES6入门课程
HTML5/CSS3/JavaScript/ES6入门课程

共102课时 | 6.5万人学习

前端基础到实战(HTML5+CSS3+ES6+NPM)
前端基础到实战(HTML5+CSS3+ES6+NPM)

共162课时 | 18.4万人学习

第二十二期_前端开发
第二十二期_前端开发

共119课时 | 12.1万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号