Chrome docs说保留大小 是“一旦删除对象本身及其从 GC 根无法访问的依赖对象后释放的内存大小”,这很公平。然而,即使对于简单的对象,保留的大小通常是浅层大小的 3 倍。我知道 V8 需要存储对隐藏形状的引用,可能是一些用于 GC 的数据等,但有时对象有数百个额外的“保留”字节,当你需要拥有数百万个这样的对象时,这似乎是个问题。我们来看一个简单的例子:
class TestObject {
constructor( x, y, z ) {
this.x = x;
this.y = y;
this.z = z;
}
}
window.arr = [];
for ( let i = 0; i < 100000; i++ ) {
window.arr.push( new TestObject( Math.random(), Math.random(), Math.random() ) );
}
这是内存快照:
浅大小是 24 字节,这与我们存储 3 x 8 字节 double 的事实完全匹配。 “额外”大小为 36 字节,允许存储 9 x 4 字节指针(假设指针压缩已打开)。如果我们添加三个额外的属性,额外的大小将是 72 (!) 字节,所以它取决于属性的数量。那里存放着什么?是否有可能避免如此巨大的内存开销?
最佳答案
V8 开发人员在这里。
浅尺寸是对象本身,由标准对象头(3 个指针)和 3 个对象内属性组成,它们又是指针。那是 6 个(压缩的)指针,每个 4 个字节 = 24 个字节。
额外的保留大小是三个属性的存储。它们中的每一个都是一个“HeapNumber”,由一个 4 字节的映射指针和一个 8 字节的有效载荷组成。所以这是 3 个属性乘以 12 个字节 = 36 个字节。 (有了这些知识,如果再加上另外三个可能也是数字的属性,这应该是 72 的两倍。)
加起来,每个对象总共占用 24+36 = 60 个字节。
映射和原型(prototype)不计入每个对象的保留大小,因为它们由所有对象共享,因此释放一个对象不会允许它们也被释放。
节省内存的一个想法(如果您觉得它很重要)是“转置”您的数据组织:而不是 1 个数组包含 100,000 个对象,每个对象有 3 个数字,您可以有 1 个对象包含 3 个数组,每个数组有 100,000 个数字。根据您的用例,这可能是也可能不是一种可行的方法:如果三元组数字来来去去很多,那么将它们存储在一个巨大的数组中会令人不快;而如果它是一个静态数据集,那么两个模型在可用性上可能相当等效。如果这样做,您将避免重复的每个对象开销;此外,数组可以内联存储双数字(只要整个数组仅包含数字),因此您可以存储相同的 300K 数字,而总内存消耗仅为 2.4MB。
如果您尝试用许多小型 TypedArrays 替换 3 属性对象,您会发现内存使用量显着增加,因为 TypedArrays 每个对象的开销比简单对象大得多。它们旨在拥有一些大型阵列,而不是许多小型阵列。
关于javascript - Chrome 内存快照中的保留大小 - 究竟保留了什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62049063/