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

Memory Leak with shallowMount / mount #2041

Open
CaptainFalcoGX opened this issue Dec 8, 2022 · 5 comments
Open

Memory Leak with shallowMount / mount #2041

CaptainFalcoGX opened this issue Dec 8, 2022 · 5 comments

Comments

@CaptainFalcoGX
Copy link

CaptainFalcoGX commented Dec 8, 2022

Subject of the issue

Seeing a 2MB heap size increase after each test using shallowMount / mount.

Steps to reproduce

Run this command with the below setup:
./node_modules/.bin/jest -- "DummyTest-test.js"

Vue Version 2.7.10
Vue Test Utils Version 1.3.3
Jest Version 29.0.3
Node Version 14.20.0
vue/vue2-jest Version 28.1.0

DummyTest-test.js

import { shallowMount } from "@vue/test-utils";
import DummyComponent from "../DummyComponent.vue";

let iterations = [...Array(9999).keys()];
let wrapper;
let heap;

afterAll(() => {
  iterations = null;
});

afterEach(() => {
  wrapper.destroy();
  wrapper = null;
  heap = null;
});

it.each(iterations)(`Test`, () => {
  wrapper = shallowMount(DummyComponent);
  heap = (process.memoryUsage().heapUsed / 1024 / 1024).toFixed(2);
  console.log(`Heap Size: ${heap} MB`);
  expect(true).toBe(true);
});

DummyComponent.vue

<template>
  <div>
    <p>Hi</p>
  </div>
</template>

<script>
export default {
  name: "DummyComponent",
};
</script>

jest.config.js

module.exports = {
  testEnvironment: "jsdom",
  transform: {
    "^.+\\.js$": "babel-jest",
    "^.+\\.vue$": "@vue/vue2-jest",
  },
};

Expected behaviour

Heap Size should remain consistent after each iteration of the test.

Actual behaviour

Seeing a 2MB memory leak.

@CaptainFalcoGX CaptainFalcoGX changed the title Memory Leak with shallowMount Memory Leak with shallowMount / mount Dec 12, 2022
@CaptainFalcoGX
Copy link
Author

CaptainFalcoGX commented Dec 15, 2022

I have posted this same question on Stack Overflow.

Changing node versions (12, 16, 18) did not resolve the issue.

This is a Vue 2 project, so I cannot use vue-test-utils v2. However, I did a brief test on Vue 3 with Vue Test Utils v2, and it looked the issue was still present.

Looking for help on what to try next, or if others are experiencing the same issue.

To prevent CI from grinding to a halt, I'm using the workerIdleMemoryLimit option from the latest version of Jest as a band-aid.

@CaptainFalcoGX
Copy link
Author

If anyone else is experiencing this issue or can reproduce with the steps listed in the original post, that would be great to know. The project I'm working on has ~6000 tests and this memory leak is causing trouble.

@leo-coco
Copy link

leo-coco commented Jan 5, 2023

I am able to reproduce the issue

@lmiller1990
Copy link
Member

Uh oh... anyone up to dig into this and find the problem?

@luddwichr
Copy link

luddwichr commented Jul 18, 2023

Hey there,
I deleved a bit into this issue and I think I found a potential culprit. However, I don't know how to solve the issue ;-)

I can reproduce the issue by:

  • clone this repo (I use the actual code base so I can easily modify the code and observe how it affects the heap leak)
  • install dependencies
  • add a test file mount.leak.spec.js to test/specs with the following content:
import { compileToFunctions } from 'vue-template-compiler'
import {mount} from 'packages/test-utils/src'

describe('mount and destroy components endlessly', () => {
  it('should not run out of memory', () => {
    const compiled = compileToFunctions('<div></div>')
    while (true) {
      const wrapper = mount(compiled)
      wrapper.destroy()
    }
  })
})
  • run the test via node --max-old-space-size=200 ./node_modules/jest/bin/jest.js test/specs/mount.leak.spec.js --runInBand with 200 MB max heap to ensure the heap is not simply increasing because the GC does not need to do its work yet...
  • observe that the process crashes after a short time

After having failed to utilize node --inject + Chrome Dev Tools, I ended up with the following strategy:
Gradually re-implement mount by re-using the original code and observe whether the test panics or not.

This eventually led me to createStubFromComponent as source of the leak. Here, two things are unexpected:

  1. here Vue is directly modified, instead of _Vue and tagName is pushed to an array ignoreElements even if it is already ignored. Since transition and transition-group are default stubs, this will make the list huge if there are many mounts with many stubs... However, this did not cause my test to panic even after 3 min...
  2. here _Vue.extend(component) is called to get hold of its options. This method is invoked for each and every stub that is created. Changing this line to {} stops the memory leak.

So I don't really understand why calling _Vue.extend(component) causes the leak. And I don't know if there is another way of getting hold of the component props.

I hope that this might be of help for you to find a solution :-)

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

No branches or pull requests

5 participants