JS中的内存占用
前言
思考下面的 r1 和 r2 谁占用的内存更多?
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)。Boolean、Undefined、Null:在数组中同样以标记指针形式存在,占用 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 语义——存对象引用会延长对象生命周期,存原始值则完全解耦。选择哪种写法取决于你是需要与源对象保持实时关联,还是只需要那一刻的快照值。