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

MaxListenersExceededWarning内存泄漏警告根源排查求助

排查Puppeteer批量检测机器人的MaxListenersExceededWarning问题

咱先把这个警告的本质说清楚:这是Node.js的保护机制,告诉你某个EventEmitter实例(比如Puppeteer的Browser、Page对象)上绑定的事件监听器数量超过了默认阈值(默认是10个)。虽然设置MaxListeners为0能屏蔽警告,但这相当于“掩耳盗铃”——放任不管可能会引发内存泄漏,甚至导致事件处理逻辑混乱,所以找到根源才是正道。

结合你提到的5个函数逻辑,我帮你梳理几个最可能的诱因和排查方向:

1. 重复绑定未清理的事件监听器

这是最常见的原因,尤其是在open_tab函数里:

  • 如果每次调用open_tab时,都给新创建的page对象绑定事件(比如page.on('error', ...)、page.on('response', ...)),但在页面关闭后没有移除这些监听器,或者用了page.on()而非page.once()(一次性监听器会触发后自动移除),循环次数多了,浏览器实例上的监听器就会累积超标。
  • 举个例子:如果你的open_tab里有这段代码:
    page.on('response', (res) => {
      // 检测消息的逻辑
    });
    
    每次打开标签页都加一个新的response监听器,10次之后就会触发警告。

2. 无限制的并发标签页

如果init函数里的循环没有做并发控制,一次性创建了10个以上的标签页,每个标签页都绑定了多个监听器,浏览器实例的总监听器数量会瞬间超过阈值。Node.js的默认限制就是为了防止这种无节制的资源占用。

3. 全局/重复绑定的浏览器监听器

如果你在init函数外或者每次启动浏览器时,都重复绑定browser对象的事件(比如browser.on('targetcreated', ...)),这些监听器会一直累积在浏览器实例上,次数多了也会触发警告。


具体排查和修复步骤

第一步:定位触发警告的具体对象

你已经用--trace-warnings拿到了调用栈,从栈里可以找到是哪个EventEmitter触发的(比如Page或者Browser)。也可以在代码里主动打印监听器数量辅助排查:

const { EventEmitter } = require('events');
// 在open_tab里创建page后加这行
console.log(`当前page的error监听器数量:${EventEmitter.listenerCount(page, 'error')}`);
// 或者看page绑定了哪些事件
console.log('page绑定的事件:', page.eventNames());

第二步:清理冗余监听器

  • 用一次性监听器:如果某个事件只需要监听一次(比如页面加载完成的domcontentloaded),把page.on()改成page.once(),触发后自动移除监听器。
  • 手动移除监听器:如果需要多次监听,在页面关闭前手动移除:
    // 先定义命名函数,不要用匿名函数
    function handleResponse(res) {
      // 处理逻辑
    }
    page.on('response', handleResponse);
    
    // 在page.close()前移除
    page.removeListener('response', handleResponse);
    await page.close();
    
  • 批量清理:如果不确定哪些监听器需要保留,可以在关闭页面时清理指定事件的所有监听器:
    page.removeAllListeners(['error', 'response', 'console']);
    await page.close();
    

第三步:控制并发量

用并发限制工具(比如p-limit)限制同时打开的标签页数量,比如最多同时开5个:

const pLimit = require('p-limit');
const limit = pLimit(5); // 限制并发数为5

// 在init函数里,把循环改成带限制的异步调用
const taskPromises = accounts.map(account => {
  return limit(async () => {
    await open_tab(browser, account, proxy);
  });
});

await Promise.all(taskPromises);

这样每个时刻的监听器数量会被控制在安全范围内,不会触发阈值警告。

第四步:检查全局监听器

如果有全局绑定的浏览器事件,确保只绑定一次,或者在关闭浏览器时移除:

// 只绑定一次,放在init函数外或者初始化时
browser.on('disconnected', () => {
  console.log('浏览器断开连接');
});

// 在程序结束时关闭浏览器并清理
await browser.close();

关于警告的严重性

这个警告本身不会直接导致程序崩溃,但它是一个明确的信号:你的代码存在未清理的资源(事件监听器),长期运行会导致内存泄漏——程序占用的内存会越来越高,最终可能导致性能下降甚至意外崩溃。所以强烈建议不要直接设置MaxListeners为0,而是按照上面的步骤排查修复。

内容的提问来源于stack exchange,提问作者max

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.13 09:18:03