c++ - 如何控制第三方库代码中的内存分配策略?

标签 c++ new-operator delete-operator

上一个标题:“我必须替换全局运算符 new 和 delete 以更改第三方代码中的内存分配策略吗?”

短篇小说: 我们需要替换第三方库中的内存分配技术而不改变其源代码。

长篇大论:

考虑进行大量动态分配(也许几乎所有可用系统内存)的内存绑定(bind)应用程序。我们使用专门的分配器,并在任何地方使用它们(shared_ptr's、容器等)。我们对应用程序中分配的每一个内存字节拥有完全的控制权和权力。

另外,我们需要链接到一个第三方帮助库。那个讨厌的家伙以某种标准方式进行分配,使用默认运算符 newnew[]deletedelete[]malloc 或其他非标准的东西(让我们概括并说我们不知道这个库如何管理它的堆分配)。

如果这个帮助程序库进行了足够大的分配,我们可能会遇到 HDD 抖动、内存碎片和对齐问题、内存不足 bad_alloc 和各种问题。

我们不能(或不想)更改库源代码。

第一次尝试:

我们以前从未在发布版本中遇到过如此邪恶的“黑客行为”。使用覆盖运算符 new 进行的第一次测试工作正常,除了:

  • 我们不知道 future 会遇到什么问题(这太可怕了)
  • 我们的用户(甚至我们的分配器)现在必须以与我们相同的方式进行分配

问题:

  1. 有没有办法在不重载全局运算符的情况下 Hook 这些分配? (仅限本地库的 Hook ?)
  2. ...如果我们不知道它到底使用什么:mallocnew
  3. 这个签名列表是否完整? (我们没有其他必须实现的东西):

    void* operator new (std::size_t size) throw (std::bad_alloc);
    void* operator new (std::size_t size, const std::nothrow_t& nothrow_value) throw();
    void* operator new (std::size_t size, void* ptr) throw();
    void* operator new[] (std::size_t size) throw (std::bad_alloc);
    void* operator new[] (std::size_t size, const std::nothrow_t& nothrow_value) throw();
    void* operator new[] (std::size_t size, void* ptr) throw();
    
    void operator delete (void* ptr) throw();
    void operator delete (void* ptr, const std::nothrow_t& nothrow_constant) throw();
    void operator delete (void* ptr, void* voidptr2) throw();
    void operator delete[] (void* ptr) throw();
    void operator delete[] (void* ptr, const std::nothrow_t& nothrow_constant) throw();
    void operator delete[] (void* ptr, void* voidptr2) throw();
    
  4. 如果该库是动态的,会有什么不同?

编辑#1

如果可能,最好使用跨平台解决方案(看起来不太可能)。如果没有,我们的主要平台:

  • Windows x86/x64 (msvc 10)
  • Linux x86/x64 (gcc 4.6)

编辑#2

将近 2 年过去了,很少有操作系统和编译器版本进化,所以我很好奇这方面是否有一些新的和未开发的东西?有什么标准建议吗?操作系统细节?黑客?您今天如何编写需要大量内存的应用程序?请分享您的经验。

最佳答案

呃,我的同情。这将在很大程度上取决于您的编译器、您的 libc 等。过去对我们来说在不同程度上“起作用”的一些橡胶与道路策略(/me 大括号表示反对票)是:

  • 您建议的 operator new/operator delete 重载 - 尽管请注意,有些编译器对没有 throw() 规范很挑剔,有些人真的想要它们,有些人想要它们用于新的但不用于删除等(我有一个巨大的特定于平台的 #if/#elif block 用于所有 4+我们现在正在开发的平台)。
  • 另外值得注意的是:您通常可以忽略展示位置版本,它们不会分配。
  • __malloc_hook and friends -- 请注意,这些已被弃用并且有线程竞争条件 -- 但它们很好,因为 new/delete 往往是根据 malloc 实现的(但并非总是如此)。
  • 提供替换 malloccallocreallocfree 并让您的链接器参数正确顺序以便覆盖发生(这是 gcc 这些天推荐的,尽管我遇到过不可能做到的情况,我不得不使用已弃用的 __malloc_hook)——再次,newdelete 倾向于根据这些来实现,但并非总是如此。
  • 在“我们的代码”中避免使用所有标准分配方法(operator newmalloc 等)并改用自定义函数——对于现有的代码库来说并不容易。
  • 跟踪库作者并提供 savage beat 礼貌请求或补丁以更改他们的库以允许您指定不同的分配器(这可能比自己执行此操作更快)——我认为这导致了我编写的任何库的“客户端始终指定分配器或进行分配”的基本规则。

请注意,这不是标准所说的应该发生的答案,只是我的经验。过去,我使用过不少有问题/损坏的编译器和 libc 实现,所以 YMMV。我也有幸在相当“密封的系统”上工作,而不必担心任何特定应用程序的可移植性。

关于动态库:我自己目前在这方面有点困难;我们的“应用程序”作为动态 .so 加载,我们必须非常小心地将任何 delete/free 请求传递回默认值如果它们不是来 self 们的分配器。当前的解决方案是封锁我们对特定区域的分配:如果我们从该地址范围内获得删除/释放,我们将调度到我们的处理程序,否则返回默认值......我什至玩弄过(恐怖) 检查调用者地址以查看它是否在我们的地址空间中的想法。 (不过,这种黑客攻击会增加繁荣的可能性。)

即使您是流程负责人并且您正在使用外部库,这也可能是一个有用的策略:标记或限制或以其他方式以某种方式识别您自己的分配(甚至保留您知道的分配列表) ),然后传递任何未知数。不过,所有这些都有丑陋的副作用和局限性。

(期待其他答案!)

关于c++ - 如何控制第三方库代码中的内存分配策略?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16377991/

相关文章:

c++ - C++如果在创建指向堆的指针后不调用 'delete'运算符怎么办?

c++ - 在堆栈上分配的变量上调用 delete

c++ - 我是否必须将经过测试的源文件添加到 VS2017 中的单元测试项目?

c++ - 如何在二维数组中分配动态内存?

c++ - 如何使用 std::array 构造函数参数 C++ 列表初始化 const std::array 成员

c++ - 你如何在 C++ 中使用 'realloc'?

c++ - C++中模板对象的动态数组

c++ - qDeleteAll 和 new[]

java - 使用json在c++和java之间交换对象

c++ - 当在 Objective-C++ 中应用于 __weak 指针时,通过 "auto"关键字推导类型的规则是什么?