C / C ++程序的最大堆栈大小

时间:2009-12-01 12:42:34

标签: c++ c stack

我想在100 X 100阵列上进行DFS。 (假设数组的元素代表图形节点)因此,假设最坏的情况,递归函数调用的深度可以达到10000,每个调用占用20个字节。那么可行的方法是否存在stackoverflow的可能性?

C / C ++中堆栈的最大大小是多少?

  

请为两者指定gcc   1)Windows上的cygwin   2)Unix

一般限制是什么?

7 个答案:

答案 0 :(得分:89)

在Visual Studio中,我认为默认的堆栈大小为1 MB,因此,当递归深度为10,000时,每个堆栈帧最多可以是~100个字节,这对于DFS算法应该足够了。

包括Visual Studio在内的大多数编译器都允许您指定堆栈大小。在某些(所有?)linux风格上,堆栈大小不是可执行文件的一部分,而是OS中的环境变量。然后,您可以使用ulimit -s检查堆栈大小,并将其设置为新值,例如ulimit -s 16384

这是一个link,其中包含gcc的默认堆栈大小。

没有递归的DFS:

std::stack<Node> dfs;
dfs.push(start);
do {
    Node top = dfs.top();
    if (top is what we are looking for) {
       break;
    }
    dfs.pop();
    for (outgoing nodes from top) {
        dfs.push(outgoing node);
    }
} while (!dfs.empty())

答案 1 :(得分:41)

线程的堆栈通常更小。 您可以在链接时更改默认值, 或者在运行时改变。 作为参考,一些默认值是:

  • glibc i386,x86_64 7.4 MB
  • Tru64 5.1 5.2 MB
  • Cygwin 1.8 MB
  • Solaris 7..10 1 MB
  • MacOS X 10.5 460 KB
  • AIX 5 98 KB
  • OpenBSD 4.0 64 KB
  • HP-UX 11 16 KB

答案 2 :(得分:14)

依赖于平台,依赖于工具链,依赖于ulimit,依赖于参数....它根本没有指定,并且有许多静态和动态属性可以影响它。

答案 3 :(得分:4)

是的,存在堆栈溢出的可能性。 C和C ++标准没有规定像堆栈深度这样的东西,这些通常是一个环境问题。

大多数体面的开发环境和/或操作系统都可以让您在链接或加载时定制进程的堆栈大小。

您应该指定您正在使用的操作系统和开发环境,以获得更有针对性的帮助。

例如,在Ubuntu Karmic Koala下,gcc的默认值是2M保留并且4K已提交,但是当您链接程序时可以更改它。使用--stack的{​​{1}}选项即可。

答案 4 :(得分:3)

我刚刚在工作中耗尽了堆栈,它是一个数据库而且它正在运行一些线程,基本上以前的开发人员已经在堆栈上抛出了一个大数组,而且堆栈仍然很低。该软件使用Microsoft Visual Studio 2015进行编译。

即使线程已经耗尽堆栈,它仍然无声地失败并继续运行,它只是在访问堆栈上的数据内容时堆栈溢出。

我能给出的最好建议是不在堆栈上声明数组 - 特别是在复杂的应用程序中,特别是在线程中,而是使用堆。这就是它的用途;)

另外请记住,在声明堆栈时可能不会立即失败,但仅限于访问。我的猜测是编译器在windows下“乐观地”声明了堆栈,即它会假设堆栈已经被声明并且有足够的大小直到它使用它然后发现堆栈不在那里。

不同的操作系统可能具有不同的堆栈声明策略。如果您知道这些政策是什么,请发表评论。

答案 5 :(得分:2)

我不确定你在矩形阵列上进行深度优先搜索是什么意思,但我猜你知道你在做什么。

如果堆栈限制有问题,您应该能够将递归解决方案转换为将中间值推送到从堆中分配的堆栈的迭代解决方案。

答案 6 :(得分:1)

(添加2020年9月26日)

2009年10月24日,as @pixelbeat first pointed out hereBruno Haible empirically discovered the following default thread stack sizes用于多个系统。他说在多线程程序中,“默认线程堆栈大小为:”

- glibc i386, x86_64    7.4 MB
- Tru64 5.1             5.2 MB
- Cygwin                1.8 MB
- Solaris 7..10           1 MB
- MacOS X 10.5          460 KB
- AIX 5                  98 KB
- OpenBSD 4.0            64 KB
- HP-UX 11               16 KB

请注意,以上单位均为MB和KB(以1000为基数),而不是MiB和KiB(以1024为基数)。我已经通过验证7.4 MB的情况向我证明了这一点。

他还说:

32 KB超出了您可以在多线程程序中安全分配到堆栈上的空间

他说:

sigaltstack的默认堆栈大小SIGSTKSZ为

  • 在某些平台上只有16 KB:IRIX,OSF / 1,Haiku。
  • 在某些平台上只有8 KB:glibc,NetBSD,OpenBSD,HP-UX,Solaris。
  • 在某些平台上只有4 KB:AIX。

布鲁诺

他编写了以下简单的Linux C程序,以凭经验确定上述值。您可以在今天的系统上运行它以快速查看最大线程堆栈大小,也可以在GDBOnline上在线运行它:https://onlinegdb.com/rkO9JnaHD。它只是创建一个新线程,以便检查线程堆栈大小,而不是程序堆栈大小,如果它们不同,则该线程重复分配128个字节的内存,向其写入0,然后打印出已分配的字节数,直到程序因Segmentation fault (core dumped)错误而崩溃。最后输出的值是系统允许的估计最大线程堆栈大小。

