Reduce Memory Usage with window.gc()
See original GitHub issueI recently discovered that a method can be exposed in Chrome and Electron to trigger garbage collection, and it seems to improve memory consumption significantly during test runs.
I’m wondering if adding this functionality to cypress by default (rather than a plugin) would be worthwhile, to hopefully reduce the number of crashes and improve performance.
Browser | Flag/Switch |
---|---|
Chrome | –js-flags=–expose-gc |
Electron | –js-flags=–expose_gc |
Once enabled, you can simply call window.gc()
within javascript code.
I ran the same Cypress test with and without calling window.gc()
in a global afterEach
and saw a 30-50% reduction in peak memory usage from the same tests.
I suspect that this will also reduce the number of crashes due to hitting the max memory. For a large and complex app with lots of requests, I think the CPU is too busy and never has the downtime required for javascripts garbage collector to clean up stale references. GC does occur occasionally, but not often enough, and that eventually becomes a fatal mistake as the cypress process gets closer to the max memory threshold and GC is not run. In my case, starting a new test could increase memory usage by 100-300mb, and put the app over the 2gb limit.
To enable win.gc()
for election, run cypress with the following with this environment variable set:
ELECTRON_EXTRA_LAUNCH_ARGS=--js-flags=--expose_gc cypress run
cypress/plugins/index.js
- plugins/index.js will not work for Cypress v10+, see https://docs.cypress.io/guides/references/migration-guide#Plugins-File-Removed
// enables win.gc() for chrome
module.exports = (on, config) => {
on('before:browser:launch', (browser, launchOptions) => {
if (browser.name === 'chrome') {
// exposes window.gc() function that will manually force garbage collection
launchOptions.args.push('--js-flags=--expose-gc');
}
return launchOptions;
});
};
cypress/support/index.js
afterEach(() => {
cy.window().then(win => {
// window.gc is enabled with --js-flags=--expose-gc chrome flag
if (typeof win.gc === 'function') {
// run gc multiple times in an attempt to force a major GC between tests
win.gc();
win.gc();
win.gc();
win.gc();
win.gc();
}
});
});
Issue Analytics
- State:
- Created 3 years ago
- Reactions:12
- Comments:27 (14 by maintainers)
Thanks alot @CoryDanielson !! I tried this fix in our jenkins-docker pipeline… it seem to have fixed the stuck runs with chrome.
@CoryDanielson that’s the reason why forced GC has not been implemented in Cypress as a whole - it is expensive, and causes user tests to pause/stutter during garbage collection.
When Cypress initially introduced Firefox support, code was introduced that used the Firefox driver protocol to force garbage collection after tests (
Cypress.on('test:before:run:async', ...)
). This was necessary because Firefox <= 79 had a memory leak that was not getting GC’d properly unless we manually forced GC: https://docs.cypress.io/guides/references/configuration.html#firefoxGcIntervalYou can manually trigger the Firefox GC routine (even in FF >= 80) by using
Cypress.backend('firefox:force:gc')
.The reason why forced GC was removed as soon as Firefox 80 became available was because it was causing user tests to run much more slowly than if we didn’t force GC.
With this in mind, I still think that the functionality you’re proposing is a good idea, because users do run into memory issues that can be helped with the use of forced GC. However, it is probably better-suited to being a user plugin than to being in Cypress core.