question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Reduce Memory Usage with window.gc()

See original GitHub issue

I 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

// 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:closed
  • Created 3 years ago
  • Reactions:12
  • Comments:27 (14 by maintainers)

github_iconTop GitHub Comments

5reactions
bahunovcommented, Dec 7, 2020

I 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

  • enable win.gc() for chrome
// 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();
		}
	});
});

Thanks alot @CoryDanielson !! I tried this fix in our jenkins-docker pipeline… it seem to have fixed the stuck runs with chrome.

3reactions
flotwigcommented, Oct 23, 2020

There’s a chance that tests for low-memory apps would run slower with this, because calling gc is expensive.

@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#firefoxGcInterval

You 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.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Garbage Collection does not reduce current memory usage
Calling GC.Collect() is only a request to collect garbage, it's not an order, so the CLR may choose to ignore the request if...
Read more >
Improve garbage collection performance - UWP applications
In this article. Reduce memory consumption; Reduce garbage collection time. Universal Windows Platform (UWP) apps written in C# and Visual ...
Read more >
Minimize Java Memory Usage with the Right Garbage Collector
As we can see, the unused RAM is not released back to OS. JVM with Parallel GC keeps it forever, even disregarding the...
Read more >
Manage your app's memory - Android Developers
Learn how you can proactively reduce memory usage while developing ... The provided onTrimMemory() callback method allows your app to listen ...
Read more >
Memory management - JavaScript - MDN Web Docs
Some high-level languages, such as JavaScript, utilize a form of automatic memory management known as garbage collection (GC). The purpose of a ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found