C++ 第四阶段 内存管理 - 第一节:内存池与自定义分配器
本文介绍了C++中内存管理的优化技术,重点讲解了自定义分配器和内存池的实现与应用。主要内容包括:1)自定义分配器的核心接口与简单实现示例;2)内存池的工作原理与固定大小内存池的实现;3)如何将内存池与STL容器结合使用;4)在游戏开发、高性能计算和嵌入式系统等典型场景中的应用。文章还提供了完整的代码示例,并强调了线程安全、内存碎片、性能测试等注意事项。这些技术可以有效优化内存分配效率,降低系统调用
·
目录
一、内存管理的核心概念
在 C++ 中,内存管理是程序性能和稳定性的重要基础。默认的 new/delete 和 malloc/free 提供了通用的内存分配方式,但在某些场景下(如高性能计算、游戏开发、嵌入式系统),自定义内存分配器和内存池技术可以显著优化内存使用效率、减少碎片化、提升性能。
二、自定义分配器(Custom Allocator)
1. 自定义分配器的作用
- 替代默认分配器:通过自定义分配策略(如内存池、对象复用)替代
new/delete。 - 与 STL 容器集成:STL 容器(如
std::vector,std::map)支持通过模板参数指定自定义分配器。 - 优化性能:针对特定场景(如频繁分配/释放小对象)设计高效的分配策略。
2. 自定义分配器的基本接口
自定义分配器需要实现以下核心方法:
template <typename T>
class CustomAllocator {
public:
using value_type = T;
// 分配内存
T* allocate(std::size_t n);
// 释放内存
void deallocate(T* p, std::size_t n);
// 构造对象
template <typename U, typename... Args>
void construct(U* p, Args&&... args);
// 析构对象
template <typename U>
void destroy(U* p) noexcept;
};
3. 简单自定义分配器示例
#include <iostream>
#include <memory>
#include <vector>
template <typename T>
class SimpleAllocator {
public:
using value_type = T;
T* allocate(std::size_t n) {
if (n > std::allocator_traits<SimpleAllocator>::max_size(*this)) {
throw std::bad_alloc();
}
return static_cast<T*>(::operator new(n * sizeof(T)));
}
void deallocate(T* p, std::size_t n) noexcept {
::operator delete(p);
}
template <typename U, typename... Args>
void construct(U* p, Args&&... args) {
new(p) U(std::forward<Args>(args)...);
}
template <typename U>
void destroy(U* p) noexcept {
p->~U();
}
};
int main() {
SimpleAllocator<int> alloc;
std::vector<int, SimpleAllocator<int>> vec(alloc);
vec.push_back(1);
vec.push_back(2);
vec.push_back(3);
for (int i : vec) {
std::cout << i << " "; // 输出 1 2 3
}
return 0;
}
三、内存池(Memory Pool)
1. 内存池的核心思想
内存池是一种预分配大块内存并按需分配小块的技术,适用于频繁创建/销毁对象的场景(如游戏中的子弹、线程池)。其优势包括:
- 减少系统调用开销:通过一次性分配大块内存,减少
malloc/free的调用频率。 - 降低内存碎片:固定大小的内存块分配避免了碎片化问题。
- 提高分配速度:通过链表管理空闲块,分配和释放操作接近常数时间复杂度。
2. 固定大小内存池实现
#include <iostream>
#include <vector>
#include <memory>
// 固定大小内存池
template <typename T>
class FixedBlockAllocator {
private:
struct FreeListNode {
FreeListNode* next;
};
size_t block_size_;
size_t pool_size_;
char* memory_pool_;
FreeListNode* free_list_;
public:
FixedBlockAllocator(size_t num_blocks)
: block_size_(sizeof(T)), pool_size_(num_blocks) {
memory_pool_ = reinterpret_cast<char*>(::operator new(block_size_ * num_blocks));
free_list_ = reinterpret_cast<FreeListNode*>(memory_pool_);
// 初始化空闲链表
for (size_t i = 0; i < pool_size_ - 1; ++i) {
FreeListNode* node = reinterpret_cast<FreeListNode*>(memory_pool_ + i * block_size_);
node->next = reinterpret_cast<FreeListNode*>(memory_pool_ + (i + 1) * block_size_);
}
FreeListNode* last_node = reinterpret_cast<FreeListNode*>(memory_pool_ + (pool_size_ - 1) * block_size_);
last_node->next = nullptr;
}
~FixedBlockAllocator() {
::operator delete(memory_pool_);
}
T* allocate() {
if (!free_list_) {
throw std::bad_alloc(); // 内存池耗尽
}
void* p = free_list_;
free_list_ = free_list_->next;
return reinterpret_cast<T*>(p);
}
void deallocate(T* p) {
FreeListNode* node = reinterpret_cast<FreeListNode*>(p);
node->next = free_list_;
free_list_ = node;
}
};
3. 将内存池与 STL 容器结合
template <typename T>
class PoolAllocator {
public:
using value_type = T;
PoolAllocator(FixedBlockAllocator<T>& pool) : memory_pool(pool) {}
T* allocate(std::size_t n) {
if (n != 1) {
throw std::bad_alloc();
}
return memory_pool.allocate();
}
void deallocate(T* p, std::size_t n) {
if (n != 1) {
throw std::bad_alloc();
}
memory_pool.deallocate(p);
}
private:
FixedBlockAllocator<T>& memory_pool;
};
int main() {
FixedBlockAllocator<int> int_pool(10); // 创建大小为10的内存池
PoolAllocator<int> allocator(int_pool);
std::vector<int, PoolAllocator<int>> vec(allocator);
for (int i = 0; i < 10; ++i) {
vec.push_back(i);
}
for (const auto& value : vec) {
std::cout << value << " "; // 输出 0 1 2 ... 9
}
return 0;
}
四、自定义分配器与内存池的典型应用场景
1. 游戏开发中的对象池
- 场景:游戏中频繁创建和销毁“子弹”对象。
- 问题:频繁调用
new/delete导致性能下降和内存碎片。 - 解决方案:使用内存池分配器管理子弹对象的内存,预先分配固定数量的内存块。
2. 高性能计算中的内存优化
- 场景:需要频繁分配/释放固定大小的数据结构(如矩阵、缓存块)。
- 问题:默认分配器性能不足。
- 解决方案:通过自定义分配器优化分配逻辑,减少系统调用开销。
3. 嵌入式系统中的资源受限环境
- 场景:内存资源有限,需严格控制内存分配。
- 问题:动态内存分配可能导致不可预测的延迟。
- 解决方案:使用内存池预分配所有内存,避免运行时动态分配。
五、注意事项与最佳实践
-
线程安全性
- 内存池和自定义分配器默认不支持多线程安全。若需多线程支持,需引入锁机制(如
std::mutex)。
- 内存池和自定义分配器默认不支持多线程安全。若需多线程支持,需引入锁机制(如
-
内存碎片问题
- 内存池适用于固定大小对象分配,无法解决可变大小内存块的碎片问题。
-
性能测试
- 自定义分配器的性能需通过基准测试验证(如使用
Google Benchmark)。
- 自定义分配器的性能需通过基准测试验证(如使用
-
与 STL 的兼容性
- 自定义分配器需符合 STL 的
Allocator接口规范,否则可能导致未定义行为。
- 自定义分配器需符合 STL 的
-
资源管理
- 使用 RAII 技术确保内存池的生命周期与容器一致,避免资源泄漏。
六、总结
| 技术 | 核心优势 |
|---|---|
| 自定义分配器 | 提供对内存分配的细粒度控制,优化性能,适用于特定场景(如高频分配/释放)。 |
| 内存池 | 通过预分配减少系统调用开销,降低内存碎片,提高分配速度。 |
| 与 STL 集成 | 通过模板参数将自定义分配器应用于 STL 容器,灵活适配不同场景需求。 |
七、代码示例汇总
1. 自定义分配器完整实现
#include <iostream>
#include <vector>
#include <memory>
template <typename T>
class SimpleAllocator {
public:
using value_type = T;
T* allocate(std::size_t n) {
if (n > std::allocator_traits<SimpleAllocator>::max_size(*this)) {
throw std::bad_alloc();
}
return static_cast<T*>(::operator new(n * sizeof(T)));
}
void deallocate(T* p, std::size_t n) noexcept {
::operator delete(p);
}
template <typename U, typename... Args>
void construct(U* p, Args&&... args) {
new(p) U(std::forward<Args>(args)...);
}
template <typename U>
void destroy(U* p) noexcept {
p->~U();
}
};
int main() {
SimpleAllocator<int> alloc;
std::vector<int, SimpleAllocator<int>> vec(alloc);
vec.push_back(10);
vec.push_back(20);
vec.push_back(30);
for (int i : vec) {
std::cout << i << " "; // 输出 10 20 30
}
return 0;
}
2. 内存池与 STL 容器结合
#include <iostream>
#include <vector>
#include <memory>
template <typename T>
class FixedBlockAllocator {
private:
struct FreeListNode {
FreeListNode* next;
};
size_t block_size_;
size_t pool_size_;
char* memory_pool_;
FreeListNode* free_list_;
public:
FixedBlockAllocator(size_t num_blocks)
: block_size_(sizeof(T)), pool_size_(num_blocks) {
memory_pool_ = reinterpret_cast<char*>(::operator new(block_size_ * num_blocks));
free_list_ = reinterpret_cast<FreeListNode*>(memory_pool_);
for (size_t i = 0; i < pool_size_ - 1; ++i) {
FreeListNode* node = reinterpret_cast<FreeListNode*>(memory_pool_ + i * block_size_);
node->next = reinterpret_cast<FreeListNode*>(memory_pool_ + (i + 1) * block_size_);
}
FreeListNode* last_node = reinterpret_cast<FreeListNode*>(memory_pool_ + (pool_size_ - 1) * block_size_);
last_node->next = nullptr;
}
~FixedBlockAllocator() {
::operator delete(memory_pool_);
}
T* allocate() {
if (!free_list_) {
throw std::bad_alloc();
}
void* p = free_list_;
free_list_ = free_list_->next;
return reinterpret_cast<T*>(p);
}
void deallocate(T* p) {
FreeListNode* node = reinterpret_cast<FreeListNode*>(p);
node->next = free_list_;
free_list_ = node;
}
};
template <typename T>
class PoolAllocator {
public:
using value_type = T;
PoolAllocator(FixedBlockAllocator<T>& pool) : memory_pool(pool) {}
T* allocate(std::size_t n) {
if (n != 1) {
throw std::bad_alloc();
}
return memory_pool.allocate();
}
void deallocate(T* p, std::size_t n) {
if (n != 1) {
throw std::bad_alloc();
}
memory_pool.deallocate(p);
}
private:
FixedBlockAllocator<T>& memory_pool;
};
int main() {
FixedBlockAllocator<int> int_pool(10);
PoolAllocator<int> allocator(int_pool);
std::vector<int, PoolAllocator<int>> vec(allocator);
for (int i = 0; i < 10; ++i) {
vec.push_back(i);
}
for (const auto& value : vec) {
std::cout << value << " "; // 输出 0 1 2 ... 9
}
return 0;
}
八、扩展阅读
- C++17/20 的
std::pmr::memory_resource:现代 C++ 提供了更灵活的内存管理接口。 - 游戏引擎中的内存管理:如 Unreal Engine 的
FMemory模块。 - 性能分析工具:使用
Valgrind、gperftools等工具检测内存分配性能瓶颈。
openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。
更多推荐


所有评论(0)