1

我正在从 NodeJS 中的 GET URL API 中抓取一堆东西。我正在遍历一年中的几个月 X 一个城市。我有一个scrapeChunk() 函数,我为每个参数实例调用一次,即{startDate: ..., endDate: ..., location:...}。在里面,我对表格进行 jsdom 解析,转换为 CSV,将 CSV 附加到文件中。在所有嵌套的异步回调中,我最终使用下一个参数实例再次调用了 scrapeChunk 函数。

一切正常,但节点实例在 RAM 中不断增长,直到我收到“致命错误:CALL_AND_RETRY_2 分配失败 - 进程内存不足”错误。

我的问题:我做错了什么还是这是 JavaScript 和/或我正在使用的库的限制?我能否以某种方式完成每个任务,释放它的内存,然后开始下一个任务?我尝试了 FuturesJS 的一个序列,它似乎也遭受了同样的泄漏。

4

4 回答 4

4

可能发生的情况是您正在构建一个非常深的调用树,并且该树的上层保留对其数据的引用,因此垃圾收集器永远不会声明它。

要做的一件事是,在您自己的代码中,当您最后调用回调时,通过调用process.nextTick();. 这样,调用函数可以结束并释放其变量。此外,请确保您没有将所有数据堆积到一个全局结构中,从而使这些引用永远存在。

在没有看到代码的情况下,想出好的响应有点棘手。但这不是 node.js 或其方法的限制(有很多长期运行和复杂的应用程序都在使用它),而是如何使用它。

于 2013-06-04T23:00:32.237 回答
2

你可能想试试cheerio而不是JSDom。作者声称它更精简,速度提高了 8 倍。

于 2013-06-07T06:54:26.097 回答
1

假设您的描述是正确的,我认为问题的原因很明显 - 对 scrapeChunk() 的递归调用。使用循环调度任务(或查看节点的流设施),并确保它们实际返回。

这里发生的事情听起来像这样:

var list = [1, 2, 3, 4, ... ];
function scrapeCheck(index) {
  // allocate variables, do work, etc, etc
  scrapeCheck(index+1)
}

使用足够长的列表,您可以保证耗尽内存、堆栈深度、堆或任何数量的东西,具体取决于您在函数体期间执行的操作。我的建议是这样的:

var list = [1, 2, 3, 4, ... ];
list.forEach(function scrapeCheck(index) {
  // allocate variables, do work, etc, etc
  return;
});

令人沮丧的嵌套回调是一个正交问题,但我建议您查看异步库(特别是async/waterfall),它对于此类任务既流行又有用。

于 2013-06-04T23:17:07.663 回答
1

这与对您的函数的递归调用有关。将递归调用放在 a

setTimeout(()=>{
recursiveScrapFunHere();
}, 2000);

这样调用是异步的,并被放入优先级堆而不是通常的递归堆栈(同步调用就是这种情况)。

这样,您的父函数(相同的函数)将运行到最后,并且 recursiveScrapFunHere() 位于递归堆栈之外。

此处将在延迟 2 秒后进行呼叫。

于 2021-03-31T11:59:12.230 回答