你什么时候担心堆栈大小?

时间:2009-12-16 16:34:45

标签: c++

当您使用允许对非常大的对象使用自动分配的语言进行编程时,您何时以及如何担心堆栈大小?关于堆栈大小的推理是否有任何经验法则?

19 个答案:

答案 0 :(得分:13)

  

当您使用允许您对非常大的对象使用自动分配的语言进行编程时......

如果我想分配一个非常大的对象,那么我可能会在堆上分配一个非常大的对象而不是堆栈,而是包含在auto_ptr中(在这种情况下,当它超出范围时,它将被释放,就像堆栈驻留对象一样,但不必担心堆栈大小。)

  

...您何时以及如何担心堆栈大小?

我保守地使用堆栈的习惯(例如,在堆上分配大于512字节的任何对象),我知道堆栈有多大(例如默认大约一兆字节),因此知道我不用担心。

  

关于堆栈大小的推理是否有任何经验法则?

  • 非常大的物体会炸掉堆叠
  • 非常深的递归会使堆栈崩溃
  • 如果有许多线程,并且如果您在有限内存的嵌入式设备上运行,则默认堆栈大小可能太大(占用总内存太多),在这种情况下,您可能希望使用O / S API或链接器选项,以减少每个线程的堆栈大小。

答案 1 :(得分:11)

你在微控制器上关心它,你经常必须明确地指定堆栈空间(或者在RAM用于静态分配+任何RAM程序空间之后你得到剩余的东西)。

答案 2 :(得分:7)

时,您开始担心堆栈大小
  • 你团队中的某个人狡猾地发明了一个连续不断的递归功能......
  • 你创建一个线程工厂,突然需要你曾经需要的十倍的堆栈(每个线程需要一个堆栈=>你拥有的线程越多,给定堆栈大小剩余的可用空间就越少)

答案 3 :(得分:6)

如果您正在编写一个小小的嵌入式平台,那么您一直担心它,但您也确切地知道它有多大,并且可能有一些有用的工具可用于查找堆栈的高水位标记

如果你不是,那么在程序崩溃之前不要担心:) 除非你要分配严重的大对象(数十KB),否则它永远不会成为问题。

但是,请注意,根据定义,堆栈上的对象是临时的。经常构造(并且可能破坏)大对象可能会导致性能问题 - 所以如果你有一个大对象,它可能应该是持久的而且是基于堆的,而不是堆栈大小。

答案 4 :(得分:4)

我从不担心。如果堆栈溢出,我很快就会知道它。此外,在C ++中,实际上很难在堆栈上创建非常大的对象。关于这样做的唯一方法是:

struct S {
   char big[1000000];
};

但是使用std :: string或std :: vector会使问题消失。

答案 5 :(得分:3)

你不应该首先避免使用堆栈来分配大型对象吗?使用堆,没有?

答案 6 :(得分:3)

我的经历: 当你使用递归函数时,请注意堆栈大小!!

答案 7 :(得分:3)

  

您何时担心堆栈大小?

从不。

如果你有堆栈大小问题,这意味着你做了别的错误,应该修复它,而不是担心堆栈大小。
对于实例:

  • 在堆栈上分配不合理的大型结构 - 不要这样做。在堆上分配。
  • 有一个可笑的长递归。我的意思是按照绘制图像的顺序并使用递归迭代像素。 - 找到更好的方法。

答案 8 :(得分:2)

当调用堆栈非常深并且每个函数分配变量(在堆栈上)时,我担心嵌入式系统上的堆栈大小。通常,当系统因堆栈中的变量发生变化而意外崩溃(堆栈溢出)时,会发生恐慌。

答案 9 :(得分:2)

您通常无法在堆栈上拥有大型对象。它们几乎总是在内部使用堆,因此即使它们在堆栈中,它们的数据成员也不是。即使是具有大量数据成员的对象,堆栈上通常也会有64个字节以下,其余的都在堆上。如果你有很多线程和大量的递归,那么堆栈通常只会成为一个问题。

答案 10 :(得分:2)

在Symbian上玩了很多这个游戏:什么时候使用TBuf(堆栈上有存储的字符串),什么时候使用HBufC(它在堆上分配字符串存储,比如std :: string,所以你必须应对离开,你的功能需要一种失败的方法。)

当时(可能还是,我不确定),Symbian线程默认有4k的堆栈。要操作文件名,您需要指望最多使用512个字节(256个字符)。

你可以想象,收到的智慧“永远不会把文件名放在堆栈上”。但事实上,事实证明,你可以比你想象的更频繁地逃避它。当我们开始运行真正的程序(TM),比如游戏时,我们发现我们需要的方式比默认的堆栈大小更多,并且它不是由于文件名或其他特定的大型对象,而是由于其复杂性游戏代码。

