【问题标题】:Why use custom dynamic memory allocation over memory from stack?为什么使用自定义动态内存分配而不是堆栈中的内存?
【发布时间】:2019-11-21 13:53:21
【问题描述】:

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

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


一个例子。如果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 的包装器

我不喜欢这种方法,我想知道这在什么时候真正有用,或者他们真正想通过这样做来解决什么问题。

【问题讨论】:

  • 一个原因是生命周期,另一个是堆栈大小限制
  • 驱动开发(堆栈严重受限)。
  • 如果分配/取消分配与正常的函数范围嵌套一致,那么一定要使用局部变量,但通常情况并非如此。
  • 您遗漏了该代码中最丑陋的部分;必须手动触发析构函数。 Placement-new 有它的位置(不是双关语),但这些位置很少出现在正常的日常代码中。如果这是来自某些微控制器环境或某些其他特定情况的边缘情况,那是另一回事。你说“他们想让我们做......” - 你问他们(不管'他们'是谁)为什么?他们可能 (a) 有一些他们认为重要的原因,或者 (b) 没有人知道线索,但对于仍然在那里的人来说,这就是一直以来的方式。
  • 我想知道为什么您的问题是关于“堆栈与自定义分配器”而不是“堆栈与堆”或“默认分配器与自定义分配器”?这个问题似乎缺少一个链接,这是new/delete 使用的默认动态分配器。我的意思是:您是在问“为什么要使用自定义动态分配器”还是“为什么要使用动态分配而不是堆栈”?

标签: c++ memory memory-management dynamic-memory-allocation static-memory-allocation


【解决方案1】:

可能的原因:

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

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

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

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

  5. 性能:Custom allocators (e.g. Intel's TBB) have been known to dramatically reduce runtime in certain circumstances。这更像是使用自定义分配器而不是默认分配器的理由;自定义分配器通常不会胜过堆栈存储(除了在真正特殊情况下,可以通过从堆栈中删除大对象并将它们放在自己的专用存储中来改善内存局部性)。

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

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

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

【讨论】:

    猜你喜欢
    • 2014-01-19
    • 2023-03-21
    • 2019-12-07
    • 2019-05-17
    • 2020-04-25
    • 2014-12-05
    • 2010-12-11
    • 2010-12-05
    • 2020-09-15
    相关资源
    最近更新 更多