uda*_*486 16 testing memory-leaks out-of-memory jestjs
我正在 NodeJS api 上运行笑话单元和集成测试,我面临着一些可能存在内存泄漏的问题。我尝试将 Jest 从 26.3.2 升级到 27.5.1,但这并没有多大帮助。我从 Chrome 控制台拍摄了一些堆快照。
快照2
快照4
从上面的快照我可以看到使用量的增长非常高。但我无法理解出了什么问题。
我发现 String、Object 和 JSBufferData 出了问题。但不确定问题是什么。
库的字符串化版本的多个调用/行,但是它来自哪里以及为什么?
如果是对象:
屏幕截图中的对象可能来自一个库,我使用countries-list它来获取国家/地区列表以查找 ISO 名称。
最后是 JSBufferData,它指向类似 URLSearchParam 的内容,但我没有在应用程序中的任何位置使用上述任何对象/库:

我使用的堆栈:
NodeJS:16.14.2 笑话:27.5.1 笑话串行运行器:1.2.0
Ped*_*oDM 27
我和我的团队在使用 jest 29.x 和 Node 18.x 时遇到内存泄漏。测试套件运行时会出现内存泄漏,并在内存使用量为 2GB 时导致节点崩溃。增加分配给节点的 RAM 内存不是一个选择,因为测试套件会增加测试数量,最终导致失败,而且 2GB 已经太多了。
这个答案并不是问题的直接解决方案,但这使问题变得无害,并且我们花了很多时间来解决它。
您需要做的第一件事是在您的文件中设置此配置jest.config.js:
module.exports = {
...
workerIdleMemoryLimit: '512MB',
...
}
Run Code Online (Sandbox Code Playgroud)
我选择了 512MB,但选择你的值并留下一些空间,例如"test_suite_ram_startup + 10 * memory_leak_increase". 每当内存使用量达到此值时,jest 将重新启动工作程序并清除内存,对运行测试所需的总时间的影响可以忽略不计。
仅此一点本身就永远不会让您的测试套件崩溃,并且问题基本上已经解决了。
现在,与不导致节点崩溃一样重要的是使用合理数量的 RAM 来运行测试。从现在开始,每一项考虑都会减少测试套件完整运行中使用的 RAM 总量。
node --expose-gc --no-compilation-cache ./node_modules/jest-cli/bin/jest.js --logHeapUsage
Run Code Online (Sandbox Code Playgroud)
“logHeapUsage”将记录内存使用情况,以便您可以检查改进情况。
如果您使用 typescript,请使用 SWC 作为 JS 的转译器,而不是 tsjest,它更快,节省了我们大量的内存,因为转译过程是我们最消耗内存的过程。
afterAll(() => {
global.gc && global.gc()
})
Run Code Online (Sandbox Code Playgroud)
原则上这不是必要的,但不知何故,玩笑并没有做到应有的程度。使用它,我们减少了执行的每个测试套件的内存总量。
一些库(例如 aws-sdk v2)将尝试导入所有内容,即使您只使用其代码的一小部分。如果您查看库中导入发生的位置,您会发现所有内容都导入到单个文件中,而不是从该文件导入,所有无用的东西都位于您的内存中。要解决这个问题,只需跟踪您想要的资产所在的确切位置并直接从那里导入即可。对于 aws-sdk,您还可以使用 aws-sdk v3 来解决此问题。
有时我们不能仅仅替换库,但从类验证器更改为像 zod 这样的小型库会有所帮助。
在我们的案例中,将 Prisma ORM 从 4.10 更新到 5.0 创造了奇迹。新的 prisma 使我们的测试套件运行速度提高了 2.6 倍,并且使用的内存更少。
这样您就可以正确看待一些数字。我们的测试套件在启动时需要 1.1GB RAM,每个测试套件都会产生内存泄漏,导致内存总量增加 100MB。每个测试套件的运行时间为 15 - 10 秒。经过 10 次测试后,节点将崩溃。
最后,每个测试套件需要 2-3 秒才能运行。
我们从一个崩溃的测试运行(从 1.1GB RAM 使用开始,每个测试套件爬升 100MB)到一个永不崩溃的测试运行(由于我首先在玩笑配置中提到的解决方案),从 30MB 开始,每个测试套件爬升 14MB。执行的每个测试套件。如果达到 512MB,它会立即再次减少到 30MB,并开始再次缓慢爬升。而且一切都快得多。
我真的希望这能帮助任何为此苦苦挣扎的人。
您可以尝试使用 --logHeapUsage
来自文档:
每次测试后记录堆使用情况。对于调试内存泄漏很有用。在节点中与 --runInBand 和 --expose-gc 一起使用。
您可以尝试使用 --expose-gc 公开垃圾收集器并添加
afterAll(() => {
global.gc && global.gc()
})
Run Code Online (Sandbox Code Playgroud)
另一种选择是jest -w 1避免这些内存问题。
--maxWorkers
别名:-w。指定工作池为运行测试而生成的最大工作人员数量。在单运行模式下,这默认为计算机上可用的核心数减去主线程的一数。在监视模式下,这默认为机器上可用内核的一半,以确保 Jest 不引人注目并且不会让您的机器停止运行。在 CI 等资源有限的环境中调整此设置可能很有用,但默认值应该足以满足大多数用例。
对于具有可用可变 CPU 的环境,您可以使用基于百分比的配置: --maxWorkers=50%
参考资料:
在我的 Express.js Jest 测试中找不到内存泄漏
我的 Jests 测试正在泄漏内存,我该如何解决此问题?
在 Jest 中调试内存泄漏需要遵循哪些步骤?
https://chanind.github.io/javascript/2019/10/12/jest-tests-memory-leak.html