在处理大型数组时,如何“帮助”节点的垃圾收集?

时间:2013-11-27 19:51:50

标签: node.js memory coffeescript garbage-collection v8

我有大约5个非常大的csv文件,我需要解析,munge和插入数据库。代码看起来大致如下:

i = 0 

processFile = (linecount, file, onDone) ->
  # process the csv as a stream 
  # NOTE: **this is where the large array gets declared**
  # insert every relevant line into an array 
  # process the array and insert it into the db (about 5k records at a time) 
  # call onDone when db insert is done 

getLinesAndProcess = (i, onDone) ->
  inputFile = inputFiles[i]
  if inputFile?
    getFileSizeAndProcess =  -> # this helps the GC
      puts = (error, stdout, stderr) ->
        totalLines = stdout.split(" ")[0]
        processFile(totalLines, inputFile, ->
          getLinesAndProcess(++i)
        )
        console.log "processing: #{inputFile}"
      exec "wc -l '#{inputFile}'", puts

    setTimeout(getFileSizeAndProcess, 5000)

getLinesAndProcess(i, ->
  # close db connection, exit process and so on
)

首百万行正常,大约需要3分钟。然后它会在下一条记录上进行分块 - 直到节点达到其内存限制(1.4GB),然后才会抓取。最可能的事情是v8的GC没有清理recordsToInsert数组,即使它已经完成了一个闭包。

我的短期解决方案是一次只运行一个文件。这很好,它可以工作等等,但我仍然坚持要解决多文件问题。我已经尝试了-–max-old-space-size=8192 fix from caustik's blog,但它没有帮助 - 节点仍然停留在1.4GB。我根据另一个SO帖子中的建议添加了setTimeout。它似乎没有帮助。

最后,我只需要在调用回调之前将数组设置回空数组。这工作正常,但感觉就像v8的GC在这里让我失望。

在处理大型数组时,我能做些什么来让v8变得更聪明吗?

1 个答案:

答案 0 :(得分:0)

关闭变量看起来就像这样,因为从语法上来说,它们看起来像就像存在于堆栈中的局部变量一样。

然而,他们非常非常不同。

如果您有这样的对象:

function Closure() {
    this.totalLines = [];
    this.otherVariable = 3;
}

var object = new Closure();

大多数人都明白只要totalLines存在,object就永远不会被垃圾收集。如果在完成对象之前很久就完成了totalLines,那么他们会将它分配给null或者至少知道垃圾收集器无法收集它。

然而,当涉及到实际的javascript闭包时,它的工作方式完全相同,但人们发现奇怪的是,他们必须将闭合的变量显式设置为null,因为语法欺骗了它们。