laverdet/node-fibers

Memory leak when using with Jest

pelotom opened this issue · 3 comments

Hi, I'm trying to debug memory leaks in my tests. With the following test:

require('fibers');
// ^ comment out this line to eliminate the leak

test('leak', () => {});

running jest --detectLeaks reports a leak:

  ● Test suite failed to run

    EXPERIMENTAL FEATURE!
    Your test suite is leaking memory. Please ensure all references are cleaned.

    There is a number of things that can leak memory:
      - Async operations that have not finished (e.g. fs.readFile).
      - Timers not properly mocked (e.g. setInterval, setTimeout).
      - Keeping references to the global scope.

As explained in this article this may not be a memory leak in the real program but just in the tests, due to the way that Jest unloads and reloads modules to ensure isolation between runs. Any idea what might be causing this or how I could work around it?

I've created a repro repo here: https://github.com/pelotom/fibers-jest-memory-leak.

By running npm test you can see that 3-4 MB are being leaked with every test:

 PASS  ./leak1.test.js (55 MB heap size)
 PASS  ./leak9.test.js (59 MB heap size)
 PASS  ./leak0.test.js (63 MB heap size)
 PASS  ./leak4.test.js (67 MB heap size)
 PASS  ./leak2.test.js (70 MB heap size)
 PASS  ./leak6.test.js (74 MB heap size)
 PASS  ./leak3.test.js (78 MB heap size)
 PASS  ./leak5.test.js (82 MB heap size)
 PASS  ./leak8.test.js (85 MB heap size)
 PASS  ./leak7.test.js (89 MB heap size)

If the require('fibers') line is commented out in includes.js, the memory stays level:

 PASS  ./leak1.test.js (57 MB heap size)
 PASS  ./leak7.test.js (57 MB heap size)
 PASS  ./leak5.test.js (57 MB heap size)
 PASS  ./leak6.test.js (57 MB heap size)
 PASS  ./leak3.test.js (57 MB heap size)
 PASS  ./leak2.test.js (57 MB heap size)
 PASS  ./leak9.test.js (57 MB heap size)
 PASS  ./leak4.test.js (57 MB heap size)
 PASS  ./leak8.test.js (57 MB heap size)
 PASS  ./leak0.test.js (58 MB heap size)

You can also run npm test --detectLeaks.

The article you linked has more information than I could give you. fibers is a native module so you can't unload it. You could try running node with node -r fibers which would preload fibers before jest and theoretically trick whatever tracking it's doing.

You could try running node with node -r fibers which would preload fibers before jest and theoretically trick whatever tracking it's doing.

This actually did the trick, cheers! 😄