我正在尝试减少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个字符/条目,则内存使用情况几乎相同。
那么为什么类型化数组会有如此大的内存开销呢?
答案 0 :(得分:12)
V8开发者在这里。您的结论是有道理的:如果将字符串中的字符与Uint8Array中的元素进行比较,则字符串将减少开销。 TypedArrays非常适合快速访问类型化元素;但是,拥有大量小型TypedArrays并不具有内存效率。
区别在于字符串和类型化数组的对象标头大小。
对于字符串,对象标题为:
其中有效负载向上舍入到指针大小对齐,在这种情况下为16个字节。
对于Uint8Array,您需要以下内容:
嵌入字段#2
数组缓冲区:隐藏类指针
数组缓冲区:嵌入字段#2
元素对象:隐藏类指针
其中,有效负载再次向上舍入到指针大小对齐,因此这里消耗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