Closed nigelellis closed 2 years ago
Sounds like a memory leak somewhere. Difficult to find without access to the repo or a memory profile.
But I think the huge memory usage originates from mini-css-extract-plugin and we have an PR open to add an experimental largely more memory efficient mode.
I'm looking for testers and your case seems like a good fit.
Here is the PR: https://github.com/webpack-contrib/mini-css-extract-plugin/pull/737
see instructions here. You can use the version of the pr with "mini-css-extract-plugin": "webpack-contrib/mini-css-extract-plugin#feature/import-module"
in package.json
@nigel-codaio Maybe you can provide example of repo, so we can investigate memory leak
@sokra -- looks like https://github.com/webpack-contrib/mini-css-extract-plugin/pull/737 was released with v1.5.0. I'll try pulling this in to see if it helps.
@alexander-akait -- I haven't had luck trimming this down but should be able to set aside time to investigate this week.
I tried the latest mini-css-extract-plugin (v1.5.0) with today's webpack5 release (5.34.0) and still hit the issue. @sokra it definitely appears related to the mini-css-extract plugin as editing non-less files doesn't exhibit the memory leak.
Sharing the full project is challenging as this is a commercial codebase. Are there any guides or instructions on how I might perform memory profile? Any best practices to follow? I'm game to dig in but don't know how to start. Thanks!
@nigel-codaio Maybe you can provide full configuration when memory leak? I think you have non official plugin(s) with leaking
Small notes - OptimizeCssAssetsWebpackPlugin
is deprecated and potential leaking, please use https://github.com/webpack-contrib/css-minimizer-webpack-plugin/. What is terser-webpack-plugin
version? You don't need cache-loader
, there big bugs with webpack v5 and possible leaks, please use cache.type: 'filesystem'
(now we have built-in cache)
maybe fixed by https://github.com/webpack/webpack/pull/13184
Hi @sokra and @alexander-akait -- thanks for the suggestions.
I made the following changes today:
With these changes in place, things appear to be working better. @sokra I also pulled in https://github.com/webpack/webpack/pull/13184 and tried bundling with that. This also gives some improvement and appears to have a lower memory cap than without it.
What's the recommendation of filecache vs. memory cache? Our production builds don't use any form of caching currently so I'm mostly focused on webpack devserver behavior with an eye on incremental compilation cost. Is there a discussion anywhere on the tradeoffs between the caching approaches, and a best-practice example on the optimal config for dev?
Thanks.
You can use the filesystem cache for production and development. The filesystem cache includes a memory cache to avoid reading/writing to disk too often. You usually don't need to change any of the advanced settings for the cache. The defaults should work fine.
@sokra The filesystem cache includes a memory cache or you mean cache: { type: 'memory' }
?
cache: { type: "filesystem" }
@sokra so you mean even though we write cache type as filesystem there will be things stored in memory as well?
Yes, memory cache is faster. So for incremental builds that's faster. But cache items only stay for a few generations in the memory cache. See maxMemoryGenerations
.
You could disable that with maxMemoryGenerations: 0
, where cache items are only stored in memory until they are serialized to disk. After that they need to be read from disk on access.
@sokra The memory consumption stays low if I give cache type as 'memory' but when I give it as filesystem the memory usage is very high and the process crashes in just a few minutes.
I'm sticking with cache type memory for now.
@vijaybritto which webpack version?
5.35.1
@vijaybritto Can you try latest version?
@vijaybritto can you share your original webpack config?
@nigel-codaio The problem still exists?
@alexander-akait @sokra we observe the similar issue with our application. Memory consumption with cache: { type: "memory" }
is around 2GB but with filesystem cache it grows dramatically (7-8GB). This basically makes filesystem cache unusable in our setup, my personal laptop starts memory swapping aggressively which degrades performance.
I was able to make a memory snapshot with Chrome, here is a screenshot. Lmk what other info you need, I can probably share the entire snapshot if you give me your email.
@kanoshin What is webpack version?
@alexander-akait 5.38.1
@kanoshin we need entire snapshot
, please do it not only once, 1-2-5-10 runs, you can put it here, because I don't think it contains something sensitivity
I also tried setting maxMemoryGenerations: 0
and experimental option from mini-css-extract-plugin
but it didn't help. We have a codebase with about 9k typescript & css files.
Feel free to ping me when you prepare more snapshots, maybe leaking in non official plugins, you can test it locally
@alexander-akait attaching the snapshots. It's almost impossible to take the snapshot in our larger app, so I decided to use a smaller one but it still shows a very disproportional memory usage comparing to the codebase size. This app is around 223MB of source code with node_modules but initial memory usage is 3x of that amount and then it keeps growing between rebuilds.
What I'm trying to say is that it's not just a memory leak I'm reporting but most importantly is that simply the initial memory consumption for larger apps makes filesystem cache unusable for larger codebases.
Google drive with zip archive: https://drive.google.com/file/d/1_XR27NpTQnYcOnFZFowqJqcB1FEbe1g0/view?usp=sharing.
@kanoshin thanks for these profiles. That were very useful. I needed to implement some custom tooling to analyse them, since the devtools show WeakMap references very badly.
Anyway I found two problems:
The less-loader
keeps hold on the last webpack compilation the processed less files.
There is a weird reference of some Compilations in v8 feedback vectors:
I hope 273bd8f
(#13580) will fix that.
I also found some places where memory usage could be improved a bit by getting rid of same unused data, but these where not leaks...
@kanoshin I released the 2. change as https://github.com/webpack/webpack/releases/tag/v5.39.1 Maybe you could test with that version again and capture new profiles (In this case a single one after 20 rebuilds would be enough). It's difficult to see from the profile if this is was the only problem...
~If you want to "workaround" for problem 1, you can remove these lines: https://github.com/webpack-contrib/less-loader/blob/465ffc4052642d799bb29a85056517db31ee1bf5/src/utils.js#L194-L209~
See less-loader@10
I think we can do major release for less-loader and drop it
@kanoshin Please try https://github.com/webpack-contrib/less-loader/releases/tag/v10.0.0
Please update https://github.com/webpack-contrib/mini-css-extract-plugin/releases/tag/v1.6.1, many memory leaks were fixed
@sokra @alexander-akait I made the same measurements using updated less-loader and mini-css-extract-plugin. Looks like memory consumption improved in absolute numbers but I still see steady growth from snapshot to snapshot. This time I used heapdump package for taking snapshots.
There are still references to Compilation between the snapshots:
Here are the snapshots: https://drive.google.com/file/d/1QPk9Qf5dBDt_YDUhZcEn4p9bTOVwbQeW/view?usp=sharing
hmm... interesting... Could you try to run gc()
before doing the heapdump? (--expose-gc
)
@sokra sure, just did that again. Here are two snapshots one after 1 rebuild and second one after ~20 rebuilds across 4-5 different files (TS, LESS, CSS) https://drive.google.com/file/d/1p0v_sdl65CqaaS1PY2WU2iSWzfk-Cn7X/view?usp=sharing.
Ok it took me a full day to implement tooling for analysing heapsnapshots, but I think I created something that works. Using the devtools it's next to impossible to follow the retainer chain when WeakMaps are involved. The tricky thing with WeakMaps it that you would need to follow the key retainer and the WeakMap retainer while skipping over cycles where the key is held by something the value references.
Anyway here is a dump of retainers of the 12 Compilation objects in your heapsnapshot:
(It's still not easy to read and you need to know a lot about v8 to understand it. A normal leak is a simple chain. When WeakMaps are involved you will see multiple chains.)
synthetic (GC roots) @3
[13] = synthetic (Global handles) @29
internal 4492 = processImmediate() in node:internal/timers @6541
internal context = object system / Context @3758169
internal previous = object system / Context @3203705
context timerListMap = object { } @3203647
[60000] = object TimersList @5832419
._idleNext = object Timeout @7284333
._onTimeout = () @11585955
internal context = object system / Context @1657275
context compiler = object Compiler @600341
._lastCompilation = object Compilation @5653795
This is the last active compilation, which is held by the Compiler. That's fine.
synthetic (GC roots) @3
[13] = synthetic (Global handles) @29
internal 4355 / DevTools console = object { dir, dirxml, profile, profileEnd, clear, table, keys, values, debug, undebug, ... } @6363
.require = require() in node:internal/modules/cjs/helpers @4210341
.cache = object { /Users/kir...le-call.js, /Users/kir...s/index.js, /Users/kir...hortOut.js, /Users/kir...terator.js, /Users/kir...i/index.js, /Users/kir...oopHook.js, /Users/kir...ailHook.js, /Users/kir...b/pkcs1.js, /Users/kir...ject-dp.js, /Users/kir...p/index.js, ... } @169023
./Users/kirilla/repos/st/app/Clients/Web/node_modules/less/lib/less/plugin-manager.js = object Module @591753
internal 2 / part of key (Module @591753) -> value (Object @1942751) pair in WeakMap (table @3687911) = object { __esModule, default } @1942751
synthetic (GC roots) @3
[13] = synthetic (Global handles) @29
internal 4516 = () in node:internal/process/esm_loader @6589
internal context = object system / Context @3004645
context ESMLoader = object Loader @3004659
.cjsCache = object WeakMap @4793971
internal table = array @3687911
internal 1235 / part of key (Module @591753) -> value (Object @1942751) pair in WeakMap (table @3687911) = object { __esModule, default } @1942751
.default = PluginManagerFactory() in /Users/kirilla/repos/st/app/Clients/Web/node_modules/less/lib/less/plugin-manager.js @1940823
internal context = object system / Context @1754891
context pm = object PluginManager @8405329
.webpackLoaderContext = object { getOptions, emitWarning, emitError, getLogger, resolve, getResolve, emitFile, addBuildDependency, utils, rootContext, ... } @8725371
.getOptions = getOptions() in /Users/kirilla/repos/st/app/Clients/Web/node_modules/webpack/lib/NormalModule.js @9361177
internal context = object system / Context @9361179
context compilation = object Compilation @7747467
This is leak number 1. Again (or still) related to the less-loader. While we removed our own reference from the less object, less itself seem to leak the last plugin manager used, which references our loader context and so the Compilation.
maybe we can remove webpackLoaderContext
from pluginManager again after finishing compilation? cc @alexander-akait
But it also makes sense to fix the pm
leak in less
: https://github.com/less/less.js/blob/c5181800980f0a24dfaa5fe71fba813a6a80542a/packages/less/src/less/plugin-manager.js#L158 cc @matthew-dean
synthetic (GC roots) @3
[13] = synthetic (Global handles) @29
internal 4355 / DevTools console = object { dir, dirxml, profile, profileEnd, clear, table, keys, values, debug, undebug, ... } @6363
.require = require() in node:internal/modules/cjs/helpers @4210341
.cache = object { /Users/kir...le-call.js, /Users/kir...s/index.js, /Users/kir...hortOut.js, /Users/kir...terator.js, /Users/kir...i/index.js, /Users/kir...oopHook.js, /Users/kir...ailHook.js, /Users/kir...b/pkcs1.js, /Users/kir...ject-dp.js, /Users/kir...p/index.js, ... } @169023
./Users/kirilla/repos/st/app/Clients/Web/node_modules/webpack/lib/ChunkGraph.js = object Module @2512045
internal 2 / part of key (Module @2512045) -> value (ChunkGraph @2640331) pair in WeakMap (table @3687911) = ChunkGraph() in /Users/kirilla/repos/st/app/Clients/Web/node_modules/webpack/lib/ChunkGraph.js @2640331
synthetic (GC roots) @3
[13] = synthetic (Global handles) @29
internal 4516 = () in node:internal/process/esm_loader @6589
internal context = object system / Context @3004645
context ESMLoader = object Loader @3004659
.cjsCache = object WeakMap @4793971
internal table = array @3687911
internal 7718 / part of key (Module @2512045) -> value (ChunkGraph @2640331) pair in WeakMap (table @3687911) = ChunkGraph() in /Users/kirilla/repos/st/app/Clients/Web/node_modules/webpack/lib/ChunkGraph.js @2640331
internal context = object system / Context @3437037
internal previous = object system / Context @3417781
context chunkGraphForChunkMap = object WeakMap @4947027
internal table = array @990375
internal 9 / part of key (Chunk @990383) -> value (ChunkGraph @990385) pair in WeakMap (table @990375) = object ChunkGraph @990385
synthetic (GC roots) @3
[13] = synthetic (Global handles) @29
internal 4492 = processImmediate() in node:internal/timers @6541
internal context = object system / Context @3758169
internal previous = object system / Context @3203705
context timerListMap = object { } @3203647
[60000] = object TimersList @5832419
._idleNext = object Timeout @7284333
._onTimeout = () @11585955
internal context = object system / Context @1657275
context compiler = object Compiler @600341
internal 39 / part of key (Compiler @600341) -> value (PersistentChildCompilerSingletonPlugin @601845) pair in WeakMap (table @601787) = object PersistentChildCompilerSingletonPlugin @601845
synthetic (GC roots) @3
[13] = synthetic (Global handles) @29
internal 4355 / DevTools console = object { dir, dirxml, profile, profileEnd, clear, table, keys, values, debug, undebug, ... } @6363
.require = require() in node:internal/modules/cjs/helpers @4210341
.cache = object { /Users/kir...le-call.js, /Users/kir...s/index.js, /Users/kir...hortOut.js, /Users/kir...terator.js, /Users/kir...i/index.js, /Users/kir...oopHook.js, /Users/kir...ailHook.js, /Users/kir...b/pkcs1.js, /Users/kir...ject-dp.js, /Users/kir...p/index.js, ... } @169023
./Users/kirilla/repos/st/app/Clients/Web/node_modules/html-webpack-plugin/lib/cached-child-compiler.js = object Module @2512555
.exports = object { CachedChil...ompilation } @3688233
.CachedChildCompilation = CachedChildCompilation() in /Users/kirilla/repos/st/app/Clients/Web/node_modules/html-webpack-plugin/lib/cached-child-compiler.js @601335
internal context = object system / Context @601299
context compilerMap = object WeakMap @601305
internal table = array @601787
internal 3 / part of key (Compiler @600341) -> value (PersistentChildCompilerSingletonPlugin @601845) pair in WeakMap (table @601787) = object PersistentChildCompilerSingletonPlugin @601845
.compilationState = object { isCompiling, isVerifyingCache, entries, compiledEntries, compilationResult, mainCompilationHash } @7146865
.compilationResult = object { compiledEntries, dependencies, mainCompilationHash } @3496709
.compiledEntries = object { /Users/kir.../index.ejs } @3554355
./Users/kirilla/repos/st/app/Clients/Web/node_modules/html-webpack-plugin/lib/loader.js!/Users/kirilla/repos/st/app/Clients/Web/packages/auth/app/index.ejs = object { content, hash, entry } @2857067
.entry = object Chunk @990383
internal 20 / part of key (Chunk @990383) -> value (ChunkGraph @990385) pair in WeakMap (table @990375) = object ChunkGraph @990385
._cacheChunkGraphModuleKey2 = object PublicPathRuntimeModule @2341369
.compilation = object Compilation @1652005
This is leak number 2. Two layers of WeakMaps are involved here. There is probably no way to find that in the normal tooling.
We can clean it up a bit by assuming that the module scoped variable chunkGraphForChunkMap
in webpack/lib/ChunkGraph.js
, and the Compiler @600341
are globals:
chunkGraphForChunkMap = object WeakMap @4947027
internal table = array @990375
internal 9 / part of key (Chunk @990383) -> value (ChunkGraph @990385) pair in WeakMap (table @990375) = object ChunkGraph @990385
object Compiler @600341
internal 39 / part of key (Compiler @600341) -> value (PersistentChildCompilerSingletonPlugin @601845) pair in WeakMap (table @601787) = object PersistentChildCompilerSingletonPlugin @601845
/Users/kirilla/repos/st/app/Clients/Web/node_modules/html-webpack-plugin/lib/cached-child-compiler.js = object Module @2512555
.exports = object { CachedChil...ompilation } @3688233
.CachedChildCompilation = CachedChildCompilation() in /Users/kirilla/repos/st/app/Clients/Web/node_modules/html-webpack-plugin/lib/cached-child-compiler.js @601335
internal context = object system / Context @601299
context compilerMap = object WeakMap @601305
internal table = array @601787
internal 3 / part of key (Compiler @600341) -> value (PersistentChildCompilerSingletonPlugin @601845) pair in WeakMap (table @601787) = object PersistentChildCompilerSingletonPlugin @601845
.compilationState = object { isCompiling, isVerifyingCache, entries, compiledEntries, compilationResult, mainCompilationHash } @7146865
.compilationResult = object { compiledEntries, dependencies, mainCompilationHash } @3496709
.compiledEntries = object { /Users/kir.../index.ejs } @3554355
./Users/kirilla/repos/st/app/Clients/Web/node_modules/html-webpack-plugin/lib/loader.js!/Users/kirilla/repos/st/app/Clients/Web/packages/auth/app/index.ejs = object { content, hash, entry } @2857067
.entry = object Chunk @990383
internal 20 / part of key (Chunk @990383) -> value (ChunkGraph @990385) pair in WeakMap (table @990375) = object ChunkGraph @990385
._cacheChunkGraphModuleKey2 = object PublicPathRuntimeModule @2341369
.compilation = object Compilation @1652005
So the html-webpack-plugin keeps a reference the (child) Compilation used to compute the html files.
Not sure if this is intendend. It seem to leak it because it references the Chunk @990383
via compiledEntries.*.entry
cc @jantimon
object Compilation @1652005
.compiler = object Compiler @3351893
.parentCompilation = object Compilation @609611
Compilation @1652005
is the compilation from the html-webpack-plugin. As it's a child compilation is has a reference to the parent Compilation.
This makes the reference to Compilation @1652005
expensive.
Now it gets dirty. The following 8 Compilations are referenced by the Compilation @609611
from above (which seem to be the first Compilation):
synthetic (GC roots) @3
[13] = synthetic (Global handles) @29
internal 4355 / DevTools console = object { dir, dirxml, profile, profileEnd, clear, table, keys, values, debug, undebug, ... } @6363
.require = require() in node:internal/modules/cjs/helpers @4210341
.cache = object { /Users/kir...le-call.js, /Users/kir...s/index.js, /Users/kir...hortOut.js, /Users/kir...terator.js, /Users/kir...i/index.js, /Users/kir...oopHook.js, /Users/kir...ailHook.js, /Users/kir...b/pkcs1.js, /Users/kir...ject-dp.js, /Users/kir...p/index.js, ... } @169023
./Users/kirilla/repos/st/app/Clients/Web/node_modules/webpack/lib/NormalModuleFactory.js = object Module @2512527
.exports = NormalModuleFactory() in /Users/kirilla/repos/st/app/Clients/Web/node_modules/webpack/lib/NormalModuleFactory.js @3350351
internal context = object system / Context @1661983
context unsafeCacheDependencies = object WeakMap @3523185
internal table = array @3524939
internal 21778 / part of key (HarmonyImportSideEffectDependency @1741453) -> value (Object @4567883) pair in WeakMap (table @3524939) = object { module, fileDependencies, missingDependencies, contextDependencies } @4567883
object Compilation @609611
.chunkGraph = object ChunkGraph @990597
.moduleGraph = object ModuleGraph @946751
._dependencyMap = object Map @1656927
internal table = array @1656953
internal 49021 = object HarmonyImportSideEffectDependency @1741453
internal 13 / part of key (HarmonyImportSideEffectDependency @1741453) -> value (Object @4567883) pair in WeakMap (table @3524939) = object { module, fileDependencies, missingDependencies, contextDependencies } @4567883
.module = object NormalModule /Users/kirilla/repos/st/app/Clients/Web/node_modules/query-string/index.js @1361323
.parser = object JavascriptParser @6141493
.hooks = object { evaluateTypeof, evaluate, evaluateIdentifier, evaluateDe...Identifier, evaluateCa...sionMember, isPure, preStatement, blockPreStatement, statement, statementIf, ... } @5786111
.callMemberChain = object HookMap @5786183
._map = object Map @10164927
internal table = array @7039523
internal 9 = object Hook @7039527
.taps = object Array @10244077
[0] = object { type, fn, name } @6142947
.fn = () in /Users/kirilla/repos/st/app/Clients/Web/node_modules/webpack/lib/dependencies/CommonJsExportsParserPlugin.js @6142949
internal context = object system / Context @8392167
context this = object CommonJsExportsParserPlugin @10247099
.moduleGraph = object ModuleGraph @5282155
._cacheModuleGraphModuleKey2 = object JsonpChunkLoadingRuntimeModule @5383121
.compilation = object Compilation @6140385
This sounds like a problem in webpack. It should not have this line: .parser = object JavascriptParser @6141493
.
Usually the parser
reference should be removed from the Modules once that Compilation is done, but that doesn't seem to be the case for all modules.
This seem to multiply the leak before, as for each leaked Compilation this problem will cause more Compilation to leak. Compilations should not be connected together via references...
Investigating... (EDIT: fixed by #13680)
The html-webpack-plugin stores a singleton of PersistentChildCompilerSingletonPlugin
to the compiler using a WeakMap
:
https://github.com/jantimon/html-webpack-plugin/blob/8f8f7c53c4e4f822020d6da9de0304f8c23de08f/lib/cached-child-compiler.js#L60-L67
This singleton stores a the result of a child compiler:
@jantimon this is memory leak...
@jantimon this is memory leak...
Let's call it a "cache". It doesn't leak a infinite amount of memory, only the latest compilation used for html generation.
Maybe we can reduce the memory usage a bit by not referencing Chunk
objects from the compilation (Chunks will hold on the whole Compilation and parent Compilation, because you can reach other chunks from there and modules, and for backward-compat reasons, etc.)
I also can't see any leaks here. But we can definitely try to improve the cache size :)
Maybe I should explain why this cache was introduced at all:
Previously the html-webpack-plugin child compiler would compile on every watch run even if no relevant file was changed. With the new filesnapshot api it was finally possible to run a child compiler only once the child compilers file dependencies change
@jantimon Why we need to keep whole compilation, maybe we can cache only related stuff?
@kanoshin Can you test again, please update webpack https://github.com/webpack/webpack/releases/tag/v5.42.0 and less-loader https://github.com/webpack-contrib/less-loader/releases/tag/v10.0.1
I've tried it. I see no memory growth when modifying both TS and LESS modules. We had +100Mb per modification of all files in package once. Right now it is gone :+1: However, when I modify LESS files there's an occasinal error in the debugger. It crashes the process. This is it:
VM3690 AssetGenerator.js:268 Uncaught TypeError: Cannot read property 'dataUrl' of undefined
at AssetGenerator.getTypes (/home/strider/Projects/st/app/Clients/Web/node_modules/webpack/lib/asset/AssetGenerator.js:268:24)
at NormalModule.getSourceTypes (/home/strider/Projects/st/app/Clients/Web/node_modules/webpack/lib/NormalModule.js:1091:39)
at NormalModule.cleanupForCache (/home/strider/Projects/st/app/Clients/Web/node_modules/webpack/lib/NormalModule.js:362:45)
at NormalModuleFactory.cleanupForCache (/home/strider/Projects/st/app/Clients/Web/node_modules/webpack/lib/NormalModuleFactory.js:670:11)
at Compiler._cleanupLastNormalModuleFactory (/home/strider/Projects/st/app/Clients/Web/node_modules/webpack/lib/Compiler.js:383:34)
at Compiler.createNormalModuleFactory (/home/strider/Projects/st/app/Clients/Web/node_modules/webpack/lib/Compiler.js:1049:8)
at Compiler.newCompilationParams (/home/strider/Projects/st/app/Clients/Web/node_modules/webpack/lib/Compiler.js:1071:30)
at Compiler.compile (/home/strider/Projects/st/app/Clients/Web/node_modules/webpack/lib/Compiler.js:1082:23)
at /home/strider/Projects/st/app/Clients/Web/node_modules/webpack/lib/Watching.js:188:19
at Hook.eval [as callAsync] (eval at create (/home/strider/Projects/st/app/Clients/Web/node_modules/webpack/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:24:1)
I remember no such error in the previous tests (before today's fixes). May it be caused by the fixes?
Yes, regression on our side, please wait, we will fix it in near future
@jantimon Why we need to keep whole compilation, maybe we can cache only related stuff? yes that's what I meant by we should try to decrease the cache size 😄
Right now the following is cached:
{
dependencies: FileDependencies,
compiledEntries: {[entryName: string]: ChildCompilationResultEntry}
}
ChildCompilationResultEntry is coming from: https://github.com/jantimon/html-webpack-plugin/blob/8f8f7c53c4e4f822020d6da9de0304f8c23de08f/lib/child-compiler.js#L189-L192
{
content: templateSource,
hash: childCompilation.hash || 'XXXX',
entry: entries[entryIndex]
}
I assume that entries might keep the entire compilation in memory or I missed something
@jackfranklin yes
chunkGraphForChunkMap = object WeakMap @4947027
internal table = array @990375
internal 9 / part of key (Chunk @990383) -> value (ChunkGraph @990385) pair in WeakMap (table @990375) = object ChunkGraph @990385
object Compiler @600341
internal 39 / part of key (Compiler @600341) -> value (PersistentChildCompilerSingletonPlugin @601845) pair in WeakMap (table @601787) = object PersistentChildCompilerSingletonPlugin @601845
/Users/kirilla/repos/st/app/Clients/Web/node_modules/html-webpack-plugin/lib/cached-child-compiler.js = object Module @2512555
.exports = object { CachedChil...ompilation } @3688233
.CachedChildCompilation = CachedChildCompilation() in /Users/kirilla/repos/st/app/Clients/Web/node_modules/html-webpack-plugin/lib/cached-child-compiler.js @601335
internal context = object system / Context @601299
context compilerMap = object WeakMap @601305
internal table = array @601787
internal 3 / part of key (Compiler @600341) -> value (PersistentChildCompilerSingletonPlugin @601845) pair in WeakMap (table @601787) = object PersistentChildCompilerSingletonPlugin @601845
.compilationState = object { isCompiling, isVerifyingCache, entries, compiledEntries, compilationResult, mainCompilationHash } @7146865
.compilationResult = object { compiledEntries, dependencies, mainCompilationHash } @3496709
.compiledEntries = object { /Users/kir.../index.ejs } @3554355
./Users/kirilla/repos/st/app/Clients/Web/node_modules/html-webpack-plugin/lib/loader.js!/Users/kirilla/repos/st/app/Clients/Web/packages/auth/app/index.ejs = object { content, hash, entry } @2857067
.entry = object Chunk @990383
internal 20 / part of key (Chunk @990383) -> value (ChunkGraph @990385) pair in WeakMap (table @990375) = object ChunkGraph @990385
._cacheChunkGraphModuleKey2 = object PublicPathRuntimeModule @2341369
.compilation = object Compilation @1652005
above trace where they stored
I see some ModuleGraphConnection
objects in the delta. I've also detected them in the previous tests.
@StreetStrider It can be any non official plugin, please provide profiling
thanks I will look soon
Bug report
What is the current behavior? We recently upgraded from webpack4 to webpack5 and are experiencing memory leaks when using webpack5 caching.
We have the following caching policy defined:
When we launch webpack devserver the VM usage climbs to about 2.5GB and then stabilizes. The bundle outputs in about 70s. When we mutate a source file, this correctly triggers a recompile, and memory doubles before hitting our max cap of 6GB. The process shortly OOM's after that.
We've tried various options for the filesystem cache including maxAge, maxGeneration, etc. all with no difference. I also tried removing contentHashs and setting
output: {clean: true}
to no avail. The only thing that does help is disabling caching completely (cache=false
) but that causes a horrible regression in incremental compilation times.Here's a snapshot of our running webpack5 config:
Here's sample output during the compile:
Versioning information: webpack: 5.32.0 webpack-cli: 4.5.0 webpack-dev-server: ^.11.2
I noticed similar issues reported in https://github.com/webpack/webpack/issues/12947. The proposal there was to use a memory cache and set
output.clean
. I've tried that with{type: "memory", maxGenerations: 1}
andoutput.clean
, as well as stripping contentHashes. I still see the issue.Anything else I can do to help narrow down the underlying issue? Any workaround that would maintain caching without the OOM?
Thanks, Nigel.
If the current behavior is a bug, please provide the steps to reproduce. I'm just running:
and then mutate a source file.
What is the expected behavior? The memory working-set should be stable and not grow without bounds.
Other relevant information: webpack version: 5.32.0 Node.js version: 14.16.1 Operating System: MacOS (or Linux Buster) Additional tools: