Skip to content
620
2 minutes

JS中的内存占用

前言

思考下面的 r1r2 谁占用的内存更多?

ts
const o = {id: 1}

const r1 = [o]
const r2 = [o.id]

JS不同类型的内存占用

在 V8 引擎(Chrome / Node.js)中:

基本类型

  • Number:在数组中作为元素存储时,Smi(小整数)模式下占用 4 字节(存储在栈/数组内部),HeapNumber 模式下占用对象头 + 8 字节数据。
  • String:可变,取决于长度和编码(UTF-8 1~4字节/字符,UTF-16 2字节/字符,且 JS 内部是 UTF-16)。
  • BooleanUndefinedNull:在数组中同样以标记指针形式存在,占用 4 字节(指针压缩模式下)。

引用类型

  • Object:至少占用对象头(Map 指针 + 属性存储指针),在指针压缩模式下为 8 字节头,加上内联属性空间。
  • Array:本质是特殊 Object,额外有 length 等内部字段。元素存储方式取决于元素类型(Smi 数组、Double 数组、对象指针数组)。
  • 引用本身:在 64 位指针压缩模式下为 4 字节,非压缩模式下 8 字节。

分析

ts
const o = {id: 1}

const r1 = [o]      // 数组中存的是 o 的引用(4 字节)
const r2 = [o.id]    // 数组中存的是 Smi 值 1(4 字节)

如果只看数组内元素,r1 存的是引用(指针压缩下 4 字节),r2 存的也是 Smi(4 字节),单从"数组元素"角度来看占用相同。

但 r1 引用了对象 o,只要 r1 存在,o 就无法被 GC 回收。o 本身还占据内存(16+ 字节)。因此:

  • r1 方案总体内存占用更高,因为还额外持有一个对象 o 的引用 → o 必须存活。
  • 如果 o 已经被其他地方引用而始终在内存中,那 r1 和 r2 的元素占用差别不大,但 r1 多了一层引用追踪。

更关键的区别

r1 存对象引用r2 存原始值
修改 o.id 后能通过 r1[0] 看到变化
o 可以被 GC(无其他引用时)❌(r1 持有引用)
序列化(JSON)能还原完整结构只能还原值

结论

这道题粗看会去比"指针 vs Number 几字节",但实际更有意义的是所有权和 GC 语义——存对象引用会延长对象生命周期,存原始值则完全解耦。选择哪种写法取决于你是需要与源对象保持实时关联,还是只需要那一刻的快照值。