为何给process.stdout的error事件绑定监听器会导致内存泄漏?
Jest检测到代码内存泄漏问题
以下代码被Jest检测出存在内存泄漏:
export const createNodeWriter = () => { process.stdout.on('error', () => {}); return () => {}; };
运行带内存泄漏检测的测试命令后,得到如下报错:
$ npm run test:jest > roarr@1.0.0 test:jest > jest --detectLeaks FAIL jestTest/memoryLeak.test.ts ● Test suite failed to run EXPERIMENTAL FEATURE! Your test suite is leaking memory. Please ensure all references are cleaned. There is a number of things that can leak memory: - Async operations that have not finished (e.g. fs.readFile). - Timers not properly mocked (e.g. setInterval, setTimeout). - Keeping references to the global scope. at onResult (node_modules/@jest/core/build/TestScheduler.js:150:18) at node_modules/@jest/core/build/TestScheduler.js:254:19 at Array.map (<anonymous>) Test Suites: 1 failed, 1 total Tests: 0 total Snapshots: 0 total Time: 0.942 s, estimated 1 s Ran all test suites.
我原本以为是函数多次调用导致重复绑定事件,但即使添加了仅绑定一次的防护逻辑,Jest仍然报内存泄漏:
let bound = false; export const createNodeWriter = () => { if (!bound) { bound = true; process.stdout.on('error', () => {}); } return () => {}; };
问题原因
process.stdout是Node.js的全局对象,一旦给它绑定事件监听器,这个监听器函数会被全局对象持续持有引用。Jest的内存泄漏检测会追踪这类全局引用——哪怕只绑定一次,测试结束后监听器也不会被自动移除,对应的内存无法被回收,最终触发泄漏告警。
解决方案
你可以通过以下两种方式解决这个问题:
1. 在代码中添加清理逻辑,测试结束时手动移除监听器
const errorHandler = () => {}; let bound = false; export const createNodeWriter = () => { if (!bound) { bound = true; process.stdout.on('error', errorHandler); } return () => {}; }; // 导出清理函数,供测试调用 export const cleanupNodeWriter = () => { if (bound) { process.stdout.off('error', errorHandler); bound = false; } };
在测试文件中调用清理:
import { createNodeWriter, cleanupNodeWriter } from '../your-file-path'; afterAll(() => { cleanupNodeWriter(); }); test('测试逻辑', () => { const writer = createNodeWriter(); // 你的测试代码 });
2. 在测试中Mock process.stdout,避免绑定真实监听器
jest.spyOn(process.stdout, 'on').mockImplementation(() => process.stdout); test('测试逻辑', () => { const writer = createNodeWriter(); // 你的测试代码 });
内容的提问来源于stack exchange,提问作者Gajus
相关产品推荐
相关产品推荐

