V8引擎对小整数(Smi)采用高位标记+低位存值的机器字优化,范围约−10.7亿至+10.7亿;数组需全为Smi且无空洞才启用PACKED_SMI_ELEMENTS模式,可通过%DebugPrint验证。

JavaScript 引擎(尤其是 V8)对小整数(Small Integer,简称 Smi)有深度内嵌优化:它不把这类整数包装成堆对象,而是直接用指针宽度的机器字(如 32 或 64 位)高位标记 + 低位存值,实现零分配、零GC、超快访问。这种优化只在满足严格条件时生效。识别它是否被启用,关键不是“看代码写得多漂亮”,而是观察运行时行为特征与约束边界。
看数组是否全由小整数构成
小整数在 V8 中指能用 31 位(32 位系统)或 32 位(64 位系统,实际可用 31 位有符号整数)精确表示的整数,即范围大致为 −2³⁰ 到 2³⁰−1(约 −10.7 亿 ~ +10.7 亿)。超出此范围的整数(如 2**31 或 Math.pow(2, 31))会触发堆分配,破坏 Smi 模式。
- ✅ 安全示例:
[1, -5, 0, 1073741823](最后一个是2³⁰−1) - ❌ 破坏示例:
[1, 1073741824](2³⁰已越界,转为 HeapNumber) - ⚠️ 注意:
1.0、0x1、1e0都是 Smi;但1.1、NaN、null、undefined会立即让整个数组退出 Smi 元素模式
查数组元素类型是否统一且无装箱痕迹
V8 对数组的元素类型做“隐藏类(hidden class)”推断。只要数组中所有索引位置都写入了 Smi,且没有插入非 Smi 值(包括空洞 arr[5] = undefined),引擎就会为其分配 PACKED_SMI_ELEMENTS 元素模式 —— 这是 Smi 优化生效的明确标志。
- 用 Chrome DevTools 的
%DebugPrint(arr)(需开启--allow-natives-syntax启动 Chrome)可直接查看: - 输出含
PackedSmiElements→ ✅ Smi 优化已激活 - 输出含
PackedElements(无 Smi)→ ❌ 元素已混入其他类型(如浮点) - 输出含
HoleySmiElements→ ⚠️ 存在空洞(例如arr[100] = 42后未填满前 100 位),虽仍为 Smi 类型,但访问效率下降
测内存与性能表现是否符合 Smi 特征
Smi 数组的核心优势是“零堆分配 + 高速随机访问”。可通过两个轻量测试验证:
-
内存无增长:连续向数组 push 小整数(如
for (let i = 0; i ),用 Performance.memory 或 DevTools Memory 面板观察,若 <code>totalJSHeapSize几乎不变 → 符合 Smi 分配特征 -
访问延迟极低:对百万级 Smi 数组执行
arr[i] | 0或简单加法,耗时应显著低于同长度但含浮点数的数组(差 2–5 倍)。注意关闭 JIT 优化干扰(临时加--noopt可比对)
避开常见破坏 Smi 优化的写法
很多看似无害的操作会悄悄让数组降级:
- 写入
arr[i] = i * 1.0→ 引入浮点,触发PackedDoubleElements - 写入
arr.length = 0后再 push → 可能重置元素模式,尤其在旧版 V8 中易退化为字典模式 - 使用
arr['key'] = val或arr.foo = 'bar'→ 添加非索引属性,可能影响隐藏类稳定性 - 从 JSON 解析得到的数组(如
JSON.parse('[1,2,3]'))→ 初始为 Smi,但若后续混入非整数则立即失效


















