Jest メモリリークと「heap out of memory」エラーの対処
Jestのメモリリーク
JavaScriptのテスティングフレームワークであるJestは、長らくメモリリーク、過大なメモリ消費が問題となっている。メモリリークの影響により、テストを実行するCIが低速になったり、「heap out of memory」エラーが出て異常終了したりする。この問題は未解決で、いくつか提示されている対処療法でごまかす必要がある。
再現環境
もともと以下の環境でテストを実施していて、ほとんどheap out of memoryになることはなかった。
対象 | 情報 |
---|---|
Node | v14 |
Jest | v26 |
プリプロセッサ | ts-jest |
CI環境 | CPU 8 Core, Memory 16GB |
上記の状態からNode、Jestそれぞれアップデートを試したところ、いずれのアップデートでもCI実行速度の低下とメモリ消費の増大を確認した。 - Jest v26からv27へのアップデート - Node 14から18へのアップデート
その他の再現情報
Jestやts-jestのissueにも同じ現象が多く報告されている。 - [Bug]: Memory consumption issues on Node JS 16.11.0+ · Issue #11956 · jestjs/jest · GitHub - Module caching memory leak · Issue #1967 · kulshekhar/ts-jest · GitHub
対処療法
前述の通りメモリリークを根本解決する方法はないので、対処療法でごまかす必要がある。
ヒープ領域を広げる
Nodeのmax-old-space-sizeオプションを使うと、確保するヒープ領域を指定できる。Node v12以降、Nodeがデフォルトで確保するヒープサイズは実行環境に応じて変化する。もし実行環境のメモリ量が少ないのであれば、オプションでヒープ領域を多めに確保すると、out of memory問題は解決する可能性がある。
以下はpackage.jsonのスクリプトでヒープ領域を指定する例。
"test": "NODE_OPTIONS=\"--max-old-space-size=4096\" jest"
テストを分割する
shardオプション(Jest CLIオプション #shard)を使うとテストケースを分割できる。CIでテストを回す際に、shardオプションを使用し別々のジョブで並列実行することで、out of memory問題の緩和とテスト実行時間の短縮が見込まれる。
以下はpackage.jsonのスクリプトでテストを3分割する例。
"test1": "jest shard=1/3" "test2": "jest shard=2/3" "test3": "jest shard=3/3"
workerのメモリ使用量を制限する
Jest v29から追加されたworkerIdleMemoryLimitオプション(Jestの設定 #workerIdleMemoryLimit)を使うと、workerが使用するメモリ量を制限できる。メモリ量は1GiB
のように指定できるほか、0.2
と書けば実行環境の20%を指定できる。
以下はpackage.jsonのスクリプトでworkerのメモリ使用量を制限する例。
"test": "jest --workerIdleMemoryLimit=0.2"