JS字符串串​​联激增了内存消耗

时间:2019-04-19 15:48:20

标签: javascript garbage-collection

我对代码进行了广泛的分析,直到发现以下代码在私有模式下在最新的Chrome版本上分配了超过1GB的RAM时,“数组”的大小约为33MB,大小并不重要,只有一个具有我正在运行测试的大小的文件。 我不知道如何在代码中生成如此大的Uint8Array供您测试,因此下面的代码无法按原样运行,但是也许您仍然可以理解它,并为此提供帮助。

    const bytesToString = function (array) {
      let uint8Array = new Uint8Array(array);
      let length = uint8Array.byteLength;

      let stringToEncode = "";

      for (let i = 0; i < length; i++) {
        stringToEncode += String.fromCharCode(uint8Array[i]);
      }

      return stringToEncode;
   }

取消注释“ for循环”时,运行我的代码时RAM消耗保持在同一水平,一旦激活“ for循环”,消耗将激增至1GB以上。当然,这在某些时候会出现GC,但我遇到了一个普遍的内存问题,由于过多的内存消耗,浏览器最终将崩溃,并且我试图找出此功能是否是问题。 我可以通过Chrome的性能分析器看到很多次被调用的GC,但我不知道Chrome的GC是如何工作的,因为您可以读取很多“次要GC”,有时甚至可以读到“主要GC”,我想知道“次要GC”是否真的不是在释放RAM,而是“收集”,而只是在稍后的时刻,“主要GC”才真正释放RAM。如果是这种情况,我想在调用此函数和“主要GC”之间,我的代码运行的东西也比平时需要更多的RAM,然后浏览器崩溃。如果是这种情况,那就是我的功能是否有更好的实现,或者我可以操纵GC吗?据我看,我看不懂。

2 个答案:

答案 0 :(得分:1)

JS中的字符串是不可变的,因此每次添加一个字符时,它都会创建一个新字符串,该字符串比上一个字符串长1个字符。直到完成所有操作,GC才会运行,因此您会被成千上万的各种长度的字符串所困扰。

您需要其他组合字符串的方法。在这种情况下,您的整个函数可以写为String.fromCharCode(...array)(尽管您实际上想从二进制数据中创建一个字符串,但应考虑使用TextDecoder来支持各种编码,但要注意的是在诸如Node.js的环境中不可用。)

更新String.fromCharCode似乎不适用于非常大的数组(任何函数的参数数量都有限制),因此您可以尝试映射该数组变成1个字符的字符串,然后将它们连接在一起:

Array.prototype.map.call(uint8Array, c => String.fromCharCode(c)).join("")

(请注意,使用Array.prototype.map代替uint8Array.map,因为后者会将您的结果截断为Uint8)

答案 1 :(得分:0)

我认为TextDecoder可能是正确的解决方案。但是,如果您坚持要求,也可以尝试创建一个Blob,然后从中读取内容。

let blob = new Blob([arrayBuffer], {type: 'application/octet-stream'});
let reader = new FileReader();
reader.onload = function (event) {
  console.log(event.target.result);
};
// Use if you want the UTF-8 encoded version
reader.readAsText(blob);
// Use if you for example need to use the result with "window.btoa" as it was in my case.
reader.readAsBinaryString(blob);