【问题标题】:Why do very large stack allocations fail despite unlimited ulimit?尽管 ulimit 无限,为什么非常大的堆栈分配会失败?
【发布时间】:2013-03-16 10:59:17
【问题描述】:

以下静态分配导致分段错误

double U[100][2048][2048];

但是下面的动态分配没问题

double ***U = (double ***)malloc(100 * sizeof(double **));

for(i=0;i<100;i++)
{
    U[i] = (double **)malloc(2048 * sizeof(double *));
    for(j=0;j<2048;j++)
    {
        U[i][j] = (double *)malloc(2048*sizeof(double));
    }
}

在linux中ulimit设置为unlimited。

谁能给我一些关于发生了什么的提示?

【问题讨论】:

  • 您确定堆栈大小是无限的吗?还有,你的内存够吗? malloced 内存可能并非全部由物理内存支持。
  • 你确定malloc 没有返回空指针吗?
  • 是的...我在声明后的代码中使用数组。每个位置都可以访问
  • 动态分配的多维数组不需要在内存中连续,静态分配的多维数组可以。
  • malloc 可以为 100*2048*2048 双倍分配空间还是只返回 NULL?该问题可能与内存(地址空间)碎片更相关,而与静态与动态的相关性较小。

标签: c linux memory-management


【解决方案1】:

当您说 ulimit 设置为无限制时,您使用的是-s 选项吗?否则这不会更改堆栈限制,只会更改文件大小限制。

无论如何,似乎都有堆栈限制。我可以分配:

double *u = malloc(200*2048*2048*(sizeof(double)));  // 6gb contiguous memory

并运行我得到的二进制文件:

VmData:  6553660 kB

但是,如果我在堆栈上分配,它是:

double u[200][2048][2048];

VmStk:   2359308 kB

这显然是不正确的(建议溢出)。使用原始分配,两者给出相同的结果:

Array:  VmStk:   3276820 kB
malloc: VmData:  3276860 kB

但是,运行堆栈版本时,无论数组的大小如何,我都无法生成段错误——即使它超过了系统上实际的总内存,如果设置了-s unlimited

编辑:

我在循环中使用malloc 进行了测试,直到失败:

VmData: 137435723384 kB  // my system doesn't quite have 131068gb RAM

不过,堆栈使用量永远不会超过 4gb。

【讨论】:

    【解决方案2】:

    假设您的机器实际上有足够的空闲内存来分配 3.125 GiB 的数据,区别很可能在于静态分配需要所有这些内存是连续的(它实际上是一个 3 维数组),而动态分配只需要大约 2048*8 = 16 KiB 的连续块(​​它是指向非常小的实际数组的指针数组的指针数组)。

    您的操作系统也有可能在堆内存用完时使用交换文件,而不是堆栈内存。

    【讨论】:

    • 分配的空间也需要连续。这是根据定义
    • @Mellowcandle,是的,但是使用动态分配,必须有 204800 个连续的 2048 字节区域(不计算更高的指针级别,因为它们比较小),而使用静态分配 ALL内存必须是连续的。
    • @Mellowcandle 是的,但是每次调用 malloc 只分配少量内存。
    • 栈内存只需要在virtual内存地址空间中是连续的,内核可以通过重新组织它认为合适的物理内存来创建虚拟内存。但是,如果内存高度碎片化,重新组织需要大量“页表”(大多数 CPU 的实际虚拟内存实现),系统可能很容易无法同时分配 3.125 GiB 的数据和所需的页表。对于高度碎片化的内存,仅页表就可能占用千兆字节的 RAM。如果系统有 32 位内核,即使系统有空闲 RAM,虚拟地址空间也可能会填满!
    【解决方案3】:

    这里有一个关于 Linux 内存管理——特别是堆栈——的很好的讨论:9.7 Stack overflow,值得一读。

    您可以使用此命令找出您当前的堆栈soft limit 是什么

    ulimit -s
    

    在 Mac OS X 上,硬限制为 64MB,请参阅How to change the stack size using ulimit or per process on Mac OS X for a C or Ruby program?

    您可以在程序运行时修改堆栈限制,请参阅Change stack size for a C++ application in Linux during compilation with GNU compiler

    我将您的代码与那里的示例结合在一起,这是一个工作程序

    #include <stdio.h>
    #include <sys/resource.h>
    
    unsigned myrand() {
        static unsigned x = 1;
        return (x = x * 1664525 + 1013904223);
    }
    
    void increase_stack( rlim_t stack_size )
    {
        rlim_t MIN_STACK = 1024 * 1024;
    
        stack_size += MIN_STACK;
    
        struct rlimit rl;
        int result;
    
        result = getrlimit(RLIMIT_STACK, &rl);
        if (result == 0)
        {
           if (rl.rlim_cur < stack_size)
           {
               rl.rlim_cur = stack_size;
               result = setrlimit(RLIMIT_STACK, &rl);
               if (result != 0)
               {
                  fprintf(stderr, "setrlimit returned result = %d\n", result);
               }
           }
       }    
    }
    
    void my_func() {
       double U[100][2048][2048];
       int i,j,k;
       for(i=0;i<100;++i)
        for(j=0;j<2048;++j)
            for(k=0;k<2048;++k)
                U[i][j][k] = myrand();
       double sum = 0;
       int n;
       for(n=0;n<1000;++n)
           sum += U[myrand()%100][myrand()%2048][myrand()%2048];
       printf("sum=%g\n",sum);
    }
    
    int main() {
       increase_stack( sizeof(double) * 100 * 2048 * 2048 );
    
       my_func();
    
       return 0;
    }
    

    【讨论】:

    • 如果堆栈大小已经设置为无限制(如问题所述),这实际上会将其减少到 100*2048*2048*sizeof(double)。
    • 代码检查请求的限制是否小于现有限制,在这种情况下它不会减少它。因此,如果堆栈限制实际上是无限的,则此代码不会减少它。
    • 哦,是的,我没有看到那部分。此代码仅在用户未增加软堆栈限制时才有用。
    【解决方案4】:

    您正在达到堆栈的限制。默认情况下,在 Windows 上,堆栈为 1M,但如果有足够的内存,可以增长更多。

    在许多 *nix 系统上,默认堆栈大小为 512K。

    您正在尝试分配 2048 * 2048 * 100 * 8 个字节,超过 2^25(堆栈超过 2G)。如果您有大量可用的虚拟内存并且仍想在堆栈上分配它,请在链接应用程序时使用不同的堆栈限制。

    Linux: How to increase the gcc executable stack size? Change stack size for a C++ application in Linux during compilation with GNU compiler

    窗户: http://msdn.microsoft.com/en-us/library/tdkhxaks%28v=vs.110%29.aspx

    【讨论】:

    • -1 :问题明确指出ulimit 堆栈大小是无限的。
    • @us2012 那么在可执行程序集中指定的堆栈大小呢?我的意思是链接器标志?
    猜你喜欢
    • 2015-12-31
    • 1970-01-01
    • 2011-05-28
    • 2022-07-21
    • 2011-10-29
    • 2012-04-09
    • 2019-07-05
    • 2011-02-11
    • 1970-01-01
    相关资源
    最近更新 更多