Linuxでメモリ使用量が増えがちな際に見直したい環境変数

先日、とあるコードで何故か使用メモリが増え続けるという事象に遭遇したので、その時有効だった方法を紹介します。

尚、おそらくLinux環境でglibcを使っている場合のみ関係すると思われます。

当てはまりそうな状況

・Linux、glibc環境で動作している(Dockerで動かしている場合などはベースイメージに注意)
・沢山処理をしている
・数MB-数十MB程度のオブジェクトを大量に作ったり削除したりする

実証コード

これを実行した結果がこちら。

 1 ❯ python increase_memory_usage.py 
start rss: 111396 malloc(current, peak): (12935, 64753)
0 rss: 427204 malloc(current, peak): (5923339, 725231273)
1 rss: 748164 malloc(current, peak): (22551444, 987080742)
2 rss: 738456 malloc(current, peak): (22155239, 996618740)
3 rss: 755340 malloc(current, peak): (10166961, 1076745328)
4 rss: 867392 malloc(current, peak): (6820159, 1076745328)
5 rss: 1127052 malloc(current, peak): (24849178, 1334372931)
6 rss: 1056596 malloc(current, peak): (4758442, 1334372931)
7 rss: 1056884 malloc(current, peak): (27922945, 1334372931)
8 rss: 1342520 malloc(current, peak): (15232500, 1334372931)
9 rss: 1245364 malloc(current, peak): (29538109, 1334372931)
finish rss: 940620 malloc(current, peak): (29532134, 1334372931)

finish rss: 940620 malloc(current, peak): (29532166, 1334372931)


少々わかりづらいですが、rssはキロバイト単位のプロセスが使用しているメモリサイズ、mallocはPython側で管理しているメモリのバイト数です。

これはPythonでの例ですが、言語は関係ありません。確保したオブジェクトを開放して、GCによる回収も確認済みにも関わらずプロセスの使用メモリが減りません。何故でしょうか。

freeしたメモリは実はfreeされていない

これ、どうもglibcのdynamic mmap thresholdという仕組みに関係があるようです。mallopt(3)にはこう書いてありました。

Note: Nowadays, glibc uses a dynamic mmap threshold by
default. The initial value of the threshold is 128*1024, but
when blocks larger than the current threshold and less than or
equal to DEFAULT_MMAP_THRESHOLD_MAX are freed, the threshold
is adjusted upward to the size of the freed block. When
dynamic mmap thresholding is in effect, the threshold for
trimming the heap is also dynamically adjusted to be twice the
dynamic mmap threshold. Dynamic adjustment of the mmap
threshold is disabled if any of the M_TRIM_THRESHOLD,
M_TOP_PAD, M_MMAP_THRESHOLD, or M_MMAP_MAX parameters is set.

未だに正確には理解できていませんが、ようはM_MMAP_THRESHOLDとM_TRIM_THRESHOLDが大きくなってしまって、用済みのメモリがOSに返却されなくなる現象が起こることがあるみたいです。

これを防ぐには、環境変数MALLOC_TRIM_THRESHOLD_かMALLOC_MMAP_THRESHOLD_あたりをセットしてあげると良いようです。

 1 ❯ MALLOC_TRIM_THRESHOLD_=-1 python increase_memory_usage.py 
start rss: 111404 malloc(current, peak): (12935, 64753)
0 rss: 123316 malloc(current, peak): (13059740, 1119566205)
1 rss: 116828 malloc(current, peak): (6250540, 1153104357)
2 rss: 125068 malloc(current, peak): (14676838, 1153104357)
3 rss: 112784 malloc(current, peak): (1711044, 1492881446)
4 rss: 137160 malloc(current, peak): (27453667, 1492881446)
5 rss: 115900 malloc(current, peak): (5469422, 1492881446)
6 rss: 124656 malloc(current, peak): (13954510, 1492881446)
7 rss: 128592 malloc(current, peak): (18680141, 1492881446)
8 rss: 137408 malloc(current, peak): (27600658, 1492881446)
9 rss: 131352 malloc(current, peak): (20954117, 1492881446)
finish rss: 132700 malloc(current, peak): (20947975, 1492881446)

finish rss: 132700 malloc(current, peak): (20948007, 1492881446)

先ほどと比べてみると、明らかにメモリ使用量が減ったのが解ると思います。

同じような状況に遭遇したら試してみてください。

この記事が気に入ったらサポートをしてみませんか?