marble log

Webエンジニアの技術ブログです

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"