如果使用堆栈会使您的代码更简单,并且只要您正确测试,并且只要您没有完全超越(没有多层次的文件处理函数 all < / em>在文件夹上放一个文件名),然后我会说尝试一下。特别是如果函数需要能够失败,无论你是使用堆栈还是堆。如果出错,您可以将堆栈大小加倍并在将来更加小心,或者为您的函数添加另一个失败案例。也不是世界末日。

答案 11 :(得分:2)

只有时间才是线程并且必须自己定义,当你进行递归时或者由于某种原因你正在分配给堆栈时。否则,编译器会确保您有足够的堆栈空间。

默认情况下,CreateThread仅为堆栈分配0x100000个字节。

答案 12 :(得分:1)

在决定是否在堆栈上分配对象与堆时,还需要考虑性能问题。堆栈上的内存分配非常快 - 它只涉及移动堆栈指针,而使用new / delete或malloc / free的动态分配/释放相当昂贵,尤其是在每个线程没有堆的多线程代码中。如果你有一个在紧密循环中调用的函数,你可能会错误地将更大的对象放在堆栈上,同时记住其他答案中提到的所有多线程警告,即使这意味着必须增加堆栈空间,大多数连接器都允许你这样做。

答案 13 :(得分:1)

一般来说,堆栈上的大量分配是不好的,原因有几个,其中最重要的原因是它们可能导致问题长时间保持隐藏状态。

问题在于检测堆栈溢出并不容易,大量分配可能会破坏大多数常用方法。

如果处理器没有内存管理或内存保护单元,则必须特别小心。但是使用某种MMU或MPU的事件,硬件可能无法检测到堆栈溢出。一个常见的方案是,如果大堆栈对象大于页面,则保留堆栈下方的页面以捕获溢出。可能只是堆叠的另一个线程坐在那里哎呀!你刚刚创造了一个非常讨厌,很难找到的bug。

无限递归通常很容易捕获,因为堆栈增长通常很小,会触发硬件保护。

答案 14 :(得分:1)

我没有。在编写正常的编程时,担心这些事情要么是过早的悲观化,要么是过早的优化。无论如何,要在现代计算机上搞砸是非常困难的。

我曾经写过一个CSV解析器,在玩弄时试图获得最佳性能,我在堆栈上分配数千个1K缓冲区。性能非常出色,但RAM从正常的30MB内存增加到大约1GB。这是由于CSV文件中的每个单元都有一个固定大小的1K缓冲区。

就像所有人都说的那样,除非你在做递归,否则你不必担心它。

答案 15 :(得分:1)

  1. 当您为PC编写的代码突然应该在手机上运行时
  2. 当您移植到移动电话上的代码突然应该在DSP上运行时
  3. (是的,这些都是现实生活中的混乱。)

答案 16 :(得分:0)

当您编写一个回调时,您会担心它,该回调将由您无法控制的运行时(例如,MS RPC运行时)生成的线程调用,其堆栈大小由该运行时决定。不知怎的like this

答案 17 :(得分:0)

在以下情况下,我遇到了堆栈空间不足的问题:

  • 一个功能意外地自行调用
  • 一个函数使用递归到深层次
  • 一个函数在堆栈上分配一个大对象,并且有一个堆。
  • 一个函数使用复杂的模板,编译器崩溃

提供我:

  • 在堆上分配大对象(例如使用“auto_ptr foo = new Foo”而不是“Foo foo”)
  • 明智地使用递归。

我通常没有任何问题,所以很遗憾不知道应该有什么好的默认值。

答案 18 :(得分:0)

在以下情况下开始担心堆栈大小:

  • 当你的程序崩溃时 - 通常这些错误在你第一次看到它们时往往会很奇怪:)
  • 您运行的算法使用递归将用户输入作为其参数之一(您不知道算法可以使用多少堆栈)
  • 您正在嵌入式平台(或每个资源都很重要的平台)上运行。通常在这些平台上,在创建流程之前分配堆栈 - 因此必须对堆栈需求做出很好的估计
  • 您正在堆栈上创建对象,具体取决于用户输入可修改的某些参数(请参阅下面的示例)
  • 当在线程/进程/任务中执行的代码非常大时,有很多函数调用深入到堆栈中并生成一个巨大的调用堆栈。这通常发生在结合了大量触发器和事件处理的大框架中(GUI框架;例如:receive_click-&gt; find_clicked_window-&gt; send_msg_to_window-&GT; process_message-&GT; process_click-&GT; is_inside_region-&GT; trigger_drawing-&GT; write_to_file-&GT; ......)简而言之,在复杂代码或未知/二进制第三方模块的情况下,您应该担心调用堆栈。

可修改输入参数的示例:

in my_func(size_t input_param)
{
  char buffer[input_param];
  // or any other initialization of a big object on the stack
  ....
}

建议:

  • 你应该用一些神奇的数字标记堆栈(如果你分配它)并检查是否会修改那些神奇的数字(在这种情况下,堆栈对于任务/线程/进程来说是不够的,应该可以增加)