jest: JavaScript heap out of memory after upgrade to Jest 26

šŸ› Bug Report

I upgraded from 24.X to 26.0.0 but now test that was passing is not Running test takes long time to complete then I get this error image

To Reproduce

My test:

  describe('when item ids are in sessionStorage', () => {
    const itemIds = [333, 222, 111];

    beforeEach(() => {
      parseLocationToQueries.mockImplementation(() => ({
        queue_id: testQueueId
      }));
      isAdHocReviewByItemId.mockReturnValue(false);
      isAdHocReviewByObjId.mockReturnValue(false);
      setItemsToBeReviewed(itemIds);
    });

    it('initial fetch', () => {
      const wrapper = tf.render();
      expect(wrapper.state('itemIds')).toEqual([]);
      expect(axios.post).toBeCalledWith('/review/items', { item_ids: itemIds });
    });

    it('fetch more while no more', () => {
      const wrapper = tf.render();
      axios.post.mockClear();
      wrapper.instance().fetchMoreItems();
      expect(axios.post).not.toBeCalled();
    });

    it('fetch more while more', () => {
      const wrapper = tf.render();
      axios.post.mockClear();
      wrapper.setState({ itemIds: [555] });
      wrapper.instance().fetchMoreItems();
      expect(axios.post).toBeCalledWith('/review/items', { item_ids: [555] });
    });
  });

code:

export function setItemsToBeReviewed(itemIds) {
  sessionStorage.setItem(ITEMS_TO_BE_REVIEWED_KEY, JSON.stringify(itemIds));
}


  fetchMoreItems = () => {
    this.setState({ loadingMoreItems: true });
    return this.fetchItems(true)
      .then(res => {
        this.loadData(res.data);
      })
      .catch(error => {
        console.log('FetchmoreError', error);
      });
  };

  fetchItems = (excludeAssigned: boolean = false) => {
    let request;
    if (this.state.itemIds) {
      request = this.fetchItemsByIds();
    } else {
      request = this.fetchItemsFIFO(excludeAssigned);
    }
    return request;
  };

  fetchItemsFIFO = (excludeAssigned: boolean = false) => {
    const { isAlignment, queueIdFromURL } = this.state;
    const url = '/review/assign';
    const params = {
      alignment: isAlignment,
      queue_id: queueIdFromURL,
      exclude_assigned: excludeAssigned
    };
    return axios.get<any>(url, { params });
  };

  fetchItemsByIds = () => {
    if (_.isEmpty(this.state.itemIds)) {
      return Promise.resolve({ data: [] });
    }
    const url = '/review/items';
    const data = {
      item_ids: _.slice(this.state.itemIds, 0, FETCH_BATCH_SIZE)
    };
    this.setState(state => ({
      itemIds: _.slice(state.itemIds, FETCH_BATCH_SIZE)
    }));
    return axios.post<any, any>(url, data);
  };

jest.config:

module.exports = {
  timers: 'fake',
  moduleDirectories: ['node_modules'],
  moduleFileExtensions: ['js', 'jsx'],
  moduleNameMapper: {
    '\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$':
      '<rootDir>/__mocks__/fileMock.js',
    '\\.(css|less)$': '<rootDir>/__mocks__/styleMock.js',
    '^Root(.*)$': '<rootDir>$1',
    '^Utils(.*)$': '<rootDir>/src/utils$1',
    '^Hoc(.*)$': '<rootDir>/src/hoc$1',
    '^Components(.*)$': '<rootDir>/src/components$1'
  },
  testRegex: 'test\\.jsx?$',
  testURL: 'http://localhost:3000',
  collectCoverageFrom: [
    'src/**/*.js',
    'src/**/*.jsx',
    '!**/node_modules/**',
    '!src/components/bulk_review/columns/**',
    '!src/components/v2/**'
  ],
  coverageReporters: ['html', 'text'],
  coverageThreshold: {
    global: {
      branches: 90,
      functions: 90,
      lines: 90,
      statements: 90
    }
  },
  coverageDirectory: 'coverage',
  snapshotSerializers: ['enzyme-to-json/serializer'],
  testEnvironment: '<rootDir>/jest-environment.js',
  setupFilesAfterEnv: ['<rootDir>/enzyme.setup.js'],
  setupFiles: [
    '<rootDir>/__mocks__/localStorageMock.js',
    '<rootDir>/__mocks__/consoleMock.js'
  ],
  globals: {
    ENVIRONMENT: 'TESTING'
  },
  testPathIgnorePatterns: ['<rootDir>/src/components/v2'],
  reporters: [
    'default',
    [
      'jest-html-reporter',
      {
        pageTitle: 'Test Report',
        statusIgnoreFilter: 'passed',
        includeFailureMsg: 'true'
      }
    ]
  ]
};

envinfo

