Насколько большим может быть malloc в C?
У меня есть malloc в C, который 26901^2*sizeof(double)
Это заставило меня задуматься о том, какая здесь самая большая ценность?
Кроме того, возникнут ли у меня проблемы с определением макроса для доступа к этому двумерному массиву?
#define DN(i,j) ((int)i * ny + (int)j)
Потому что это, кажется, не работает для меня - или я по крайней мере не уверен, что это так. Я не могу понять, как заставить макет totalview нырнуть в макрос, чтобы сказать мне, на что на самом деле смотрит A[DN(indx,jndx)].
6 ответов
наблюдения
Предполагая типичный распределитель, такой как тот, который использует glibc, есть некоторые наблюдения:
- Независимо от того, используется ли память на самом деле, область должна быть зарезервирована непрерывно в виртуальной памяти.
- Самые большие свободные смежные области зависят от использования памяти существующих областей памяти и доступности этих областей для
malloc
, - Методы сопоставления зависят от архитектуры и ОС. Кроме того, эти методы затрагивают основные системные вызовы для получения областей памяти (например,
malloc
призывая кmmap
приобрести страницы).
эксперимент
Вот простая программа для выделения максимально возможного блока (скомпилировать с gcc largest_malloc_size.c -Wall -O2
:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
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;
}
}
Запуск вышеуказанной программы (./a.out
) на моем Linux stanley 2.6.32-24-generic-pae #39-Ubuntu SMP Wed Jul 28 07:39:26 UTC 2010 i686 GNU/Linux
Машина получает такой результат:
<snip>
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
:
<snip>
0804a000-0804b000 rw-p 00001000 08:07 3413394 /home/matt/anacrolix/public/stackru/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
<snip>
bfc07000-bfc1c000 rw-p 00000000 00:00 0 [stack]
Заключение
Похоже, что куча была расширена в области между данными программы и кодом и отображениями совместно используемой библиотеки, которые плотно прилегают к границе пространства памяти пользователя / ядра (очевидно, 3G/1G в этой системе).
Этот результат предполагает, что максимальное выделяемое пространство с использованием malloc примерно равно:
- Область пользовательского пространства (3 ГБ в примере)
- За вычетом смещения к началу кучи (программный код и данные)
- Меньше места, зарезервированного для стека основного потока
- Меньше места, занимаемого всеми отображаемыми в общих библиотеках
- Наконец, самая большая непрерывная область, которая может быть найдена базовым системным вызовом в пределах области, доступной для кучи (которая может быть фрагментирована другими отображениями)
Заметки
Что касается реализаций 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
s, так что результаты могут быть значительно другими.
Это зависит от вашей реализации malloc!
Согласно Википедии, "начиная с выпуска v2.3 библиотека GNU C (glibc) использует модифицированный ptmalloc2, который сам основан на dlmalloc v2.7.0". dlmalloc ссылается на реализацию malloc Дуга Ли. Важно отметить, что в этой реализации большие mallocs выполняются с помощью функциональных возможностей отображаемого в память файла операционной системы, поэтому эти блоки могут быть действительно достаточно большими без многих проблем, связанных с поиском смежного блока.
Ответ на вопрос о 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));
Это заставило меня задуматься о том, какая здесь самая большая ценность?
26'901 ^ 2 = 723'663'801. Если ваш дубль равен 8 байтам, то он меньше 8 ГБ. Я не вижу абсолютно никаких проблем с распределением такого объема памяти, а мои приложения обычно выделяют (на 64-битных системах) гораздо больше. (Самое большое потребление памяти, которое я когда-либо видел, составляло 420 ГБ (в системе Solaris 10 numa с 640 ГБ ОЗУ) с самым большим непрерывным блоком ~24 ГБ.)
Трудно определить наибольшее значение, поскольку оно зависит от платформы: подобно 32-битным системам, оно зависит от разделения пространства пользователя / ядра. В настоящее время все обстоит так, я думаю, что сначала нужно достичь предела фактической физической ОЗУ, прежде чем достигнуть предела того, что может выделить libc. (А ядру все равно, оно просто часто расширяет виртуальную память, даже не задумываясь о том, достаточно ли ОЗУ для ее закрепления.)
Параметр size в вызове malloc имеет тип size_t, который зависит от реализации. Смотрите этот вопрос для более.
Самый большой блок памяти, который вы можете спросить malloc()
это самый большой size_t
ценность - это SIZE_MAX
от <limits.h>
, Самая большая сумма, которую вы можете успешно запросить, очевидно, зависит от операционной системы и конфигурации отдельного компьютера.
Ваш макрос небезопасен. Он выполняет расчет индекса с int
переменная, которая должна иметь диапазон до 32767. Любое значение выше этого может вызвать переполнение со знаком, что приведет к неопределенному поведению. Вы, вероятно, лучше всего делать вычисления как size_t
, поскольку этот тип должен содержать любой допустимый индекс массива:
#define DN(i, j) ((size_t)(i) * ny + (size_t)(j))
(Хотя учтите, что если вы поставите отрицательные значения для i
или же j
, вы получите индекс далеко за пределами).