You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何给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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 03:15:11