transition 通过触发 hidden class 切换间接重构内存布局:in-object 存储满则转 properties 数组,乱序添加导致 map 分裂、字典模式降级不可逆,类型漂移还会退化数组 elements kind。

隐藏类 Transition 本身不直接重写对象内存,而是通过触发对象内部存储模式切换、改变属性存放位置和访问路径,间接重构内存布局。
Transition 触发对象从 in-object 切换到 properties 数组
新对象创建时,若构造函数一次性声明全部字段(如 class User { constructor(id, name) { this.id = id; this.name = name; } }),V8 会尝试将属性存入对象头后的固定槽位(in-object storage),访问只需一次指针偏移。
但每次新增属性(如先 obj.x = 1 再 obj.y = 2)都会生成新 Hidden Class,并检查当前 in-object 槽位是否已满。一旦用尽,后续属性就不再进 in-object,而是写入独立的 properties 数组(out-of-object storage)。这导致:
- 对象本体变小(只保留指针),但需额外分配数组内存
- 访问
obj.y多一次间接寻址(先读 properties 指针,再查数组索引) - 多个对象即使结构相同,若 Transition 路径不同,properties 数组可能无法共享内存页
乱序添加或条件分支导致 Map 分裂,加剧内存碎片
V8 的 Transition 链是单向且顺序敏感的。两个对象:
-
const a = {}; a.x = 1; a.y = 2;→ 走 C0 → C1 → C2 路径 -
const b = {}; b.y = 2; b.x = 1;→ 走 C0 → C1' → C2' 路径
即使最终都有 x 和 y,C2 和 C2' 是两个独立 Hidden Class。它们各自对应的 properties 数组布局、偏移计算逻辑互不兼容,无法复用内联缓存,也难以被 GC 合并回收,造成内存碎片上升。
delete 或 defineProperty 强制进入字典模式,彻底放弃紧凑布局
执行 delete obj.x 或 Object.defineProperty(obj, 'x', { writable: false }) 会中断 Transition 链:
- V8 放弃维护线性属性结构,将对象标记为“字典模式”(dictionary mode)
- 所有属性改用哈希表存储,每个键值对单独分配内存块
- 对象不再有固定偏移量,
obj.x变成 O(log n) 哈希查找,且无法利用 CPU 缓存局部性 - 该降级不可逆:即使再赋值
obj.x = 1,也不会回到 fast-property 模式
类型漂移间接影响元素种类,拖慢整体内存效率
Hidden Class 不记录属性值类型,但 V8 会为属性值类型建立反馈(feedback)。当同一属性反复赋不同类型(如 count = 1 → 'done' → null):
- 引擎标记该属性为多态(polymorphic),禁用内联缓存
- 若该属性所在对象还含数组,其 elements kind(如 PACKED_SMI_ELEMENTS)可能退化为 GENERIC_ELEMENTS
- 数组从连续整数内存块变成稀疏哈希映射,大幅增加每项内存开销和访问延迟