System: OS: Linux 4.15 Ubuntu 18.04.4 LTS (Bionic Beaver) CPU: (36) x64 IntelĀ® XeonĀ® Platinum 8124M CPU @ 3.00GHz Binaries: Node: 14.1.0 - ~/.nvm/versions/node/v14.1.0/bin/node Yarn: 1.22.4 - /usr/bin/yarn npm: 6.14.4 - ~/.nvm/versions/node/v14.1.0/bin/npm npmPackages: jest: ^26.0.0 => 26.0.0

About this issue

  • Original URL
  • State: open
  • Created 4 years ago
  • Reactions: 59
  • Comments: 62 (6 by maintainers)

Commits related to this issue

Most upvoted comments

I had the same problem (also while collecting coverage data, on GitHub Actions/CI) and fixed it by limiting the number of workers:

  • maxWorkers: 2 in jest.config.js
  • or --w 2 as a command line parameter.

Trying to migrate from 26.6.3 to 27.2.5 and got the same issue.

After doing some research, it seems this memory leak has been an ongoing issue since 2019 (Jest 22) so wanted to consolidate some notes here for posterity. Past issues have been related to graceful-fs and I think some have solved it via a hack/workaround that removes graceful-fs and then re-adds graceful-js after running jest. One troubleshooting thread was looking at compileFunction in the vm package as a potential cause. It seems that jest, webpack-dev-server, babel, and create-react-app are using graceful-js as a dependency. The memory leak issue was supposed to be fixed in a newer release of Jest but there may have been a regression since it is popping up again. I can confirm everything was working fine until a substantial amount of Jest tests were created in our environment and then the heap overflows on our CI machine after the heap size grows larger than the allocated memory due to the leak. Iā€™ve tried using 1 worker, runInBand, etc. without success.

The common cause of the issues Iā€™ve seen is collecting coverage via collecting coverage and graceful-fs. I havenā€™t done an in-depth analysis of those issues but seeing that they are both filesystem-related and having solved my own issue which was related to file imports I suspect they are some version of the same issue I was having.

Wanted to provide the solution I found so others may reap benefits:

The cause:

Using imports of the format import * from 'whatever'

The solution:

Using the format import { whatINeed } from 'whatever' instead dramatically reduced the memory accumulation

i was running into this issue and it appears that upgrading to jest 27 fixed it

@pedrohamarques

Iā€™m facing the same problem. Did you solve it?

Yes, by migrating to mocha + chai + sinon šŸ˜ƒ

Updating my jest.config.ts with coverageProvider: 'v8' and maxWorkers: 2 did the trick for me!

I was facing the same error on GitHub actions. I was able to pin down the problem in my case to the currently experimental ESM support #9430.

For comparison here are two profiling screenshots (I followed this article for instructions):

CJS (leaked ~20mb)

CleanShot 2021-06-23 at 19 36 59

ESM (leaked ~1gb šŸ˜± )

CleanShot 2021-06-23 at 19 39 12

I had a similar problem where I used to run into Out of Memory error when Jest started to do coverage on ā€œuntested filesā€. Using v8 as coverage provider solved the issue for me. However, its an experimental feature (as per documentation) - https://jestjs.io/blog/2020/01/21/jest-25#v8-code-coverage

I suspect that this issue might be related to the way that the objects that are being compared are being printed. Filed an issue with a minimal reproduction here: https://github.com/facebook/jest/issues/12364

In effect, Jest will stop short if itā€™s trying to print a very deeply nested object, but doesnā€™t account for ā€œtotal nodesā€ so for objects that are fairly shallow but very wide (such as many React/Enzyme elements) it will try to print the whole object and will run out of memory while constructing the string.

I had what I thought may be a similar error.

Using --coverage while running inside a container like the node:alpine docker image was a problem for me. I really didnā€™t need to be running tests in a container so maybe my comment/solution here https://github.com/facebook/jest/issues/5837#issuecomment-1002239562 may help someone.

I had the same problem (also while collecting coverage data, on GitHub Actions/CI) and fixed it by limiting the number of workers:

  • maxWorkers: 2 in jest.config.js
  • or --w 2 as a command line parameter.

This solved my issue: https://stackoverflow.com/a/68307839/9331978

I was having a similar issue where random tests would timeout. I added three arguments to my jest.config.js file and it seems to have helped. Seems to have been happening on both windows, mac, and in our gitlab pipeline.

testTimeout: 10000, maxConcurrency: 3, maxWorkers: ā€˜50%ā€™,

Surprisingly the tests execute faster as well. By ~15-20%.

We see this regularly on our tests ā€“ the first one succeeds then the second fails.

+1 @alexfromapex solution did not worked for me.

Jest 26.6.3 Node 14.15.4

Dump: https://pastebin.com/Mfwi2iiA

It happens after some re-runs on any CI server (my runners are docker containers). Always after a fresh boot it works normally, and after some runs, it breaks again, only comming back after a new reboot. I tried with 1GB RAM and 2GB RAM machines, same result. It seems not happening with 8GB+ RAM hardware (my local machine).

Some other info Iā€™ve gathered, it happens always after ~5m running, everytime the test log has the same size (it might be happening at same spot).

