8

以下静态分配给出分段错误

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));
    }
}

ulimit 在 linux 中设置为无限制。

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

4

4 回答 4

5

当您说 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。

于 2013-03-16T12:39:38.323 回答
4

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

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

于 2013-03-16T11:49:06.273 回答
2

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

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

ulimit -s

在 Mac OS X 上,硬限制为 64MB,请参阅如何在 Mac OS X 上使用 ulimit 或每个进程更改堆栈大小以获取 C 或 Ruby 程序?

您可以在程序运行时修改堆栈限制,请参阅使用 GNU 编译器编译期间在 Linux 中更改 C++ 应用程序的堆栈大小

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

#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;
}
于 2013-03-16T16:34:03.317 回答
-3

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

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

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

Linux: 如何增加 gcc 可执行堆栈大小? 在使用 GNU 编译器编译期间更改 Linux 中 C++ 应用程序的堆栈大小

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

于 2013-03-16T11:53:41.303 回答