C++ 自定义内存池优化点云处理:从裸指针到 RAII 自动回收

标题

C++ 自定义内存池优化点云处理:从裸指针到 RAII 自动回收

摘要

实现一个基于 std::shared_ptr 自定义 deleter 的点云内存池,解决裸指针手动 release 容易泄漏的问题。采用预分配 + 空闲队列 + 引用计数 + 自动清零归还,适合激光雷达、点云处理、感知管线等高频分配场景。

关键词

C++;内存池;对象池;shared_ptr;自定义 deleter;点云;RAII;std::queue

一、问题背景

点云处理中,每帧都要申请和释放大量数据。

如果每次都用 new / delete,会带来频繁堆分配、内存碎片和额外锁竞争。

原始版本使用裸指针和手动 release,调用方一旦忘记归还,就会造成内存泄漏。

同时,空闲队列、析构函数和容器类型不匹配,也容易引发编译错误和未定义行为。

二、改进思路

核心目标有三点:

第一,预分配内存,减少运行时分配开销;

第二,用空闲队列管理可用块,实现 O(1) 获取和归还;

第三,用 std::shared_ptr 自定义 deleter 替代手动 release,离开作用域自动归还。

三、关键设计

模块 作用 实现方式
PointXYZ 替代 vector<float> 语义更清晰,便于后续对齐
Block 内存池基本单元 包含数据向量和 std::atomic<int> 引用计数
pool_ 所有权容器 vector<shared_ptr<Block>> 负责最终释放
freeList_ 空闲块队列 queue<shared_ptr<Block>> 管理可用块
acquire 获取内存 从队列取出并返回带自定义 deleter 的 shared_ptr
deleter 自动归还 清零数据、引用计数减一、归零后放回队列
available 监控容量 加锁返回空闲队列大小

四、完整代码

cpp 复制代码
#include <iostream>
#include <vector>
#include <queue>
#include <mutex>
#include <memory>
#include <algorithm>
#include <atomic>

// 点云基本结构,替代原来的 vector<float>
// 每个点有 xyz 三个坐标,语义清晰
struct PointXYZ {
    float x = 0.0f, y = 0.0f, z = 0.0f;
};

class PointCloudPool : public std::enable_shared_from_this<PointCloudPool> {
private:
    // 内存池里的一块,包含实际点云数据和引用计数
    struct Block {
        std::vector<PointXYZ> data;
        std::atomic<int> ref{0}; // 0=空闲,>=1=正在被使用
    };

    // pool_ 持有所有 Block,负责最终释放内存
    std::vector<std::shared_ptr<Block>> pool_;

    // freeList_ 存放当前空闲可用的 Block (使用 std::queue)
    std::queue<std::shared_ptr<Block>> freeList_;

    mutable std::mutex mtx_; // 加 mutable 允许在 const 方法中加锁
    size_t capacity_;

public:
    // 1. 构造函数:启动时一次性分配 count 块内存,全部塞进 freeList_
    PointCloudPool(size_t count, size_t pointsPerCloud) : capacity_(count) {
        for (size_t i = 0; i < count; ++i) {
            auto blk = std::make_shared<Block>();
            blk->data.resize(pointsPerCloud);
            pool_.push_back(blk);
            freeList_.push(blk);
        }
        std::cout << "内存池初始化完成,预分配 " << count << " 块内存" << std::endl;
    }

    // 2. 析构函数:直接清空 pool_,所有 Block 会被自动安全释放
    // 删掉了会导致报错的 freeList_.clear()
    ~PointCloudPool() {
        std::lock_guard<std::mutex> lock(mtx_);
        pool_.clear(); 
    }

