类型化数组与字符串的内存开销

时间:2017-08-21 18:49:29

标签: javascript memory v8 typed-arrays

我正在尝试减少javascript Web应用程序的内存使用量,该应用程序以大量小字符串的形式将大量信息存储在内存中。当我将代码更改为使用Uint8Array而不是String时,我注意到内存使用率上升了。

例如,请考虑以下创建许多小字符串的代码:

// (1000000 strings) x (10 characters)
var a=[];
for (let i=0; i<1000000; i++)
    a.push("a".repeat(10).toUpperCase());

如果您将其放入空白页并让内存使用量停留几秒钟,则会在Google Chrome上以 70 MiB 结算。另一方面,以下代码:

// (1000000 arrays) x (10 bytes)
var a=[];
for (let i=0; i<1000000; i++)
    a.push(new Uint8Array(10));

使用 233 MiB 的内存。没有任何代码的空页使用大约20 MiB。另一方面,如果我创建少量的大字符串/数组,差异会变小,如果单个字符串/数组有10000000个字符/条目,则内存使用情况几乎相同。

那么为什么类型化数组会有如此大的内存开销呢?

2 个答案:

答案 0 :(得分:12)

V8开发者在这里。您的结论是有道理的:如果将字符串中的字符与Uint8Array中的元素进行比较,则字符串将减少开销。 TypedArrays非常适合快速访问类型化元素;但是,拥有大量小型TypedArrays并不具有内存效率。

区别在于字符串和类型化数组的对象标头大小。

对于字符串,对象标题为:

  1. 隐藏类指针
  2. 散列
  3. 长度
  4. 有效载荷
  5. 其中有效负载向上舍入到指针大小对齐,在这种情况下为16个字节。

    对于Uint8Array,您需要以下内容:

    1. 隐藏类指针
    2. 属性指针(未使用)
    3. 元素指针(见下文)
    4. 数组缓冲区指针(见下文)
    5. 偏移到数组缓冲区
    6. 字节长度
    7. 进入数组缓冲区的视图长度
    8. 长度(用户可见)
    9. embedder field#1
    10. 嵌入字段#2

    11. 数组缓冲区:隐藏类指针

    12. 数组缓冲区:属性指针(未使用)
    13. 数组缓冲区:元素指针(见下文)
    14. 数组缓冲区:字节长度
    15. 数组缓冲区:后备存储
    16. 数组缓冲区:分配基础
    17. 数组缓冲区:分配长度
    18. 数组缓冲区:位字段(内部标志)
    19. 数组缓冲区:嵌入字段#1
    20. 数组缓冲区:嵌入字段#2

    21. 元素对象:隐藏类指针

    22. 元素对象:(支持商店的)长度
    23. 元素对象:基础指针(后备存储的)
    24. 元素对象:偏移到数据开始
    25. 元素对象:有效负载
    26. 其中,有效负载再次向上舍入到指针大小对齐,因此这里消耗16个字节。

      总之,每个字符串消耗5 * 8 = 40个字节,每个类型化数组消耗26 * 8 = 208个字节。这似乎是很多开销;原因是由于TypedArrays提供了各种灵活的选项(它们可以与ArrayBuffers重叠视图,可以直接从JavaScript分配,或与WebGL共享等等)。

      (它不是&#34;优化内存分配&#34;也不是&#34;更好地收集垃圾字符串&#34; - 因为你要抓住所有对象, GC没有发挥作用。)

答案 1 :(得分:1)

不应该以这种方式使用类型化数组。

如果您想要高内存效率,只需使用一个类型数组来保存所有整数。由于低级原因,不使用大量数组来保存整数。

这些低级别的原因与在内存中保存一个对象需要多少开销有关,而这个数量取决于不可变性和垃圾收集等几个方面。在这种情况下,保持一个类型化数组比保持一个简单字符串具有更高的开销。这就是为什么你应该只支付一次这个价格

你应该利用:

var a = [];                       for (let i=0; i<1000000; i++) a.push("1");
var b = new Uint8Array(10000000); for (let i=0; i<1000000; i++) a[i] = 1;
// 'b' is more memory efficient than 'a', just pay the price of Uint8Array one time
// and save the wasted memory in string allocation overhead