Not sure if it is related. But I get heap leak for simple expect:

  let items = tree.root.findAllByProps({ testID: 'CrewItem.Employee' })

  expect(items).toHaveLength(8) // stacked and throws leak in 30-60 seconds
  expect(items.length).toEqual(8) // works ok

Clearing cache doesnā€™t help

We will need a repro that can be downloaded and analyzed. Also, please make sure to clear cache just in case, e.g with jest --clear-cache

For me this happened only on CI. Turns out it was because on CI the default is runInBand, so adding this locally helped me replicate the issue on a local machine. For me it happened when an exception was thrown inside the callback given to a useLayoutEffect(). It just ran for ever, consuming more and more memory. Hope this helps someone in this thread šŸ™

Have no idea why this worked for me but I had accidentally removed 'js' from moduleFileExtensions in my jest.config.ts and then thatā€™s when my heap issues started going wild. But when I added it back, my heap issues went away.

So now I have moduleFileExtensions: ['js', 'ts']. Hopefully this helps someone!

We see this regularly on our tests at https://github.com/renovatebot/renovate

I tried a few different solutions that didnā€™t work for me:

  • increasing --max-old-space-size
  • using the node flag: node --no-compilation-cache
  • use --runInBand
  • use --expose-gc
  • limiting the number of workers

What did work for me:

Limiting the idle memory per worker using the flag workerIdleMemoryLimit

Iā€™m also limiting the number of workers so maybe it was a combination of the solutions.

I also had this issue, and I had to change my collectCoverageFrom array.

Before I had:

"collectCoverageFrom": [ "**/*.{js,jsx}", "!**/node_modules/**", ... ] But for some reason it seemed to still be running over the node_modules folder. I changed this by taking away the exclude node_modules and changing my patterns to include just the directories I needed:

"collectCoverageFrom": [ "<rootDir>/*.js", "<rootDir>/components/**", I donā€™t like having to do this, because it may be easy to miss future coverage, but all attempts to exclude the node_modules folder either had other errors or reintroduced the ā€œout of memoryā€ issue.

I have very similar issue,

My test:

const first = [ div_obj, p_obj, a_obj ]; // array with three DOM elements
const second = [ div_obj, p_obj, a_obj ]; // array with same DOM elements
second.push( pre_obj ); // add new obj

expect(first).toEqual(second); // compare two arrays one 3 elements other 4 elements

test should fail within 250 ms (timeout), but it takes 40 sec and it spits out message:

FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory
...
<--- JS stacktrace --->

==== JS stack trace =========================================

    0: ExitFrame [pc: 0x55e78ec781b9]
Security context: 0x086b49dc08d1 <JSObject>
    1: toString [0x2cb560879521](this=0x1a95f0796eb1 <Object map = 0x2b3cdd5e4839>)
    2: printComplexValue(aka printComplexValue) [0x329610605059] [/home/joe/../node_modules/pretty-format/build/index.js:~198] [pc=0x1413a8b0e5ac](this=0x059db4cc04b1 <undefined>,0x1a95f0796eb1 <Object map = 0x2b3cdd5...

Somehow I believe stack trace points to printComplexValue. I also tried toMatchObject, but exactly the same.

Jest: v26.6.3

Oh --clear-cache fixed it.

Same problem in Node 16.20.2 and Jest 29.7.0

What did not work for me:

  • Clearing cache
  • Limiting maxWorkers to 2 or to 1, or runInBand
  • Setting workerIdleMemoryLimit to 1 GB, to 50%, or to 0.2
  • Changing Node version is not an option.

This worked:

  • Setting NODE_OPTIONS max_old_space_size before running jest:
export NODE_OPTIONS=--max_old_space_size=8192
jest

or

NODE_OPTIONS='--max_old_space_size=8192' jest

I ran into the issue with Node 16.19.0 and Jest 29.7.0.

What did not work for me:

What did work for me:

  • Setting workerIdleMemoryLimit to 1 GB

I tried in on pure Windows and Mac and itā€™s basically the same behaviour. The single test, which virtually does nothing, takes up about 2.5 GB of RAM. For some reasons thatā€™s too much for my WSL, in PS and Mac the max heap seems to be higher for some reason. With --max-old-space-size=4096 everything is ā€œfineā€, it just take 2.5 GB of RAM. But ofc, thatā€™s an absurd amount.
In Windows, jest launches as many workers as I have virtual cores (I think), but each one takes up at least 500 MB, even though thereā€™s nothing to do for them (I run 1 test).

Maybe related: https://github.com/jestjs/jest/issues/11956

My stripped-down sample only seems to use aroud 350MB:

[130094:0x71dd830] 3764 ms: Scavenge 389.2 (415.0) -> 381.8 (418.8) MB, 3.80 / 0.00 ms (average mu = 0.973, current mu = 0.975) task;

It does the same thing, thereā€™s just less code not being executed.

Specifying NODE_OPTIONS=ā€œā€“max-old-space-size=2048ā€ helped us to solve the issue

I can prevent this error by using fake timers:

jest.test.setup.js:

jest.useFakeTimers();

i have the same issue

Same issue here as well. (using ts-jest)