V8 Number类压缩

V8 Number类压缩

之前知道V8对堆地址和小整数(Smi)即Number类进行了压缩,只保存了低32bit的地址,但调试时就会发现高位地址并非全0,便好奇高位地址的作用是什么。

Smi就是指针是合法的整形。

arduino 复制代码
class Smi: public Object {
public:
    inline int32_t value(){
        return static_cast<int32_t>(reinterpret_cast<intptr_t>(this)) >> 1;
    }
​
    static Smi* fromInt(int32_t value){
        intptr_t tagged_value = (static_cast<intptr_t>(value) << 1) | 0;
        return reinterpret_cast<Smi*>(tagged_value);
    }
​
    static bool isSmi(Smi* ptr){
        return (reinterpret_cast<intptr_t>(ptr) & 1) == 0;
    }
};

原因

任何独特的操作都有其出现的原因。

1. 64位系统的内存利用率问题
  • 在64位系统中,原生指针占用8字节(64位),而JavaScript对象(如对象头、属性指针等)大量使用指针,导致内存浪费。

    • 有一个生活中比较能理解的例子,用过edge的朋友都知道,网页一开多内存的占用会达到非常夸张的巨大程度。
2. 堆内存范围的限制
  • V8将堆内存限制在 4GB或不到的连续虚拟地址空间 内。

    • 4GB的地址空间仅需32位((2^{32} = 4,294,967,296) 字节)即可覆盖,因此低32位足以表示堆内任何对象的偏移量。
3. 高32位的固定基址设计
  • 基址的高位固定 : V8在初始化堆时,分配一块连续内存区域,并确保其起始地址(基址)的高32位是固定的(如0x00001234)。
4. 压缩的收益
  • 内存节省: 将指针从8字节压缩为4字节,显著减少JavaScript对象的内存占用(如对象头、隐藏类、属性指针等),降低GC压力。
  • 性能提升: 更小的内存占用提高了CPU缓存命中率,减少内存带宽消耗,从而加速代码执行。

标记值(Tagged Values)的存储

  • Smi:

    • 64位架构下使用32位存储数值,低32位直接保存整数(符号扩展到64位)。
    • 标记位为最低位0,与指针区分。
  • 指针 :

    • 低32位存储从基地址开始的偏移量,标记位为01(区分强/弱引用)。
    • 高32位通过基地址隐式确定,无需存储。

所以高位地址的作用是什么?同样是32位内容的存储。

性能优化过程

  1. 分支优化 :发现有分支解压代码比无分支快7%(依赖CPU分支预测)。
  2. 消除冗余操作 :在TurboFan编译器中新增"解压消除"阶段,减少不必要的压缩/解压操作。
  3. 汇编优化 :直接加载并符号扩展32位值,减少指令数。
  4. 模式匹配修复 :调整优化阶段的匹配规则,恢复分配预配置等关键优化。
  5. Smi处理改进 :忽略Smi高32位,统一解压逻辑(无条件加基地址)。

回归源代码

v8/src/heap/heap-write-barrier.cc at main · v8/v8

scss 复制代码
#ifdef V8_COMPRESS_POINTERS  // 条件编译:仅在启用指针压缩时生效
​
// 获取当前线程的标记屏障(Marking Barrier)
// 用于追踪堆中对象的存活状态,确保垃圾回收的正确性
MarkingBarrier* marking_barrier = CurrentMarkingBarrier(host);
​
// 通过标记屏障获取当前Isolate实例(V8的隔离环境)
// IsolateForPointerCompression 是用于指针压缩的上下文封装
IsolateForPointerCompression isolate(marking_barrier->heap()->isolate());
​
// 获取C++堆指针表(管理外部C++对象的压缩指针)
CppHeapPointerTable& table = isolate.GetCppHeapPointerTable();
​
// 获取指针表对应的空间(Space是内存管理的基本单位)
CppHeapPointerTable::Space* space = isolate.GetCppHeapPointerTableSpace();
​
// 从插槽中以宽松内存顺序加载压缩后的外部指针句柄(32位偏移量)
// Relaxed_LoadHandle() 避免不必要的内存屏障开销
ExternalPointerHandle handle = slot.Relaxed_LoadHandle();
​
// 标记该外部指针为存活:
// 1. 通过基地址(隐含在Isolate中) + 偏移量(handle)解压指针
// 2. 在垃圾回收中标记对应的C++对象为存活
// 3. slot.address() 提供原始存储地址,用于记录或更新指针
table.Mark(space, handle, slot.address());
​
#endif  // V8_COMPRESS_POINTERS  // 结束指针压缩条件编译

包括ForRangeImpl 函数中获取页地址也是通过压缩后的地址获取的。

ini 复制代码
Tagged_t compressed_page = tagged_value & kPageMask;

效果

让V8拥有64位应用的性能,同时拥有32位的内存占用。

相关推荐
安全系统学习2 小时前
【网络安全】Mysql注入中锁机制
安全·web安全·网络安全·渗透测试·xss
深圳安锐科技有限公司6 小时前
深圳安锐科技发布国内首款4G 索力仪!让斜拉桥索力自动化监测更精准高效
运维·安全·自动化·自动化监测·人工监测·桥梁监测·索力监测
潘锦6 小时前
海量「免费」的 OPENAI KEY,你敢用吗?
安全·openai
冰橙子id6 小时前
linux系统安全
linux·安全·系统安全
上海锝秉工控8 小时前
防爆拉线位移传感器:工业安全的“隐形守护者”
大数据·人工智能·安全
你不知道我是谁?9 小时前
AI 应用于进攻性安全
人工智能·安全
薄荷椰果抹茶10 小时前
【网络安全基础】第一章---引言
安全·网络安全
zskj_zhyl11 小时前
智绅科技:以科技为翼,构建养老安全守护网
人工智能·科技·安全
zsq12 小时前
【网络与系统安全】域类实施模型DTE
网络·安全·系统安全
缘友一世14 小时前
网安系列【4】之OWASP与OWASP Top 10:Web安全入门指南
安全·web安全