一个malloc在C中有多大?

我在C中有一个malloc,它是26901 ^ 2 * sizeof(double)

这让我想到这里最大的价值是什么?

另外,定义宏来访问这个2D数组有什么问题吗?

#define DN(i,j) ((int)i * ny + (int)j) 

因为这对我来说似乎不起作用 – 或者我至少不确定是这样。 我无法弄清楚如何在宏上进行全视图潜水,告诉我A [DN(indx,jndx)]实际上是在看什么。

意见

假设一个典型的分配器,例如一个glibc使用的,有一些观察结果:

  1. 无论是否实际使用了内存,该区域必须在虚拟内存中连续保留。
  2. 最大的自由连续区域取决于现有内存区域的内存使用情况以及malloc的这些区域的可用性。
  3. 映射实践取决于体系结构和操作系统。 此外,获取内存区域的底层系统调用受这些实践的影响(例如malloc调用mmap来获取页面)。

实验

这是一个分配最大可能块的简单程序 (使用gcc largest_malloc_size.c -Wall -O2编译:

 #include  #include  #include  static void *malloc_wrap(size_t size) { void *p = malloc(size); if (p) { printf("Allocated %zu bytes from %p to %p\n", size, p, p + size); } else { printf("Failed to allocated %zu bytes\n", size); } return p; } int main() { size_t step = 0x1000000; size_t size = step; size_t best = 0; while (step > 0) { void *p = malloc_wrap(size); if (p) { free(p); best = size; } else { step /= 0x10; } size += step; } void *p = malloc_wrap(best); if (p) { pause(); return 0; } else { return 1; } } 

在我的Linux stanley 2.6.32-24-generic-pae #39-Ubuntu SMP Wed Jul 28 07:39:26 UTC 2010 i686 GNU/Linux上运行上述程序( ./a.out Linux stanley 2.6.32-24-generic-pae #39-Ubuntu SMP Wed Jul 28 07:39:26 UTC 2010 i686 GNU/Linux机器获得此结果:

  Allocated 2919235584 bytes from 0x9763008 to 0xb7763008 Allocated 2936012800 bytes from 0x8763008 to 0xb7763008 Failed to allocated 2952790016 bytes Failed to allocated 2953838592 bytes Failed to allocated 2953904128 bytes Failed to allocated 2953908224 bytes Allocated 2936012800 bytes from 0x85ff008 to 0xb75ff008 

这是一个完全2800MiB的分配。 观察/proc/[number]/maps的相关映射:

  0804a000-0804b000 rw-p 00001000 08:07 3413394 /home/matt/anacrolix/public/stackoverflow/a.out 085ff000-b7600000 rw-p 00000000 00:00 0 [heap] b7600000-b7621000 rw-p 00000000 00:00 0 b7621000-b7700000 ---p 00000000 00:00 0 b7764000-b7765000 rw-p 00000000 00:00 0 b7765000-b78b8000 r-xp 00000000 08:08 916041 /lib/tls/i686/cmov/libc-2.11.1.so  bfc07000-bfc1c000 rw-p 00000000 00:00 0 [stack] 

结论

看来堆已经扩展到程序数据和代码之间的区域,以及共享库映射,它们紧贴用户/内核内存空间边界 (显然是这个系统上的3G / 1G)。

这个结果表明使用malloc的最大可分配空间大致等于:

  1. 用户空间区域(示例中为3GB)
  2. 减去堆开始的偏移量(程序代码和数据)
  3. 减少为主线程堆栈保留的空间
  4. 减少共享库中所有映射所占用的空间
  5. 最后,底层系统可以在堆可用的区域内找到最大的连续区域(可能被其他映射分段)

笔记

关于glibc和Linux实现,以下手册片段引起了极大的兴趣:

malloc

  Normally, malloc() allocates memory from the heap, and adjusts the size of the heap as required, using sbrk(2). When allocating blocks of mem‐ ory larger than MMAP_THRESHOLD bytes, the glibc malloc() implementation allocates the memory as a private anonymous mapping using mmap(2). MMAP_THRESHOLD is 128 kB by default, but is adjustable using mal‐ lopt(3). 

mmap

  MAP_ANONYMOUS The mapping is not backed by any file; its contents are initial‐ ized to zero. 

后记

此测试在x86内核上完成。 我期望x86_64内核得到类似的结果,尽管返回的内存区域要大得多。 其他操作系统的映射位置和大型malloc的处理可能会有所不同,因此结果可能会有很大不同。

这取决于你的malloc实现!

根据维基百科的说法,“自从v2.3发布以来,GNU C库(glibc)使用了一个修改过的ptmalloc2,它本身就是基于dlmalloc v2.7.0。” dlmalloc指的是Doug Lea的malloc实现。 在这个实现中需要注意的重要事项是,大型mallocs是通过操作系统的内存映射文件function完成的,因此这些块可能非常大,确实没有找到连续块的许多问题。

malloc问题得到解答(取决于操作系统,你没有指定),所以关于那个定义:

 #define DN(i,j) ((int)i * ny + (int)j) 

是不是很安全,因为有人可能会扩展到DN(a+b,c)

 ((int)a+b * ny + (int)c) 

这可能不是你想要的。 所以在那里放了很多括号:

 #define DN(i,j) ((int)(i) * ny + (int)(j)) 

看看DN(indx,jndx)指向的是什么,只需printf("%d\n",DN(indx,jndx));

调用malloc时的size参数的类型为size_t,因实现而异。 有关更多信息,请参阅此问

这让我想到这里最大的价值是什么?

26’901 ^ 2 = 723’663’801。 如果你的双倍是8字节,那么它小于8GB。 我认为分配大量内存完全没问题,我的应用程序经常分配(在64位系统上)更多。 (我见过的最大内存消耗是420GB(在带有640GB RAM的Solaris 10 numa系统上),最大连续块大约为24GB。)

最大的值很难识别,因为它取决于平台:类似于32位系统,它取决于用户空间/内核空间分割。 就目前的情况而言,我认为在达到libc可以分配的限制之前,首先会达到实际物理RAM的极限。 (并且内核并不关心,它只是在不考虑是否有足够的RAM来固定它的情况下经常扩展虚拟内存。)

您可以 malloc() 询问的最大内存块是最大的size_t值 – 这是 SIZE_MAX 。 您可以成功请求的最大数量显然取决于操作系统和单个计算机的配置。

你的宏不安全。 它使用int变量执行索引计算,该变量只需要具有最大32767的范围。任何高于此值的值都可能导致签名溢出,从而导致未定义的行为。 您可能最好以size_t的forms进行计算,因为该类型必须能够保存任何有效的数组索引:

 #define DN(i, j) ((size_t)(i) * ny + (size_t)(j)) 

(虽然请注意,如果你为ij提供负值,你会得到一个远远超出界限的索引)。