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

Fix memory leak #137

Merged
merged 5 commits into from
Nov 2, 2018
Merged

Fix memory leak #137

merged 5 commits into from
Nov 2, 2018

Conversation

dylang
Copy link
Contributor

@dylang dylang commented Oct 26, 2018

graceful-fs is relied on by many major projects. Test frameworks, such as jest, both reset the module cache between tests to facilitate mocking, and rely on graceful-fs.

Because of this, graceful-fs is re-wrapping fs multiple times, causing ever-growing memory leaks. In a project with a a few thousand tests, this balloons to GB.

Three memory leaks were fixed in this PRz

  • fs.close was getting re-wrapped every time graceful-fs was loaded.
  • fs.closeSync was getting re-wrapped every time graceful-fs was loaded.
  • The same clone of fs generated by ./fs.js was getting re-wrapped every time graceful-fs was loaded.

These memory leaks show up when:

  • More than one version of graceful-fs is required.
  • The module cache is cleared, which happens in Jest and other test frameworks.
  • The impact of this leak is multiplied as thousands of modules rely on this package.

Note:
--enable-gc was added to the test runner so that we can monitor memory usage in one of the tests.

Fixes #102

Some related tickets:

@tmax
Copy link

tmax commented Oct 31, 2018

this is cool, but i have a noobish question. how do you test or "measure" the memory-leak-fix?

do you have a script to simulate typical usage and compare the expected memory usage vs actual?
any tools for doing that? interested to know how you'd solve this type of problem in general (tooling for fixing memory leaks).

@dylang
Copy link
Contributor Author

dylang commented Oct 31, 2018

Hi @tmax - great question!

If you look in test/avoid-memory-leak.js, you'll see we're using importFresh from the always amazing @sindresorhus to load the module.

Normally, node caches modules once they are required, but this library deletes the cache before loading so that we can reload it. This simulates what happens in frameworks like Jest that clear the module cache between tests.

First, we look at the memory usage with process.memoryUsage().heapUsed / Math.pow(1024, 2); The math is to convert it to Mb for easy reading.

Then we require "fresh" the module 4000 times, simulating 4000 unit tests.

We then call global.gc(); - this is an internal node command garbage collect. We normally don't need this, node will garbage collect as it has time, but in this
case we want to measure the memory impact right away.

Before this fix, garbage collecting had no effect because every copy of the module was still referenced by fs.

Finally, we look at the memory usage with process.memoryUsage().heapUsed / Math.pow(1024, 2) again, and compare to what we had before. A little growth is natural, so I made sure it's less than 2mb. Without the fix, the number can be in the hundreds of MB.

@isaacs isaacs merged commit 19c8372 into isaacs:master Nov 2, 2018
@isaacs
Copy link
Owner

isaacs commented Nov 2, 2018

Thanks! Landed on graceful-fs@4.1.12.

@@ -1,6 +1,8 @@
var fs = require('fs')
var polyfills = require('./polyfills.js')
var legacy = require('./legacy-streams.js')
var clone = require('./clone.js')

Choose a reason for hiding this comment

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

missing clone.js

@kirillgroshkov
Copy link

great work!

@dylang
Copy link
Contributor Author

dylang commented Nov 4, 2018

Thank you!

@addaleax
Copy link
Contributor

addaleax commented Dec 4, 2018

@dylang This doesn’t seem to resolve the issue completely on Node 11 … do you think you could take a look? We generally run graceful-fs’s tests as part of each Node.js release, so having them pass is really nice for us :)

@addaleax
Copy link
Contributor

ping @dylang ?

@DanielGibbsNZ
Copy link

This works fine on version 4.1.15 for fixing the memory leak, but when I upgrade to version 4.2.3 the memory leak comes back. Was a regression introduced with the changes in 4.2.x?

@dylanwulf
Copy link

I'm experiencing the same as @addaleax. I have this package locked at v4.1.15 but it's still causing memory leaks in node v11, v12, v13

@DanielGibbsNZ
Copy link

Using graceful-fs 4.2.4 on Node 12.19.1 seems to be working fine without any noticeable memory leak.

Copy link

@manjaneqx manjaneqx 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.

Memory leak when required repeatedly
9 participants