垃圾回收:Scavenge、Mark-Sweep-Compact 与增量优化
JavaScript 的内存管理是自动的——代码里没有 malloc 和 free。但"自动"不意味着"没有代价"。V8 的垃圾回收(GC)在清理内存时会占用主线程,产生停顿。一次严重的 GC 停顿可以让页面卡顿 100ms 以上。理解 V8 的 GC 策略,不只是知识储备,直…
// 共 17 篇文章
JavaScript 的内存管理是自动的——代码里没有 malloc 和 free。但"自动"不意味着"没有代价"。V8 的垃圾回收(GC)在清理内存时会占用主线程,产生停顿。一次严重的 GC 停顿可以让页面卡顿 100ms 以上。理解 V8 的 GC 策略,不只是知识储备,直…
await 让函数"暂停",等 Promise 完成后"继续"——这个描述没错,但暂停是什么意思?主线程真的停下来等了吗?V8 用什么机制实现了"看似同步、实为异步"的执行模式?答案在 V8 把 async 函数转换成的状态机里,以及await 背后的微任务调度机制里。
在 MutationObserver 出现之前,DOM 变化监听依赖 Mutation Events(如 DOMNodeInserted、DOMAttrModified)。这套 API 存在根本性的性能问题——每次 DOM 变化都同步触发事件,阻塞主线程,大量操作时性能灾难。M…
setTimeout(fn, 0) 的 fn 为什么不是"立即"执行,而是"稍后"执行?为什么两个延时相同的 setTimeout 之间还有顺序关系?这些问题的答案在 V8 的事件循环设计里,宏任务(Macrotasks)是这套设计的核心单元。
"Promise 的 then 回调比 setTimeout 先执行"——大多数 JavaScript 开发者知道这个结论,但很少有人能说清楚为什么。这不是约定俗成的规则,而是 V8 内部有具体的执行机制在支撑:MicrotaskQueue 数据结构 + PerformMicr…
obj.x 看起来很简单,但 JavaScript 是动态类型语言,V8 不知道 obj 的 x 在哪——每次都去查?那性能没法看。V8 的解法是内联缓存(Inline Cache,IC):第一次访问时记录 "这种形状的对象,x 在 offset 0",后续直接复用这个结论,无…
加载一个大型 JavaScript 文件时,V8 并不会立即把里面所有函数都完整解析和编译。如果这样做,首次执行 的时间会随着代码量线性增长。V8 的实际策略是惰性解析(Lazy Parsing)——先快速扫描,推迟函数体的完整解析,等到函数真正被调用时再处理。这套机制和闭…
1 + "2" 的结果是 "12" 而不是 3——大多数开发者知道这个结果,背后的理由通常是"JavaScript 会把数字转成字符串"。但这个说法不够精确。为什么是数字转成字符串,而不是字符串转成数字?+ 运算符究竟在什么时候做字符串拼接,什么时候做数字加法?ECMAScri…
"作用域"和"执行上下文"这两个概念,很多 JavaScript 开发者混用。它们不是同一个东西,但联系很紧密。更少有人知道的是,这两个概念在 V8 内部有具体的数据结构对应——理解这些结构,才能真正解释闭包的行为,以及变量查找为什么是这个顺序。
前面的文章解释了 V8 为什么会切换到字典模式(Dictionary Mode)——对象结构变得不稳定,维持隐藏类的成本超过了收益。但字典模式并不意味着"放弃优化"。V8 在哈希表层面做了一系列针对性的优化,同时,字典模式也有硬性的性能天花板。理解这两面,才能在实际开发中做出准…
函数调用在 JavaScript 里无处不在,但"调用一个函数"在 V8 内部并不简单。每次调用都要创建执行上下文、分配栈帧、传递参数、管理返回地址——这些开销在热点代码中积累,会变成性能瓶颈。TurboFan 用函数内联(Function Inlining)来消除这个开销。这…
prototype 和 proto 的混淆是 JavaScript 原型系统里最常见的误解之一。很多人知道"它们都和原型有关",但说不清楚谁属于谁、谁在什么时候起作用。这两个属性在 V8 内部分属不同的数据结构,服务于不同的目的——厘清这一点,原型链就从"需要记忆的规则"变成了…
"delete 会破坏 V8 优化"——这个建议在前端社区流传很广,但通常没有解释背后的原因。知道"不要用"和知道"为什么不要用"是两回事。后者能帮你在真正需要用 delete 的场景里,做出更准确的判断。
一个常见的认知是:JavaScript 是动态语言,对象可以随意增删属性,这天然就是慢的。但 V8 不接受这个结论。它在内部引入了隐藏类(Hidden Class)机制,让动态对象的属性访问接近静态语言的速度。理解这套机制,是理解 V8 对象优化的起点。
"字典是非线性数据结构"这句话在很多前端技术文章里出现,但很少有人解释清楚:这里的"非线性"是什么意思,和"线性"相比到底差在哪,以及 V8 为什么要在对象属性存储上区分线性和非线性。
在 JavaScript 里,函数既是对象(typeof fn === 'function',但 fn instanceof Object === true),又是可执行的代码块。这个"双重身份"不是语言层面的比喻,而是 V8 内部有具体实现的机制。一个普通对象写了 foo()…
很多开发者知道"V8 是 Chrome 的 JavaScript 引擎",但说不清楚它具体做了什么。更重要的是,理解 V8 的执行流程,直接影响如何写出性能更好的代码——为什么同样的逻辑,换一种写法就快了几倍?为什么某些操作会让代码突然变慢?这些问题的答案,都藏在 V8 的执行…