glibc ヒープ領域の縮退
概要
mysqlコマンドで大量のレコードを取得中に、psコマンドでメモリの使用量を見ていたらクエリ実行中はVSZ(仮想アドレス空間のサイズ)がじわじわと増え続けていたが、クエリの実行が完了するとVSZのサイズが小さくなった。
(だいぶ前の)libcではmalloc()してヒープ領域が拡張されたら、free()してもバッファがFreeListに返されるだけで、ヒープ領域はそのままであったため、VSZのサイズが小さくなることはなかったと思ったが、最近ではちゃんとヒープ領域を縮退してくれるらしい。少し調査。
glibcのソース
glibc-2.4.tar.gzのソースを確認してみる。詳しくは追っていないが、以下のような流れで縮退処理に行っている。grow_heap()にはマイナスの引数を渡して縮退させている。
free() => public_fREe() => _int_free() => heap_trim() => grow_heap()
実際の挙動
実際にmalloc()&free()してプロセスのアドレス空間のサイズの動きを見てみた。以下のソースは1kbのメモリを20k個確保(合計20M)して解放する。(使用したライブラリはglibc 2.3.5)
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#define BUFFCNT (20*1024)
#define BUFFSIZE (1024)
void *buffp[BUFFCNT];
main()
{
void *p;
int i;
for (i = 0 ; i < BUFFCNT ; i++) {
if (!(buffp[i] = malloc(BUFFSIZE)))
return -1;
}
sleep(5);
printf("free\n");
for (i = 0 ; i < BUFFCNT; i++)
free(buffp[i]);
while (1) {
sleep(1);
}
}
このプログラムを実行してps -auxでVSZの変化を見てみると、malloc後はVSZは20M+となり、freeしたらVSZが小さくなって、ヒープ領域がちゃんと縮退されているのがわかる。USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND malloc後 tomita 5759 0.6 2.1 22216 21048 pts/9 S+ 14:35 0:00 ./a.out free後 tomita 5759 0.6 0.0 1628 572 pts/9 S+ 14:35 0:00 ./a.outまた、以下のようにして末尾のバッファをfreeしないと、ヒープ領域の末尾にバッファが残るので縮退はされない。
for (i = 0 ; i < BUFFCNT - 1; i++)
free(buffp[i]);
