ClaudeNext.jsFeb 19, 2019, 2:00 PM

Next.js 8 Webpack Memory Improvements

A condensed section focused on the key takeaways first.

Original Post

Quick Digest

Summary

A condensed section focused on the key takeaways first.

claudeenmodel: claude-sonnet-4-20250514

Next.js 8 Webpack Memory Improvements

Key Points

  • Reduced webpack memory spikes from 548 MB to 182 KB chunks
  • Added concurrency limits to prevent simultaneous asset processing
  • Introduced futureEmitAssets option to optimize memory caching

Summary

Next.js 8 introduced significant build-time memory usage reductions by optimizing webpack's asset writing process. The improvements addressed memory spikes that occurred when building projects with hundreds of pages, sometimes exceeding Node.js's 1.4 GB heap limit.

Key Points

  • Root Cause: Webpack was processing all compilation assets concurrently, causing massive memory spikes (548 MB at once for large projects)
  • Concurrency Fix: Implemented asyncLib.forEachLimit with a limit of 15 concurrent operations instead of unlimited concurrent processing
  • Caching Optimization: Introduced output.futureEmitAssets option to prevent unnecessary in-memory caching of assets after writing
  • Results: Memory allocation reduced from 548 MB chunks to 182 KB chunks over time
  • Upstream Benefits: Optimizations were contributed back to webpack core, benefiting the entire webpack ecosystem

Full Translation

Translations

A translation section that keeps the flow of the original article.

claudejamodel: claude-sonnet-4-20250514

Next.js 8 Webpack メモリ改善

Next.js 8 Webpack メモリ改善

最近、Next.js 8が導入されました。このリリースには、ビルド時のメモリ使用量の大幅な削減が含まれていました。このブログ記事では、コミュニティのためにwebpackの最適化をどのように支援したかを探ります。

Next.jsはゼロ設定で、webpackやBabelなどのツールの上に構築されています。その目標は、重要なことに集中できるよう支援することです:あなたのアプリケーションコードです。

ページベースのアーキテクチャ

現代のWebアプリケーションは1つ以上のページで構成されています。例えば、ホームページ、ブログ、ダッシュボード、または商品一覧などです。Next.jsでは、これらのページはプロジェクトのルートにある特別なpagesディレクトリ内のファイルになります。例えば:pages/about.jsファイルは/aboutのURLにマップされます。

フレームワークの主要な設計制約の1つは、単一ページと数千ページの両方で適切に動作する必要があることです。

メモリ使用量の問題の発見

Serverless Next.jsの実装中に、数百ページのプロジェクトでnext buildを実行すると高いメモリ使用量が発生することが明らかになりました。時にはNode.jsが持つ約1.4 GBのメモリヒープ制限を超えることもありました。

Chrome開発者ツールを使用してビルドプロセスのメモリ使用量をプロファイリングし始めました。結果のプロファイルで、webpackが一度に548 MBのメモリチャンクを割り当てるポイントを発見しました。割り当てられるメモリ量はページ数と直接相関しており、ページが多いほどメモリ使用量が増加することを意味していました。

根本原因の特定

メモリプロファイルのスタックトレースを調べることで、メモリ割り当てスパイクを引き起こした関数を特定することができました。割り当て自体はsource.source()メソッドの呼び出しから発生し、これが結果ファイルを生成してメモリに保存していました。

しかし、source()メソッドを呼び出す関数をさらに上位で見ると、compilation.assetsasyncLib.forEachを使用して反復処理されていることがわかりました。これは、提供された関数がcompilation.assets配列内のすべてのファイルに対して同時に呼び出されることを意味していました。

つまり、例えば100ページがあり、各ページをディスクに書き込む必要がある場合、上記のコードは100すべてを同時に書き込もうとし、100ファイルすべてを同時に生成することになります。

解決策:同時実行制限

この問題の解決策は、セマフォを使用して同時書き込み数を制限することです。一般的にはasync-semaを使用しますが、この場合webpackにはneo-asyncで利用可能な適切なメソッドがすでにありました:

// すべてのアセットに対して関数を同時実行する以前のコード
asyncLib.forEach(compilation.assets, (source, file, callback) => {
  // etc
});

// 最大15個まで同時実行する新しいコード
asyncLib.forEachLimit(compilation.assets, 15, (source, file, callback) => {
  // etc
});

さらなる最適化

この同時実行制限を実装し、ビルドメモリ使用量を再度プロファイリングした後、メモリ割り当てが34 MBの小さな断片に分割されることがわかりました。

この変更は非常に有望な結果を示しましたが、実際にはビルドがまだメモリ不足になったため、問題のプロファイリングと調査を続けました。

メモリプロファイルをさらに詳しく調べると、source.source()メソッドが呼び出された後、メモリがその後クリーンアップされない(ガベージコレクションされない)ことに気づきました。

webpackでは、アセットは一般的にSourceクラスのインスタンスです。これらのクラスはすべて、ファイルソースを生成するsource()メソッドを実装しています。プロファイルでは、多くのアセットがCachedSourceのインスタンスであることが示されました。

CachedSourceの動作方法は、source()が呼び出されると、アセットが破棄されるまで結果がメモリ内にキャッシュされることです。Next.jsが使用するwebpackプラグインを調べると、webpackがファイルを書き込んだ後にsource()を呼び出すプラグインがないことがわかり、書き込まれた値をキャッシュすることに利益がないことを意味していました。

最終的な解決策

Tobias Koppersとの協力の後、彼は新しいアセット書き込み動作にオプトインできる新しいオプションoutput.futureEmitAssetsを実装しました。この新しい動作により、割り当てられるチャンクは時間の経過とともに182 KBまで削減されました。

結論

Next.js 8にはすでにこれらすべての最適化が組み込まれています。Next.jsを使用する際に何かを変更する必要はありません。

この最適化はwebpackに導入されたため、Next.jsユーザーだけでなく、すべてのwebpackユーザーがこれらの最適化の恩恵を受けることができます。

私たちはNext.jsとwebpackのメモリ使用量とパフォーマンスの改善を積極的に続けていきます。

Next.js 8 Webpack Memory Improvements | Next.js | DocsDigest