Chrome 61 发布后,被爆存在安全漏洞,而 Chrome 团队在修复安全漏洞的过程中发现一些漏洞是由 V8 的 escape analysis 引起的,编号为 #765433 #752423 等。
escape analysis 学名是逃逸分析。在编译优化理论中,逃逸分析是一种确定指针动态范围的方法——分析在程序的哪些地方可以访问到指针。
V8 v6.1 及更早版本使用了一个复杂的逃逸分析实现,自从引入以来就产生了许多错误(2333)。此实施已被删除,并在 V8 v6.2 中提供了全新的逃逸分析代码库。
为了保护用户的安全,Chrome 团队在 Chrome 61 中关闭了逃逸分析。关闭逃脱分析会对性能产生负面影响,因为它会禁用某些优化。具体来说,以下 ES2015 功能可能会变慢:
- 解构(destructuring)
- for-of 迭代器(for-of iteration)
- 数组扩展(array spread)
- 剩余参数(rest parameters)
Chrome 团队的这一举措,使得数以亿计的页面执行速度变慢。
不过可以确认的是,禁用逃逸分析只是临时措施。在 Chrome 62 版本中将使用 V8 6.2 中全新的逃逸分析功能。
目前这个举措只影响到 Chrome 61,根据 Node.js 官方 issue #15564 的讨论,Node 8 不受影响,因为漏洞利用不受信任的 JavaScript 执行,除非在 Node.js 中使用了 eval()
,否则 Node.js 运行的代码都是有开发者写的受信代码,Node 8 依然会默认开启逃逸分析特性。
什么是逃逸分析?
在 JavaScript 中,如果从当前函数外部可访问某个对象,则这个分配的对象"逃逸(escape)"了。
一句话概括就是,V8 在JavaScript 堆上分配新对象,使用逃逸分析,当 V8 分析出此对象只在函数内部起作用(和函数有相同的生命周期),则 V8 可以把对象分配到栈上,甚至可以把某些变量分配到寄存器中,把对象作为一个简单的局部变量。
如果对象逃逸了,则必须在堆上分配。
例如,逃逸分析使 V8 能够有效地重写以下代码:
function foo(a, b) {
const object = { a, b };
return object.a + object.b;
// Note: `object` does not escape.
}
...这个代码,可以进行如下优化:
function foo(a, b) {
const object_a = a;
const object_b = b;
return object_a + object_b;
}
object_a
和 object_b
分配在栈上甚至寄存器上。
逃逸分析除了可以将堆分配转化为栈分配以外,还可以:
- 同步省略:如果一个对象被发现只能从一个线程被访问到,那么对于这个对象的操作可以不考虑同步。
- 分离对象或标量替换:有的对象可能不需要作为一个连续的内存结构存在也可以被访问到,那么对象的部分(或全部)可以不存储在内存,而是存储在 CPU 寄存器中。
大部分动态语言都使用逃逸分析进行优化。而 V8 下一个版本将采用新一代逃逸分析技术。
欢迎关注我的公众号,关注前端文章: