You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Debug模式下自定义RegionAllocator出现double free问题求助

RegionAllocator双free问题:Debug模式异常,Release/Dist正常

我在项目中实现了符合标准C++规范的RegionAllocator(又称Bump up Allocator),此前用C语言实现过类似分配器。本次用intptr_t*而非void*是因为之前在x86平台的C语言代码中遇到过对齐问题,具体实现代码如下:

template <class T>
class RegionAllocator {

public:
    using value_type = T;
    using pointer = T*;
    using const_pointer = const T*;
    using size_type = std::size_t;

    RegionAllocator() : size(0), capacity(STANDARD_ALLOCATOR_SIZE) {
        data = (intptr_t*)std::malloc(sizeof(intptr_t)*STANDARD_ALLOCATOR_SIZE);
        if (!data) throw std::bad_alloc();
    }
    template <class U>
    RegionAllocator(const RegionAllocator<U>& other) {
        data = (intptr_t*)std::malloc(sizeof(intptr_t)*other.get_capacity());
        if (!data) throw std::bad_alloc();
        memcpy(data, other.get_data(), other.get_size() * sizeof(intptr_t));
        size = other.get_size();
        capacity = other.get_capacity();
    }
    ~RegionAllocator() {
        std::free(data);
    }
    inline size_type to_allocator_space(const size_type n) {
        return ((n * sizeof(T)) + (sizeof(intptr_t) - 1)) / sizeof(intptr_t);
    }
    pointer allocate(size_type n) {
        if (n == 0) {
            throw std::bad_alloc();
            return nullptr;
        }
        size_type nn = to_allocator_space(n);
        if (size + nn > capacity) {
            throw std::bad_alloc();
            return nullptr;
        }
        pointer value = (pointer)(data + size);
        size += nn;
        return value;
    }
    void deallocate(pointer p, size_type n) noexcept {
        if ((size_type)p < (size_type)data) return;
        size_type nn = to_allocator_space(n);
        size_type pn = to_allocator_space((size_type)p);
        if (to_allocator_space((char*)p - (char*)data) + nn == size) {
            size -= nn;
        }
    }
    inline size_type max_size() const noexcept {
        return static_cast<size_type>(capacity*sizeof(intptr_t)) / sizeof(T);
    }
    template<class U> bool operator==(const RegionAllocator<U>& other) const noexcept
    {
        return other.data == this->data && other.size == this->size && other.capacity == this->capacity;
    }
    template<class U> bool operator!=(const RegionAllocator<U>& other) const noexcept
    {
        return other.data != this->data || other.size != this->size || other.capacity != this->capacity;
    }
    inline const intptr_t* get_data() const {
        return data;
    }
    inline size_type get_size() const {
        return size;
    }
    inline size_type get_capacity() const {
        return capacity;
    }
private:
    intptr_t* data;
    size_type size;
    size_type capacity;
};

测试用例:

std::vector<int, RegionAllocator<int>> vector;
vector.push_back(1);
vector.push_back(2);
vector.push_back(3);
for (size_t i = 0; i < vector.size(); ++i) {
    printf("%d ", vector[i]);
}
return 0;

项目的三种构建模式:

  • Debug:开启调试符号,关闭优化
  • Release:开启调试符号,开启优化
  • Dist:关闭调试符号,开启优化

问题现象与临时解决

Debug模式下会触发data的double free错误,但切换到Release或Dist模式后代码正常运行。已确认问题根源是重复调用std::free(data),临时通过让析构函数不释放内存(造成内存泄漏)规避了问题。测试环境为Windows平台下的MSVC编译器,修改C++标准版本无法解决Debug模式的问题。

问题原因分析

核心问题在于RegionAllocator未实现自定义的拷贝赋值、移动构造及移动赋值运算符:

  1. C++默认的拷贝赋值运算符会执行浅拷贝,导致两个RegionAllocator实例共享同一个data指针。当这些实例被销毁时,都会调用std::free(data),从而触发double free。
  2. Debug模式下,MSVC的STL容器(比如std::vector)会进行更多的边界检查和分配器操作,可能会额外拷贝分配器实例,进而触发浅拷贝导致的double free。
  3. Release/Dist模式下,编译器的优化(比如RVO/NRVO、消除冗余拷贝)会减少分配器实例的拷贝次数,直接避免了浅拷贝场景,因此不会触发double free错误,但这只是巧合,并非真正解决了问题。

修复方案

需要补充实现拷贝赋值运算符、移动构造函数和移动赋值运算符,确保内存管理的正确性:

// 拷贝赋值运算符
RegionAllocator& operator=(const RegionAllocator& other) {
    if (this == &other) return *this;
    std::free(data);
    data = (intptr_t*)std::malloc(sizeof(intptr_t)*other.capacity);
    if (!data) throw std::bad_alloc();
    memcpy(data, other.data, other.size * sizeof(intptr_t));
    size = other.size;
    capacity = other.capacity;
    return *this;
}

// 移动构造函数
RegionAllocator(RegionAllocator&& other) noexcept : data(other.data), size(other.size), capacity(other.capacity) {
    other.data = nullptr;
    other.size = 0;
    other.capacity = 0;
}

// 移动赋值运算符
RegionAllocator& operator=(RegionAllocator&& other) noexcept {
    if (this == &other) return *this;
    std::free(data);
    data = other.data;
    size = other.size;
    capacity = other.capacity;
    other.data = nullptr;
    other.size = 0;
    other.capacity = 0;
    return *this;
}

此外,还可以补充模板版的赋值运算符,确保不同U类型的RegionAllocator之间赋值时的正确性。

内容的提问来源于stack exchange,提问作者Dimitur Karabuyukov

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 23:51:00