Наблюдения
Предполагая, что для типичного распределителя, такого как тот, который использует 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
Это выделение ровно 2800 МБ. Наблюдение за соответствующим отображением из /proc/[number]/maps
:
<snip>
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
<snip>
bfc07000-bfc1c000 rw-p 00000000 00:00 0 [stack]
Заключение
Похоже, что куча была расширена в области между данными программы и кодом и отображениями совместно используемой библиотеки, которые плотно прилегают к границе пространства памяти пользователя / ядра (очевидно, 3G / 1G в этой системе).
Этот результат свидетельствует о том, что максимальное выделяемое пространство с использованием malloc примерно равно:
- Область пользовательского пространства (в данном примере 3 ГБ)
- Меньше смещенияк началу heap (программный код и данные)
- Меньше места, зарезервированного для стека основного потока
- Меньше места, занимаемого всеми отображенными в общих библиотеках
- Наконец, наибольшее смежноерегион, который может быть найден базовым системным вызовом в пределах области, доступной для кучи (которая может быть фрагментирована другими отображениями)
Примечания
Что касается реализаций 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.
Afterword
Этот тест был выполнен на ядре x86.Я ожидаю аналогичных результатов от ядра x86_64, хотя и с гораздо большими областями памяти.Другие операционные системы могут различаться по расположению отображений и обработке больших malloc
с, поэтому результаты могут значительно отличаться.