本地堆栈数组与动态分配

问题描述 投票:0回答:3

用 C++ 编程时,根据经验,分界点是什么,而不是...

char array[MAX_PATH + 1] = {0};

...人们会使用:

char *array = new char[MAX_PATH + 1];
// ...
delete[] array;

在什么时候需要保留堆栈空间?

20 年前,我被告知应该在堆上分配所有超过 32 字节的数组,而不考虑性能成本,并将堆栈保存为简单变量。 我看到很多现代示例代码相当挥霍地使用堆栈,那么这种想法是否改变了?

c++ memory-management stack-overflow
3个回答
8
投票

堆栈分配的好处是不用检查是否失败,不用担心内存碎片,而且函数返回时内存会自动释放。缺点是您无法检查是否成功,并且失败时通常会发生一些不好的事情。

如果没有虚拟内存(例如,在小型微控制器上),通常可用内存较少。如果您的程序在堆栈上分配了太多空间,它就会溢出堆栈并写入超出为堆栈保留的空间的内容。您可以留出更多的堆栈空间来处理最坏的情况,但是无论您是否需要,该空间始终专用于堆栈。而使用动态分配时,您只需在使用时付费,并且可以检查

malloc
的返回码来确定是否发生故障(或者对于尝试将 C++ 压缩到微控制器中的人来说,捕获
new
上的异常) )。或者,如果应用程序始终需要它,您可以为对象静态保留一部分 RAM,因为这样它具有更可预测的运行时行为,并且您不必担心碎片。

使用虚拟内存,操作系统可以通过将页面添加到堆栈末尾的连续虚拟地址来动态增长堆栈。这样,您只需为正在使用的堆栈量支付物理内存费用。然而,使用较小的(32 位或更少?)地址空间,如果您有大量线程和太多堆栈分配,您仍然可能会遇到麻烦。

每个线程都必须为其堆栈拥有连续的空间,并且必须与系统中的其他所有线程共享。例如,如果操作系统占用 1 GB 的地址空间(并不罕见),并且您需要 1 GB 用于线程堆栈,那么您将剩下 2 GB 用于堆、数据和代码。如果您有 16 个线程,则意味着每个线程堆栈可以拥有大约 64 MB 的空间。根据您的应用程序,这可能会也可能不会。

如果您有更多线程或更大的堆栈分配对象,则必须调整线程堆栈大小和堆大小以获得正确的平衡。如果你错了,你的程序会因堆栈溢出而出现分段错误。因此,由于运行时行为更可预测,建议在堆上进行所有“大”分配。

20 年前,4 GB 地址空间对于堆、数据、代码和堆栈来说是“很大”的空间,而今天却没有那么多了。幸运的是,地址空间也变得更大。使用 64 位地址空间,即使您拥有超过 1 TB 的 DRAM,您也会在耗尽物理内存之前很久就用完 DRAM。 几个注意事项

    运行时通常会对堆栈大小施加任意限制,以捕获失控的递归错误。如果您使用的堆栈比运行时系统预期的多得多,您可能必须告诉它为您提供更大的堆栈。通常,默认尺寸足够大,并且通常不难更改。
  1. 操作系统/运行时在返回堆栈空间方面不如在获取堆栈空间方面那么好。当您的程序占用更多堆栈空间时,它将获得新页面。在线程退出之前,这些页面很可能不会被释放(至少过去是这样)。如果您使用堆栈空间,那没什么大不了的。如果很少使用并且线程运行很长时间,如果您有后备存储,内存将被交换到磁盘(对于具有旋转驱动器的台式机/笔记本电脑通常不是问题,但对于 iOS8 或 Android 64 可能不太好-位)。
  2. 如果您希望代码在与您计划堆栈分配的对象相比具有较大物理和虚拟内存空间的系统上运行,则堆栈分配可能没问题。在大多数情况下,收益将超过成本。


4
投票


4
投票

在现代系统上您可以轻松做到:

char array [123456];

但是你不会在通过递归重复调用自身的函数中这样做。

© www.soinside.com 2019 - 2024. All rights reserved.