这是Bruno Haible的程序,copied directly from the GNU mailing list here

// By Bruno Haible
// 24 Oct. 2009
// Source: https://lists.gnu.org/archive/html/bug-coreutils/2009-10/msg00262.html

// =============== Program for determining the default thread stack size =========

#include <alloca.h>
#include <pthread.h>
#include <stdio.h>
void* threadfunc (void*p) {
  int n = 0;
  for (;;) {
    printf("Allocated %d bytes\n", n);
    fflush(stdout);
    n += 128;
    *((volatile char *) alloca(128)) = 0;
  }
}

int main()
{
  pthread_t thread;
  pthread_create(&thread, NULL, threadfunc, NULL);
  for (;;) {}
}

当我使用上面的链接在GDBOnline上运行它时,无论是C语言还是C ++ 17程序,每次运行都得到完全相同的结果。运行大约需要10秒钟。这是输出的最后几行:

Allocated 7449856 bytes
Allocated 7449984 bytes
Allocated 7450112 bytes
Allocated 7450240 bytes
Allocated 7450368 bytes
Allocated 7450496 bytes
Allocated 7450624 bytes
Allocated 7450752 bytes
Allocated 7450880 bytes
Segmentation fault (core dumped)

因此,此系统的线程堆栈大小为〜7.45 MB,如上文Bruno所述(7.4 MB)。

我已经对该程序进行了一些更改,主要是为了清楚起见,还为了提高效率,还做了一些学习。

我的更改摘要:

  1. [学习]我传入BYTES_TO_ALLOCATE_EACH_LOOP作为threadfunc()的参数,只是为了练习传入和在C语言中使用通用void*参数。

  2. [效率]我让主线程进入睡眠状态,而不是浪费时间地旋转。

  3. [声明],我添加了更详细的变量名,例如BYTES_TO_ALLOCATE_EACH_LOOPbytes_allocated

  4. [clarity]我更改了此内容:

     *((volatile char *) alloca(128)) = 0;
    

    对此:

     volatile uint8_t * byte_buff = 
             (volatile uint8_t *)alloca(BYTES_TO_ALLOCATE_EACH_LOOP);
     byte_buff[0] = 0;
    

这是我修改后的测试程序,它与Bruno的程序完全一样,甚至具有相同的结果:

您可以run it online heredownload it from my repo here。如果您选择从我的仓库中本地运行它,这是我用于测试的构建和运行命令:

  1. 以C程序构建并运行它:

     mkdir -p bin && \
     gcc -Wall -Werror -g3 -O3 -std=c11 -pthread -o bin/tmp \
     onlinegdb--empirically_determine_max_thread_stack_size_GS_version.c && \
     time bin/tmp
    
  2. 以C ++程序的形式构建并运行它:

     mkdir -p bin && \
     g++ -Wall -Werror -g3 -O3 -std=c++17 -pthread -o bin/tmp \
     onlinegdb--empirically_determine_max_thread_stack_size_GS_version.c && \
     time bin/tmp
    

在线程堆栈大小约为7.4 MB的快速计算机上本地运行需要不到0.5秒的时间。

这是程序:

// =============== Program for determining the default thread stack size =========

// Modified by Gabriel Staples, 26 Sept. 2020

// Originally by Bruno Haible
// 24 Oct. 2009
// Source: https://lists.gnu.org/archive/html/bug-coreutils/2009-10/msg00262.html

#include <alloca.h>
#include <pthread.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <unistd.h> // sleep

/// Thread function to repeatedly allocate memory within a thread, printing
/// the total memory allocated each time, until the program crashes. The last
/// value printed before the crash indicates how big a thread's stack size is.
void* threadfunc(void* bytes_to_allocate_each_loop)
{
    const uint32_t BYTES_TO_ALLOCATE_EACH_LOOP =
            *(uint32_t*)bytes_to_allocate_each_loop;

    uint32_t bytes_allocated = 0;
    while (true)
    {
        printf("bytes_allocated = %u\n", bytes_allocated);
        fflush(stdout);
        // NB: it appears that you don't necessarily need `volatile` here,
        // but you DO definitely need to actually use (ex: write to) the
        // memory allocated by `alloca()`, as we do below, or else the
        // `alloca()` call does seem to get optimized out on some systems,
        // making this whole program just run infinitely forever without
        // ever hitting the expected segmentation fault.
        volatile uint8_t * byte_buff =
                (volatile uint8_t *)alloca(BYTES_TO_ALLOCATE_EACH_LOOP);
        byte_buff[0] = 0;
        bytes_allocated += BYTES_TO_ALLOCATE_EACH_LOOP;
    }
}

int main()
{
    const uint32_t BYTES_TO_ALLOCATE_EACH_LOOP = 128;

    pthread_t thread;
    pthread_create(&thread, NULL, threadfunc,
                   (void*)(&BYTES_TO_ALLOCATE_EACH_LOOP));
    while (true)
    {
        const unsigned int SLEEP_SEC = 10000;
        sleep(SLEEP_SEC);
    }

    return 0;
}

示例输出(与Bruno Haible的原始程序相同的结果):

bytes_allocated = 7450240                                                                                                                                                        
bytes_allocated = 7450368                                                                                                                                                        
bytes_allocated = 7450496                                                                                                                                                        
bytes_allocated = 7450624                                                                                                                                                        
bytes_allocated = 7450752                                                                                                                                                        
bytes_allocated = 7450880                                                                                                                                                        
Segmentation fault (core dumped)