This is a basic Webpack setup to reproduce the memory leak described in this Webpack issue: webpack/webpack#17857
NOTE: This is similar to https://github.com/helloitsjoe/webpack-memory-leak. It looks like a different root cause and different effect, but seems to stem from not cleaning up after child compilers.
This repo uses a custom loader to render React components to HTML during the build. The libraries used during that process (react
and react-dom
) are stored in cache and never cleaned up, leaking a few MB on each compilation in watch mode. Similar to https://github.com/helloitsjoe/webpack-memory-leak, HtmlWebpackPlugin
creates a child compiler so it's useful in this reproduction, but the leak itself seems to be in Webpack.
Note: duplicate strings are retained in memory even without the custom loader, the loader just makes it more obvious. See the simplified branch for vanilla HtmlWebpackPlugin usage.
yarn
- Run
webpack serve
in inspect mode:NODE_OPTIONS=--inspect $(yarn bin webpack) serve
- Open a memory profiler, for example
chrome://inspect
in Chrome - Take a heap snapshot
- Save
src/app.js
, which will causeHtmlWebpackPlugin
to create a child compiler. Save a few times. - Take a second heap snapshot and notice the difference in size
- Take a third snapshot and select
Objects allocated between snapshots 1 and 2
, or selectComparison
view and compare snapshot 3 to 1.
Here's where this is different from https://github.com/helloitsjoe/webpack-memory-leak:
Find and open the (string)
constructor in the snapshot diff, it should be the first or second item when sorting by retained size or size delta. You should see multiple copies of the same strings, including source files (react
and react-dom
) used in src/react-loader.js
, and this string from EvalDevToolModulePlugin
:
/* * ATTENTION: The "eval" devtool has been used (maybe by default in mode: "development") ...
A new copy of each of these is added every recompile. It looks to me like these are being cached in a Map
in MemoryCachePlugin
using a child compiler ID, for example (note the HtmlWebpackPlugin_0-7
, the 7th time I ran a recompilation):
'HtmlWebpackCompiler|0|Compilation/codeGeneration|javascript/esm|data:text/javascript,__webpack_public_path__ = __webpack_base_uri__ = htmlWebpackPluginPublicPath;|HtmlWebpackPlugin_0-7'
I'm not sure this is the only place these are being cached, and this may not be the root cause, but I can see that cache growing by a number of entries on each recompile.
I also noticed the number of instances of most of the Source
classes have many more new than deleted when comparing to a previous snapshot:
😬