    // 3. 获取内存:返回 shared_ptr,利用自定义 deleter 自动归还
    std::shared_ptr<std::vector<PointXYZ>> acquire() {
        std::lock_guard<std::mutex> lock(mtx_);
        if (freeList_.empty()) return nullptr;  // 池空了返回空指针

        auto blk = freeList_.front();
        freeList_.pop();
        blk->ref.store(1);  // 标记为已占用

        auto self = shared_from_this();
        auto raw = &blk->data;

        // 自定义 shared_ptr,deleter 在对象离开作用域时自动触发
        return std::shared_ptr<std::vector<PointXYZ>>(
            raw,
            blk, self {
                // 归还时先清零,防止下一帧拿到脏数据
                std::fill(blk->data.begin(), blk->data.end(), PointXYZ{});
                // 引用计数减 1,如果降为 0 就放回空闲队列
                if (blk->ref.fetch_sub(1) == 1) {
                    std::lock_guard<std::mutex> lk(self->mtx_);
                    self->freeList_.push(blk);
                }
            });
    }

    // 4. 查询当前还有多少块空闲内存
    size_t available() const { 
        std::lock_guard<std::mutex> lock(mtx_);
        return freeList_.size(); 
    }
};

int main() {
    // 创建内存池:10 块,每块 10 万个点
    auto pool = std::make_shared<PointCloudPool>(10, 100000);

    // 检查初始状态
    std::cout << "初始化空闲块数: " << pool->available() << std::endl;

    // 获取一块内存
    auto cloud = pool->acquire();
    if (cloud) {
        std::cout << "获取点云成功,点数: " << cloud->size()
                  << ",剩余空闲: " << pool->available() << std::endl;
        // 写入测试数据
        (*cloud)[0] = {1.0f, 2.0f, 3.0f};
    } 
    // cloud 离开作用域,deleter 自动触发:清零 + 归还

    // 检查归还后的状态
    std::cout << "归还后空闲块数: " << pool->available() << std::endl;

    return 0;
}

五、踩坑记录

报错现象 原因 修复方式
queue 没有 clear() std::queue 不提供 clear 接口 析构时改为 pool_.clear()
vector 没有 push() push 是 queue 的接口 freeList_ 统一使用 std::queue
vector 没有 front() 原定义与使用方式不匹配 改用 queue 的 front() / pop()
初始化后空闲数异常 缺少构造函数或类型不匹配 补全构造函数,统一容器类型
多线程访问不安全 空闲队列被并发修改 acquire、available、析构都加锁

六、运行结果

程序输出大致如下:

text 复制代码
内存池初始化完成,预分配 10 块内存
初始化空闲块数: 10
获取点云成功,点数: 100000,剩余空闲: 9
归还后空闲块数: 10

七、适用场景

该方案适合点云帧缓存、传感器回调缓冲、仿真数据复用等固定大小对象的高频分配场景。

如果进入高频多线程管线,可以进一步替换为无锁队列、线程本地缓存或 std::pmr 分配器,降低锁竞争。

相关推荐
梦帮科技1 小时前
量子纠缠网络与量子隐形传态攻防:量子中继器信道串扰、纠缠态保真度降解与量子路由劫持
开发语言·网络·人工智能·算法·自动化·php
老王IT1 小时前
C语言语法基础语法介绍
c语言·开发语言·算法
zhangdaliangmvp2 小时前
快速排序的学习理解:从基础分治到三路划分优化
c++·学习·算法
code2cat2 小时前
Java高级篇之JDBC:连接数据库,完成查询、更新与事务
java·开发语言·数据库
cu1432 小时前
细谈IT6563的具体功能与其应用
c语言·c++·单片机·嵌入式硬件
hzxxxz2 小时前
PHP引擎两个高危漏洞复盘:计数顺序与旧地址缺陷的机理与处置清单
开发语言·php
马立杰2 小时前
PHP 未授权 RCE 应急响应:CVE-2026-109618 CVE-2026-109619 排查与加固实战
android·开发语言·php
布莱克6052 小时前
C++ 内存泄漏全解析:常见场景、成因与规避实践
开发语言·c++·内存泄露
jimy13 小时前
lvalue、xvalue、prvalue(一):三者的分类
开发语言·c++