観察
glibc が使用するような典型的なアロケーターを想定すると、いくつかの観察事項があります。
- メモリが実際に使用されるかどうかにかかわらず、領域は仮想メモリ内で連続して予約する必要があります。
- 最大の空き連続領域は、既存のメモリ領域のメモリ使用量と、これらの領域の可用性に依存します
malloc
。
- マッピングの方法は、アーキテクチャと OS によって異なります。さらに、メモリ領域を取得するための基になるシステム コールは、これらのプラクティスの影響を受けます (ページを取得するために を
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/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 を使用して割り当て可能な最大スペースが次のようになることを示しています。
- ユーザー空間領域 (例では 3GB)
- ヒープの先頭へのオフセットを差し引いたもの (プログラム コードとデータ)
- メイン スレッド スタック用に予約されたスペースを減らす
- マップされたすべての共有ライブラリが占有するスペースが少ない
- 最後に、ヒープで使用可能な領域内で基礎となるシステム コールによって検出できる最大の連続領域 (他のマッピングによって断片化される可能性があります)。
ノート
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
の処理が異なる場合があるため、結果がかなり異なる可能性があります。