Fast memory leak/creep when cy.get fails to find the element, and eventually crashes
See original GitHub issueCurrent behavior
While cy.get() retries to find an element , the memory usage of the tab shoots up quickly
See video https://youtu.be/N5jtyBdd05Q
Desired behavior
Memory usage should not increase this quickly
Test code to reproduce
describe('Memory issue', () => {
it('Simple memory test', () => {
cy.visit('https://www.androidpolice.com')
cy.get('.garbageId')
})
})
Cypress Version
5.1.0 and latest 8.6.0
Other
Initially I found this issue while testing my company’s website, then I found that it is easily reproducible with other websites as well
Pre-requisistes
- numTestsKeptInMemory has to be set to 1 or higher
- set defaultCommandTimeout to a higher value (eg. 50000) so there’s more time for the memory to increase
Steps to reproduce
- Visit a website (eg. https://www.androidpolice.com)
- Use cy.get() to find an non-existent element
As you can see in the video linked above, the memory usage starts shooting up when Cypress is trying to find the element. When you re-run the test without closing the tab, the memory doesn’t reset and keeps adding up. Eventually when the usage reaches 4GB, the tab crashes with the “Aw snap” message.
This issue is a lot more apparent if the test is longer with more steps, which can put the memory usage close to the ceiling of 4GB.
So far the reliable workaround is to set numTestsKeptInMemory to 0, which stops the memory creep altogether.
Very occasionally I can observe the garbage collection kicking in and reducing the memory usage, however the cy.get() still rapidly increases the memory usage after
Issue Analytics
- State:
- Created 2 years ago
- Reactions:2
- Comments:8 (3 by maintainers)
Top GitHub Comments
So a bit of an update here - the retries were the dragon’s tail. Tug it, and you get not a snake but a dragon nesting in the depths of the codebase with its treasure horde and toothy grin.
I’ve updated the attached PR with a better solution, which should reduce unnecessary DOM snapshotting in any situation where implicit assertions that an element exists in the DOM fail - including temporarily while we’re waiting for an element to be actionable in tests that eventually pass.
Poking around here further, it’s because of DOM snapshotting. Whenever an assertion fails (badSelector doesn’t exist on the page), we call
Cypress.log()
. https://github.com/cypress-io/cypress/blob/develop/packages/driver/src/cy/assertions.ts#L200. This happens several times per second as we retry the query, and because this is a DOM-related assertion, we snapshot the dom for each failure.In Firefox, these snapshots get cleaned up efficiently, but in the current electron version (and certain chromium / chrome versions) they stick around in memory. While it might be possible to clean up the snapshots better, I’m still investigating why we make dozens of copies of the DOM per second. Even if they’re GCed regularly, it still seems wasteful. Leaking memory at 100mb / second means that we’re allocating memory at 100mb / second.
All this is fairly deem in the Cypress internals, and not easy to debug / change regardless.