Visual Studio 2019中std::pmr::memory_resource是否存在问题?
C++17内存资源(内存池)异常问题排查
我正在学习C++17的memory resources(比如内存池)相关概念,部分功能能正常运行,但使用new_delete_resource()以外的内存资源时会出现异常行为。我用了Visual Studio 2019,同时也测试过LLVM和Intel 2022编译器。
简化示例代码
#include <iostream> #include <memory_resource> int main(int argc, char *argv[]) { //std::pmr::memory_resource* mr = std::pmr::new_delete_resource(); // 正常运行 std::pmr::memory_resource *mr = new std::pmr::unsynchronized_pool_resource(); // 运行异常 //std::pmr::memory_resource* mr = new std::pmr::monotonic_buffer_resource(1000); // 运行异常 void* ptr = mr->allocate(sizeof(int)); int *i1 = new (ptr) int; *i1 = 4711; ptr = mr->allocate(sizeof(int)); int* i2 = new (ptr) int; *i2 = 815; std::cout << *i1 << '\n'; std::cout << *i2 << '\n'; delete i1; std::cout << *i2 << '\n'; return 0; }
这是不是Visual Studio的库实现bug?我期望三种内存资源的运行结果一致。
使用类的示例代码
#include <iostream> #include <memory_resource> class myclass { public: int x; }; int main(int argc, char *argv[]) { //std::pmr::memory_resource* mr = std::pmr::new_delete_resource(); // 正常运行 std::pmr::memory_resource *mr = new std::pmr::unsynchronized_pool_resource(); // 运行异常 //std::pmr::memory_resource* mr = new std::pmr::monotonic_buffer_resource(1000); // 运行异常 void* ptr = mr->allocate(sizeof(myclass)); myclass *c1 = new (ptr) myclass; c1->x = 4711; ptr = mr->allocate(sizeof(myclass)); myclass *c2 = new (ptr) myclass; c2->x = 815; std::cout << c1->x << '\n'; std::cout << c2->x << '\n'; c1->~myclass(); mr->deallocate(c1, sizeof(myclass)); std::cout << c2->x << '\n'; return 0; }
问题原因分析
这不是Visual Studio的库实现bug,而是违反了内存资源的核心使用规则:
new_delete_resource()底层直接调用标准new/delete,所以用delete i1刚好匹配,不会出问题。- 但
unsynchronized_pool_resource和monotonic_buffer_resource是自定义内存池,它们分配的内存绝对不能用标准delete释放,必须调用对应内存资源的deallocate方法,且要严格匹配分配时的大小和对齐参数。
第一个示例的核心错误
delete i1; // 错误:用标准delete释放内存池分配的内存,属于内存操作不匹配
正确的释放流程是:先调用析构函数(对于int这类平凡类型可省略,但保持流程统一更规范),再调用对应内存资源的deallocate:
i1->~int(); // 平凡类型可省略,形式上更规范 mr->deallocate(i1, sizeof(int));
第二个示例的潜在问题
你已经做对了析构和deallocate,但仍可能异常的原因是:你用new创建了内存池对象,但没有在程序结束时销毁它。内存池本身持有分配的内存块,销毁时会自动释放所有未手动deallocate的内存;如果内存池生命周期管理不当,可能导致访问已释放的内存区域。
另外,monotonic_buffer_resource的特性是只分配不回收,调用deallocate不会实际释放内存,直到整个资源被销毁,但这不会导致立即异常,除非后续存在越界操作。
修正后的第一个示例代码
#include <iostream> #include <memory_resource> int main(int argc, char *argv[]) { std::pmr::memory_resource *mr = new std::pmr::unsynchronized_pool_resource(); void* ptr = mr->allocate(sizeof(int)); int *i1 = new (ptr) int(4711); ptr = mr->allocate(sizeof(int)); int* i2 = new (ptr) int(815); std::cout << *i1 << '\n'; std::cout << *i2 << '\n'; // 正确释放:先析构,再用对应内存资源deallocate i1->~int(); mr->deallocate(i1, sizeof(int)); std::cout << *i2 << '\n'; // 释放i2和内存池本身 i2->~int(); mr->deallocate(i2, sizeof(int)); delete mr; return 0; }
关键规则总结
- 内存资源分配的内存,必须用同一内存资源的
deallocate释放,禁止混用标准delete。 - 对于非平凡类型,必须先手动调用析构函数,再释放内存;平凡类型(如int)析构可省略,但建议保持统一流程。
- 内存池对象本身的生命周期要管理好,用完后要销毁(比如用
delete或智能指针),避免内存泄漏。
内容的提问来源于stack exchange,提问作者Thomas Krebs
相关产品推荐
相关产品推荐

