节点正则表达式解析器中的内存泄漏?

时间:2015-05-19 18:04:50

标签: javascript regex node.js memory-leaks

以下代码导致节点消耗大量内存并在内存不足时崩溃。但是,如果我将找到的字符串的长度从13更改为12,一切都很好。看起来正则表达式搜索返回的字符串包含对搜索到的原始字符串的隐藏引用。但是,只有找到的匹配长度至少为13个字符。 这是一个错误还是有某种充分理由可以解释这种行为?

function randString(length) {
  var a = "a".charCodeAt(0),
      result = [];
  for(var i = 0; i < length; i++) {
    result.push(a + Math.floor(Math.random() * 26));
  }
  return String.fromCharCode.apply(null, result);
}


var arr = [];

for(var i = 0; i < 1000000; i++) {
  if(i % 1000 === 0) console.log(i);
  var str = randString(13);
  str = randString(5000) + "<" +str + ">" + randString(5000);
  var re = /<([a-z]+)>/gm;
  var next = re.exec(str);
  arr.push(next[1]);
}

2 个答案:

答案 0 :(得分:2)

我在Chrome中观察到相同的行为。我认为这两个(node.js和Chrome)的行为相同,因为它们基于相同的Javascript引擎(V8)。

没有内存泄漏,但Javascript中的垃圾管理存在问题。我从观察中推断出这一点,当我在Google Dev Tools中强制进行垃圾收集时,释放了内存的Gbytes。

您可以强制运行垃圾收集器,如explained here。这样,你的node.js就不会崩溃。

修改

进一步测试我可以告诉他们:

关于你的评论但只要仍有对数组的引用,就不会释放内存。

它看起来比这更复杂,但你是对的,arr似乎占据了所有空间1.1去100'000项,这是每项10kB。当您查看数组next时,它确实具有大约10kB的大小(next.input的10015字节。如果所有工作都像预期的那样,next[1]将是一个简单的字符串并且仅使用slighly超过13个数据字节,但事实并非如此。在数组next[1]中引用arr不允许对next进行垃圾回收。

作为解决方案,我想出了这个经过修改的代码(fiddle):

function randString(length) {
  var a = "a".charCodeAt(0),
      result = [];
  for(var i = 0; i < length; i++) {
    result.push(a + Math.floor(Math.random() * 26));
  }
  return String.fromCharCode.apply(null, result);
}


var arr = [];

for(var i = 0; i < 100000; i++) {
  if(i % 1000 === 0) console.log(i);
  var str = randString(13);
  str = randString(5000) + "<" +str + ">" + randString(5000);
  var re = /<([a-z]+)>/gm;
  var next = re.exec(str);
  arr.push(next[1].split('').join(''));
}
console.log(arr)

诀窍是通过拆分字符串并再次连接来剪切nextarr中存储的字符串之间的引用。

我对内部结构一无所知,但它看起来像是V8中的一个错误。在Firefox上测试相同的内容,一切都按预期工作,并且没有过多的内存使用。

答案 1 :(得分:1)

我找到了问题的根源。这不是regexp解析器负责这个,而是字符串上的substring方法。它旨在使子串的创建更有效。 在V8错误报告页面上有一个关于此问题的公开问题。 https://code.google.com/p/v8/issues/detail?id=2869