現在、Jest にはメモリリークの問題がある。テストが増えてきたことで、テストを実行するとマシンのメモリを食い尽くすようになってきた。その調査と対処の記録。
2024/04/08追記
問題は Node.js の方にあったようで、Node.js 21.1 で修正されたようです。
https://github.com/jestjs/jest/issues/7874#issuecomment-1935517499
TL;DR
- Jest v29 以降ならば、この問題を回避するための設定 workerIdleMemoryLimit を使える
- Create React App に bundle されている Jest はバージョンが古いので、workerIdleMemoryLimit は使えない。その場合、環境変数
NODE_OPTIONS=--max-old-space-size=<MB>
を指定してヒープの最大容量を制限することで回避可能 - 大体全部ここに書いてある
https://github.com/jestjs/jest/issues/11956#issuecomment-969041221
再現
テストプロジェクト作成。
#! /bin/env bash
set -eu
PJ=mem-leak-test
TEST_DIR=$PJ/src/tests
npx create-react-app $PJ
mkdir $TEST_DIR
for i in {1..100}; do
echo "test('t', () => expect('A').toBe('A'))" > `printf $TEST_DIR/%03d.test.jsx $i`
done
Jest の --logHeapUsage を使って計測。--expose-gc を使えとあるが、環境変数 NODE_OPTIONS 経由では指定できないので、npm run test
ではなく内部スクリプトを直接実行。
node --expose-gc node_modules/react-scripts/scripts/test.js --no-watch --runInBand --logHeapUsage src
結果。
PASS src/App.test.js (53 MB heap size)
PASS src/tests/012.test.jsx (56 MB heap size)
PASS src/tests/094.test.jsx (59 MB heap size)
PASS src/tests/098.test.jsx (62 MB heap size)
PASS src/tests/043.test.jsx (65 MB heap size)
~~ 中略 ~~
PASS src/tests/008.test.jsx (313 MB heap size)
PASS src/tests/099.test.jsx (332 MB heap size)
PASS src/tests/056.test.jsx (335 MB heap size)
PASS src/tests/006.test.jsx (338 MB heap size)
PASS src/tests/051.test.jsx (343 MB heap size)
PASS src/tests/060.test.jsx (344 MB heap size)
PASS src/tests/052.test.jsx (347 MB heap size)
PASS src/tests/003.test.jsx (350 MB heap size)
対処
このメモリリークの問題は下記の通り Issue として登録されているが、まだ解決には至っていない。
v29 から回避策として workerIdleMemoryLimit という設定が追加されているが、我々は Create React App を使っている都合上、それは使えないので、代わりに Node.js のオプション --max-old-space-size を使って対処。
試しに、150MB に制限して実行してみる。
node --expose-gc --max-old-space-size=150 node_modules/react-scripts/scripts/test.js --no-watch --runInBand --logHeapUsage src
結果。
PASS src/tests/040.test.jsx (45 MB heap size)
PASS src/tests/016.test.jsx (49 MB heap size)
PASS src/tests/082.test.jsx (52 MB heap size)
PASS src/tests/094.test.jsx (56 MB heap size)
PASS src/tests/021.test.jsx (97 MB heap size)
PASS src/tests/013.test.jsx (102 MB heap size)
PASS src/tests/025.test.jsx (103 MB heap size)
PASS src/tests/004.test.jsx (106 MB heap size)
~~ 中略 ~~
PASS src/tests/100.test.jsx (100 MB heap size)
PASS src/tests/090.test.jsx (103 MB heap size)
PASS src/tests/048.test.jsx (107 MB heap size)
PASS src/tests/057.test.jsx (109 MB heap size)
PASS src/tests/097.test.jsx (103 MB heap size)
PASS src/tests/059.test.jsx (105 MB heap size)
PASS src/tests/099.test.jsx (108 MB heap size)
PASS src/tests/058.test.jsx (111 MB heap size)
PASS src/tests/006.test.jsx (114 MB heap size)
PASS src/tests/003.test.jsx (116 MB heap size)
PASS src/tests/088.test.jsx (117 MB heap size)
PASS src/tests/070.test.jsx (68 MB heap size)
PASS src/tests/072.test.jsx (71 MB heap size)
150MB を境にメモリが解放されていることが見て取れる。
普段は、次のように環境変数 NODE_OPTIONS 経由で指定すると楽。
NODE_OPTIONS=--max-old-space-size=150 npm run test
以上。