Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Performance: fix memory leak #5460

Conversation

olafurpg
Copy link
Member

@olafurpg olafurpg commented Sep 4, 2024

Fixes CODY-3616
Previously, the Cody extension in all IDEs (including VS Code and JetBrains) had a memory leak causing the process to consume multiple GB of memory after long usage. This PR fixes this issue by calling tree.delete() on tree-sitter ASTs after they are evicted from an LRU cache.

The fix itself is a one-liner. Most of the diff in this PR is adding the reproduction in memory.test.ts, which is a test that we skip by default but I think it's helpful to keep ti in the repo as documentation on how to reproduce memory leaks in the future.

Test plan

  • Follow instructions in memory.test.ts to download and convert Cody NES recordings.
  • Run the "replay" test with and without the second commit of this PR where we delete trees on LRU eviction.
# Before, reach 2gb "external" heap after processing only 200 editor events
stdout | src/memory.test.ts > Memory Usage > replay
{ totalEvents: 0, heapUsed: '-170952.00 B', external: '-1013.00 B' }

stdout | src/memory.test.ts > Memory Usage > replay
{ totalEvents: 50, heapUsed: '36.85 MB', external: '115.02 MB' }

stdout | src/memory.test.ts > Memory Usage > replay
{ totalEvents: 100, heapUsed: '38.91 MB', external: '590.83 MB' }

stdout | src/memory.test.ts > Memory Usage > replay
{ totalEvents: 150, heapUsed: '35.38 MB', external: '1.14 GB' }

stdout | src/memory.test.ts > Memory Usage > replay
{ totalEvents: 200, heapUsed: '42.41 MB', external: '1.70 GB' }

stdout | src/memory.test.ts > Memory Usage > replay
<empty line>
stderr | src/memory.test.ts > Memory Usage > replay
----stderr----
Aborted()
--------------
----stderr----
Aborted()
# After, the "external" heap is stable at ~32mb
stdout | src/memory.test.ts > Memory Usage > replay
{ totalEvents: 0, heapUsed: '-71456.00 B', external: '-1413.00 B' }

stdout | src/memory.test.ts > Memory Usage > replay
{ totalEvents: 50, heapUsed: '41.70 MB', external: '32.30 MB' }

stdout | src/memory.test.ts > Memory Usage > replay
{ totalEvents: 100, heapUsed: '43.79 MB', external: '32.49 MB' }

stdout | src/memory.test.ts > Memory Usage > replay
{ totalEvents: 150, heapUsed: '40.27 MB', external: '32.04 MB' }

stdout | src/memory.test.ts > Memory Usage > replay
{ totalEvents: 200, heapUsed: '47.41 MB', external: '32.27 MB' }

stdout | src/memory.test.ts > Memory Usage > replay
{ totalEvents: 250, heapUsed: '48.65 MB', external: '32.47 MB' }

stdout | src/memory.test.ts > Memory Usage > replay
{ totalEvents: 300, heapUsed: '49.43 MB', external: '32.16 MB' }
...

Changelog

  • Fixed a memory leak where Cody would sometimes use several GB of memory after long use.

We have a lot of bug reports related to high memory usage and bad
performance. This PR is an attempt to identify how we can fix these
problems by creating a minimized reproduction of real-world usage
scenarios.

See `memory.test.ts` for the reproduction. In short, we use new
record/replay infrastructure to replay events from a recording of my own
coding session yesterday. When replaying these events, it's clear that
the "external" memory usage of the agent process quickly grows up to
2gb due to tree-sitter parsing that we run on every document content
change. When we disable tree-sitter parsing, the "external" memory usage
stays stable below <1mb.
@olafurpg olafurpg force-pushed the olafurpg-cody-3547-high-memory-usage-by-cody-after-installation4 branch from ec3378f to f1acdf0 Compare September 5, 2024 11:34
We don't run this test in CI.
@olafurpg olafurpg marked this pull request as ready for review September 5, 2024 11:42
@olafurpg olafurpg changed the title Performance: investigate memory issues Performance: fix memory leak Sep 5, 2024
@olafurpg olafurpg requested a review from a team September 5, 2024 11:46
@olafurpg olafurpg enabled auto-merge (squash) September 5, 2024 11:50
Copy link
Contributor

@abeatrix abeatrix left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I dont really know how it works to give concrete feedback but the test results lgtm. Approve to unblock!

@olafurpg olafurpg merged commit e806f7c into main Sep 5, 2024
25 checks passed
@olafurpg olafurpg deleted the olafurpg-cody-3547-high-memory-usage-by-cody-after-installation4 branch September 5, 2024 12:28
@@ -8,6 +8,9 @@ import { type WrappedParser, createParser, getParser } from './parser'

const parseTreesPerFile = new LRUCache<string, Tree>({
max: 10,
// Important: we need to call `Tree.delete()` to free up memory. Without
// this, we leak memory. See CODY-3616.
disposeAfter: tree => tree.delete(),
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@abeatrix the short explanation of this fix is that tree-sitter trees are not garbage collected by default, they are stored on the Node.js "external" heap and we have to explicitly free them with .delete(). FYI @valerybugakov

Copy link
Member

@valerybugakov valerybugakov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants