ts-jest
ts-jest copied to clipboard
Module caching memory leak
š Bug Report
ts-jest
seems to cache compiled modules over and over again, creating a memory leak. There is a basline suite of tests using just jest
on main
To Reproduce
Steps to reproduce the behavior:
-
git clone [email protected]:willsoto/jest-repro.git
(note the branch ists-jest-memory-leak
) -
npm ci
- Run any of the test scripts to see memory usage increasing in a way that is indicative of a memory leak. You can run
npm run test:force-gc
to see memory usage when a garbage collection is forced.
Expected behavior
Modules should be cached once for the duration of the test run or allowed to be garbage collected after the test suite has completed.
Link to repo (highly encouraged)
https://github.com/willsoto/jest-repro/tree/ts-jest-memory-leak
Debug log:
This is the log from the full run, no forced garbage collection: npm test
.
āÆ TS_JEST_LOG=ts-jest.log npm run test
> [email protected] test jest-repro
> jest --no-cache --runInBand --logHeapUsage ./test
PASS test/with-nest/test10.spec.ts (79 MB heap size)
PASS test/with-nest/test11.spec.ts (97 MB heap size)
PASS test/with-nest/test12.spec.ts (125 MB heap size)
PASS test/with-nest/test1.spec.ts (150 MB heap size)
PASS test/with-nest/test2.spec.ts (176 MB heap size)
PASS test/with-nest/test3.spec.ts (94 MB heap size)
PASS test/with-nest/test4.spec.ts (120 MB heap size)
PASS test/with-nest/test7.spec.ts (145 MB heap size)
PASS test/with-nest/test5.spec.ts (170 MB heap size)
PASS test/with-nest/test6.spec.ts (195 MB heap size)
PASS test/with-nest/test8.spec.ts (220 MB heap size)
PASS test/with-nest/test9.spec.ts (245 MB heap size)
PASS test/force-gc/test12.spec.ts (256 MB heap size)
PASS test/force-gc/test10.spec.ts (277 MB heap size)
PASS test/force-gc/test11.spec.ts (288 MB heap size)
PASS test/force-gc/test7.spec.ts (297 MB heap size)
PASS test/force-gc/test4.spec.ts (306 MB heap size)
PASS test/force-gc/test6.spec.ts (328 MB heap size)
PASS test/force-gc/test1.spec.ts (338 MB heap size)
PASS test/force-gc/test5.spec.ts (347 MB heap size)
PASS test/force-gc/test9.spec.ts (356 MB heap size)
PASS test/force-gc/test8.spec.ts (178 MB heap size)
PASS test/force-gc/test3.spec.ts (188 MB heap size)
PASS test/force-gc/test2.spec.ts (210 MB heap size)
PASS test/without-nest/test10.spec.ts (219 MB heap size)
PASS test/without-nest/test11.spec.ts (228 MB heap size)
PASS test/without-nest/test12.spec.ts (250 MB heap size)
PASS test/without-nest/test1.spec.ts (259 MB heap size)
PASS test/without-nest/test2.spec.ts (268 MB heap size)
PASS test/without-nest/test3.spec.ts (277 MB heap size)
PASS test/without-nest/test4.spec.ts (299 MB heap size)
PASS test/without-nest/test7.spec.ts (308 MB heap size)
PASS test/without-nest/test5.spec.ts (317 MB heap size)
PASS test/without-nest/test6.spec.ts (338 MB heap size)
PASS test/without-nest/test8.spec.ts (348 MB heap size)
PASS test/without-nest/test9.spec.ts (357 MB heap size)
Test Suites: 36 passed, 36 total
Tests: 3600 passed, 3600 total
Snapshots: 0 total
Time: 10.716 s
Ran all test suites matching /.\/test/i.
ts-jest.log
``` {"context":{"allowJs":false,"logLevel":20,"namespace":"jest-preset","package":"ts-jest","version":"26.3.0"},"message":"creating jest presets not handling JavaScript files","sequence":1,"time":"2020-09-18T17:00:10.883Z"} {"context":{"logLevel":20,"namespace":"Importer","package":"ts-jest","version":"26.3.0"},"message":"creating Importer singleton","sequence":2,"time":"2020-09-18T17:00:12.088Z"} {"context":{"allowJs":false,"logLevel":20,"namespace":"jest-preset","package":"ts-jest","version":"26.3.0"},"message":"creating jest presets not handling JavaScript files","sequence":3,"time":"2020-09-18T17:00:12.092Z"} {"context":{"actualVersion":"26.4.2","expectedVersion":">=26 =3.8envinfo
System:
OS: macOS 10.15.6
CPU: (8) x64 Intel(R) Core(TM) i7-4980HQ CPU @ 2.80GHz
Binaries:
Node: 12.18.3 - ~/.volta/tools/image/node/12.18.3/bin/node
Yarn: 1.22.4 - ~/.volta/tools/image/yarn/1.22.4/bin/yarn
npm: 6.14.5 - ~/.volta/tools/image/npm/6.14.5/bin/npm
npmPackages:
jest: ^26.4.2 => 26.4.2
ts-jest: ^26.3.0 => 26.3.0
typescript: ^4.0.2 => 4.0.2
Additional screenshots of the heap


An interesting observation as I was exploring this was whether or not I was using a custom tsConfig
. I'm not sure if this has a meaningful impact on smaller apps, but for large apps the difference is substantial.
When I point ts-jest
at a custom tsconfig
with an include: []
(an idea I got from this repo), base memory usage goes down significantly but still climbs consistent with a memory leak. When I use whatever the default is, base memory usage increases significantly.
@ahnpnl I am more than willing to work on this issue since this is a big problem once there are hundreds of source files. We've seen heap usage climb to 1.5gb in some cases. If you can point me to possible culprits that would be great.
When I point
ts-jest
at a customtsconfig
with aninclude: []
(an idea I got from this repo), base memory usage goes down significantly but still climbs consistent with a memory leak. When I use whatever the default is, base memory usage increases significantly.
This has been mentioned in the doc https://kulshekhar.github.io/ts-jest/user/config/isolatedModules
The reason for that is: when specifying include: []
, TypeScript LanguageService
will do lazy fetch modules which will reduce overhead I/O at start up.
Feel free to pick up this one @willsoto :)
@ahnpnl can you point me to any possible places to start looking?
You can look into this one https://github.com/kulshekhar/ts-jest/blob/master/src/compiler/language-service.ts#L175 . This is the place where I/O overhead appears.
What would you suggest in such case for now, until investigation is happening? Downgrade to lower version? but then there is no solution right now for projects that started upgrading to typescript v4 ?
The workaround is following the doc https://kulshekhar.github.io/ts-jest/user/config/isolatedModules
If using isolatedModules: true
, you should combine with tsc
for type checking.
@ahnpnl That does not resolve the problem, it simply reduces growth but does not mitigate it entirely.
After looking into this all day yesterday, we managed to reduce the issue to jest + typescript. You can see a minimal reproduction here where we introduced a minimal transformer that just compiles the TS via transpileModule
. The memory leak still exists.
Our working assumption right now is that there is some negative interaction between Jest and TypeScript. Given Jest's history of memory leaks, we assume the fault is on Jest's side but are still working on a minimal reproduction.
Give this information, do you have any other thoughts on this @ahnpnl ?
If transpileModule
still produces memory leak, I also think the problem is more in Jest or TypeScript side. I think the issue might be more on Jest side.
Out of curiosity, have you tried with jest CLI flag runInBand
@willsoto ? If yes, does it produce the similar leak behavior ?
@ahnpnl You can see in the last screenshot that we are running with --runInBand
. All of our tests have been using the following flags for consistency: --no-cache --runInBand --logHeapUsage
I bet my cents on jest š
Reported with Jest here. I think this issue should remain open though in case others come looking, if that is okay?
Sure I think that is a good idea š
Is this being worked on? Is there anything we can do to help?
Please check https://github.com/facebook/jest/issues/10550 because it relies on that. Besides, you can also look at the current codebase of ts-jest
to see any suspicious points, here is the part using TypeScript Compiler API https://github.com/kulshekhar/ts-jest/blob/master/src/compiler/ts-compiler.ts#L72
I don't know if this will help or not but I was having issues with my tests failing in CI due to the heap size and "occasionally" failing on my local machine.
When running with --expose-gc and --logHeapSize I was finding inconsistent results with sometimes the memory ballooning to over 6gb but others times never breaking 300mb.
The scenario I have found to make recreate it on my tests 100% of the time is to run jest with --no-cache or --clearCache before running the tests. If the cache is enabled then (obviously I guess) it can make this issue not apparent because loading the modules from the cache does not run the transformer and this does not have this leak
@willsoto I don't think this is an issue with jest the more I look into it. I think it is ts-jest that is the issue. The reason being is if I switch my transformers from 'ts-jest' to 'babel-jest' my tests pass everytime. The memory never goes past 300mb on the suite.
To me this sounds like ts-jest is either responsible for the memory leak or the something in the transformer is crawling and transforming modules in a destructive manner
Any progress on this issue?
Currently no unfortunately :) for isolatedModules: true
nothing much to do for it. For isolatedModules: false
, there might be a way.
any process on it? I set max-old-memory to 24g. But still out of memory
@willsoto I don't think this is an issue with jest the more I look into it. I think it is ts-jest that is the issue. The reason being is if I switch my transformers from 'ts-jest' to 'babel-jest' my tests pass everytime. The memory never goes past 300mb on the suite.
To me this sounds like ts-jest is either responsible for the memory leak or the something in the transformer is crawling and transforming modules in a destructive manner
does babel-jest can compile ts? I like have a try
yes babel-jest
does support TypeScript. You just need to configure Babel to use TypeScript plugin and it will do the job. You can either use babel-jest
to process TypeScript or you can use isolatedModules: true
option from ts-jest
to disable type checking. Type checking is the most intensive part when compiling codes with TypeScript compiler.
Maybe support disable source map can ease this bug
That is possible, Iām curious how significant the improvement is when disabling source map.
There is a similar issue in jest https://github.com/facebook/jest/pull/8331, not sure that is the true reason.
@sinbargit you can now disable source map with ts-jest
27.0.0-next.12 and jest
27.0.0-next.9 by setting sourceMap: false
in your test tsconfig.
Adding this to jest.config.js worked for me.
globals: {
'ts-jest': {
isolatedModules: true
}
}
Adding this to jest.config.js worked for me.
globals: { 'ts-jest': { isolatedModules: true } }
I got an error **Make sure you are providing an explicit type for the xx xx ** after do that
@sinbargit you can now disable source map with
ts-jest
27.0.0-next.12 andjest
27.0.0-next.9 by settingsourceMap: false
in your test tsconfig.
@ahnpnl I tried this with jest 27.0.0-next.9 and got the following warning:
ā Validation Warning:
Unknown option "sourceMap" with value false was found.
This is probably a typing mistake. Fixing it will remove this message.
Configuration Documentation:
https://jestjs.io/docs/configuration
Can you pls open a separate issue with a repo for it?