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未实现自定义的拷贝赋值、移动构造及移动赋值运算符:
- C++默认的拷贝赋值运算符会执行浅拷贝,导致两个RegionAllocator实例共享同一个
data指针。当这些实例被销毁时,都会调用std::free(data),从而触发double free。 - Debug模式下,MSVC的STL容器(比如
std::vector)会进行更多的边界检查和分配器操作,可能会额外拷贝分配器实例,进而触发浅拷贝导致的double free。 - 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
相关产品推荐
相关产品推荐

