0

背景:我正在做一个项目,客户需要我们使用自定义动态内存分配,而不是从堆栈中分配对象。请注意,有问题的对象在编译期间具有已知的大小,甚至不需要动态分配。这让我想知道,

在哪些情况下,对象的自定义动态内存分配可能比从堆栈中分配对象更好?(在编译期间已知大小)


一个例子。如果Dog是一个类,那么不仅仅是声明Dog puppy;他们希望我们做

Dog* puppy = nullptr; 
custom_alloc(puppy);
new(puppy) Dog(); // the constructor
// do stuff
puppy->~Dog(); // the destructor
custom_free(puppy)

custom_alloc我们不知道真正的功能。为了使程序运行,给定的custom_alloc函数将是malloc. 并且custom_free将是一个包装free

我不喜欢这种方法,并且想知道这何时真正有用,或者他们真正试图通过这样做来解决什么。

4

1 回答 1

0

可能的原因:

  1. 堆栈大小是有限的;虽然典型的线程库为每个线程的堆栈分配 1-10 MB,但对于预期同时启动数百或数千个线程的应用程序(例如高流量 Web 服务器;Microsoft IIS 过去使用256 KB 限制,对于 64 位设置仅将其提高到 512 KB)。

  2. 您可能希望在函数返回后保留一个对象(不使用全局变量)。虽然 NRVO 和/或移动语义确实意味着按值返回对象通常相对便宜,但当 NRVO 不适用时,围绕单个指针进行复制比其他任何东西都便宜。

  3. 审计/跟踪:他们可能希望针对特定类型使用自定义函数来跟踪内存分配模式

  4. 持久存储:分配器可以由内存映射文件支持;对于结构化数据,该文件可以作为长期存储的两倍

  5. 性能:众所周知,自定义分配器(例如 Intel 的 TBB)在某些情况下会显着减少运行时间。这更像是使用自定义分配器而不是默认分配器的理由;自定义分配器通常不会击败堆栈存储(除非在真正的小众情况下,可以通过从堆栈中删除大对象并将它们放入自己的专用存储中来改善内存局部性)。

  6. (可能是一个糟糕的主意)避免异常处理清理开销。如果您的类是 RAII,则必须生成代码以在发生异常时沿各种代码路径清理它们。原始指针不会生成任何此类代码。当然,如果您自己不采取措施对异常执行清理,这意味着内存泄漏,但在极少数情况下(例如,当您希望程序完全退出,并且您希望操作系统处理内存清理时)这可能提供轻微的“好处”。

  7. 上述组合:他们可能希望能够通过链接不同的运行时库来在跟踪分配器和性能分配器之间进行交换,以提供custom_alloc

尽管如此,他们这样做的方法非常糟糕。需要手动放置new和析构函数调用是令人不快的(std::unique_ptr/std::shared_ptr可以通过提供为您完成这项工作的自定义删除器函子来提供一些帮助,但即使这样也很丑陋)。通常,如果您需要自定义分配器,您会为operator new/定义适当的重载operator delete。这样,避免堆栈分配(无论出于何种原因)几乎不会那么令人不快。std::unique_ptr您只需用s (通过创建)替换逻辑堆栈分配的变量std::make_unique,您的代码仍然相当简单。

于 2019-07-11T20:14:59.560 回答