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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 19:15:31