jest: jsdom or babel-polyfill cause memory leak (?)
Hello.
First of all thanks for this project, it’s been great so far. I’m running into a issue where when Jest can resolve babel-polyfill the node memory heap keeps on growing resulting in memory allocation errors in node 5 or extremely slow test in node 6 when node starts to reach the memory limit.
This very simple repository reproduce the issue https://github.com/quentin-/jest-test
describe('test', function() {
beforeAll(function() {
global.gc();
});
it('work', function() {
expect(1).toBe(1);
});
});
# heap size is trending upward.
# test become unbearably slow after a while
npm run test -- --logHeapUsage
node: 6.7.0
About this issue
- Original URL
- State: closed
- Created 8 years ago
- Reactions: 10
- Comments: 52 (8 by maintainers)
This was fixed accidentally with this commit https://github.com/facebook/jest/commit/8256904751d85e16909152ddaf8395d7c2b60eba and I have no idea why this works, seriously. Anybody have any ideas? Maybe it’s some kind of weird edge bug in Node.
It will land in Jest 19. We plan to release it in a week.
Some more specific data on the heap growth in the example repo, from running it on my machine.
The heap size after the first test is 45MB and it grows linearly with a slope of 0.48.
The heap size after the first test is 48MB and it grows linearly with a slope of 1.03.
Is that to be expected? This is 1MB growth on the heap for this test:
@thymikee I was facing the same memory issue and then very slow tests. I made a fresh install of
node_modules
and thenjest@next
- my tests went from ~750s to ~125s and memory was stable around 800mb per worker (previously hitting the limit of 1.45gb per worker)I apologize. I forgot to merge https://github.com/facebook/jest/pull/2755 into the release (honestly I thought I did), it will be part of the next release.
We upgraded to Jest 19 in the hopes that this memory leak would be fixed, but unfortunately that’s not the case. We’ve been using the
--runInBand
flag to run tests on individual folders to dodge this memory leak up until this point, but now (with Jest 19) the memory leak has resurfaced. You can see how the test times are snowballing here:As a result, Circle CI gives us this failure:
Is anyone else still plagued by this memory leak in Jest 19?
@thymikee any idea when this will be released? Also hitting issues with memory leaks on circleci and the setImmediate change appears to do the trick. Thanks for tracking it down!
Okay. We have this issue fixed in our project with a small workaround. As already said earlier, both issues (jsdom and babel-polyfill) have to be tackled in order to have a constant memory usage. These steps are necessary:
Following example of a setup file:
Now you can run jest with:
Profit!
In the jest project, following could be done to make the life easier:
I’m wondering: do we even need babel-polyfill in recent versions of Node? Shall we just not load it (except for regenerator, which we may still need).
I did research further. It seems that JSDOM is just a drop in an ocean. If I implement a fix for JSDOM locally, I still get huge memory leaks. However, as soon as I remove babel-polyfill, the leaks drop by 95%. IT seems that certain shims in corejs (the Object getters/setters, for example) are causing the leaks when combined with jest.
A solution would be disabling babel-polyfill and starting jest with
See my comment in tmpvar/jsdom#1682 : the memory leak in jsdom only happens when window.close() is called synchronously after jsdom.jsdom() I see two options here:
@thymikee I’ll give it a try.
Update: @next (19.1.0-alpha.eed82034), with
babel-polyfill
re-installed in node_modules (dependency of another package).node ./node_modules/.bin/jest
- Heap size growing, running slownode --expose-gc ./node_modules/.bin/jest --logHeapUsage [--runInBand]
- Heap size stable, running slowNote: “running slow” here means:
After removing
babel-polyfill
from node_modules:node ./node_modules/.bin/jest
(heap size growing)@mlanter @jedmao I encourage you to debug your tests with
--inspect
flag for Chrome Debugger and try to figure out where the leak may come from (it’s likely something in your tests). To simulate slow CI environment, you can throttle the CPU a bit:Still getting the memory leak on Jest 20.0.1, but it only happens in our Travis-CI environment. Any ideas on why that would be?
The same tests run in just under a minute on my local machine:
Hi there, let me add some observations from our project. I am trying to migrate our Mocha stack to Jest and I encountered similar problems.
I’ve tried removing
babel-polyfill
and adding theafterAll
hacks as @romansemko suggested, however, I have got the same results for all cases — around 7 extra MB in heap memory perdescribe
. It makes the heap grow over 1G after 1500 tests and crashes node forJavaScript heap out of memory
after a while. We have over 3k tests for now and the main problem with the leakage is that the tests take 25m to finish. Withmocha
it is under 3 minutes for comparison.Our test environment is quite complicated, we mock canvas rendering context in jsdom, we still have half of our codebase in
coffee-script
and we have large number of dependencies, any of them might have another leaks on their own.I realized that besides transforming
coffee-script
via transform config option I hadrequire('coffee-script/register')
in one of our setupFiles as well. So I removed it and tada 🎉 the memory usage dropped. So the main culprit in our case was globalcoffee-script/register
.We have some async-await stuff in our codebase so we need
babel-polyfill
. I added it to the env and I could see the rise of the heap again. This time it was around 50% of the original space filled withcoffee-script
+babel-polyfill
leaks. It is interesting that when I removedbabel-polyfill
only and keptcoffee-script
there were similar amounts of leaks as withcoffee-script
+babel-polyfill
. Butbabel-polyfill
itself had half of them too.Since we don’t really need the whole
babel-polyfill
I replaced it withregenerator-runtime/runtime
and I was able to get almost the same results as without it. I also tried Node 7 with--harmony --harmony-async-await
but I was not able to make it work, I was still getting errorsReferenceError: regeneratorRuntime is not defined
.Anyway, I think the best solution would be to solve this on
jest
level. Not injsdom
orbabel-polyfill
and not intestEnv
ortestRunner
(inafterAll
s). Currently we run our tests with Mocha and everything works (with possibly present hidden leaks) quite fast. I guess there has to be more thorough environment reset in Mocha. Yes, we can delete/unset more things indispose
method as @cpojer suggested, but it shouldn’t bejsdom
orbabel-polyfill
thinggie only, it should be something more radical as the leaks might be in variety of dependencies. Maybe it should be even higher up in the environment creation hierarchy? Could there be a particular reason, maybe a design decision, that leads to the leaks from the specs being leaked “globally” (copared toMocha
), @cpojer?Are you sure
jsdom.env
fixes the problem for you? I’ve changedjest-environment-jsdom
locally to use it once and didn’t see better results. Jest disposesjsdom
(callswindow.close()
) not in the same tick. also wrapping the close() method in setTimeout didn’t help.BTW, I appreciate your help on investigating that so much, thank you!
…unless you want to test with exactly the same setup as the “real” thing. The node and core.js implementations of the ES6/7 features could be different. 😉
Still, it’s better to get rid of it, instead of having OOM problems in bigger test suites. Or, at least, giving an option to disable auto-loading of babel-polyfill.