目录

一、内存管理的核心概念

二、自定义分配器(Custom Allocator)

1. 自定义分配器的作用

2. 自定义分配器的基本接口

3. 简单自定义分配器示例

三、内存池(Memory Pool)

1. 内存池的核心思想

2. 固定大小内存池实现

3. 将内存池与 STL 容器结合

四、自定义分配器与内存池的典型应用场景

1. 游戏开发中的对象池

2. 高性能计算中的内存优化

3. 嵌入式系统中的资源受限环境

五、注意事项与最佳实践

六、总结

七、代码示例汇总

1. 自定义分配器完整实现

2. 内存池与 STL 容器结合

八、扩展阅读


C++从入门到入土学习导航_c++学习进程-CSDN博客

一、内存管理的核心概念

在 C++ 中,内存管理是程序性能和稳定性的重要基础。默认的 new/deletemalloc/free 提供了通用的内存分配方式,但在某些场景下(如高性能计算、游戏开发、嵌入式系统),自定义内存分配器内存池技术可以显著优化内存使用效率、减少碎片化、提升性能。


二、自定义分配器(Custom Allocator)

1. 自定义分配器的作用
  • 替代默认分配器:通过自定义分配策略(如内存池、对象复用)替代 new/delete
  • 与 STL 容器集成:STL 容器(如 std::vectorstd::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. 嵌入式系统中的资源受限环境
  • 场景:内存资源有限,需严格控制内存分配。
  • 问题:动态内存分配可能导致不可预测的延迟。
  • 解决方案:使用内存池预分配所有内存,避免运行时动态分配。

五、注意事项与最佳实践

  1. 线程安全性

    • 内存池和自定义分配器默认不支持多线程安全。若需多线程支持,需引入锁机制(如 std::mutex)。
  2. 内存碎片问题

    • 内存池适用于固定大小对象分配,无法解决可变大小内存块的碎片问题。
  3. 性能测试

    • 自定义分配器的性能需通过基准测试验证(如使用 Google Benchmark)。
  4. 与 STL 的兼容性

    • 自定义分配器需符合 STL 的 Allocator 接口规范,否则可能导致未定义行为。
  5. 资源管理

    • 使用 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 模块。
  • 性能分析工具:使用 Valgrindgperftools 等工具检测内存分配性能瓶颈。
Logo

openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。

更多推荐