v8字节码
对于字节码,之前有过听过,但是没有深入的了解。
高级语言中,cpp是直接转换成二进制,java作为一个跨平台的语言,其中就有字节码的概念(点这里),对于js,都知道的V8引擎,它是怎么工作的呢?
请先阅读参考文章:
文章中最重要的是这张图片了,v8会先将javascript翻译成字节码,再将字节码翻译成机器语言,如何查看生成的字节码呢?使用node --print-bytecode test.js
,只要再原来的基础上加上–print-bytecode就可以了。
让我们写一个for循环
function for_loop(){
for (let i = 0; i < 10; i++) {
console.log(i)
}
}
for_loop()
使用上面的命令,会打印出来很多很多东西,v8不仅仅会翻译你的代码,其中也会有一些自己包含的东西在里面,例如console,可以使用--print-bytecode-filter
来过滤一下,你会得到
[generated bytecode for function: for_loop]
Parameter count 1
Frame size 24
17 E> 0x1897b5c16f02 @ 0 : a0 StackCheck
36 S> 0x1897b5c16f03 @ 1 : 0b LdaZero
0x1897b5c16f04 @ 2 : 26 fb Star r0
41 S> 0x1897b5c16f06 @ 4 : 0c 0a LdaSmi [10]
41 E> 0x1897b5c16f08 @ 6 : 66 fb 00 TestLessThan r0, [0]
0x1897b5c16f0b @ 9 : 94 1c JumpIfFalse [28] (0x1897b5c16f27 @ 37)
23 E> 0x1897b5c16f0d @ 11 : a0 StackCheck
58 S> 0x1897b5c16f0e @ 12 : 13 00 01 LdaGlobal [0], [1]
0x1897b5c16f11 @ 15 : 26 f9 Star r2
66 E> 0x1897b5c16f13 @ 17 : 28 f9 01 03 LdaNamedProperty r2, [1], [3]
0x1897b5c16f17 @ 21 : 26 fa Star r1
66 E> 0x1897b5c16f19 @ 23 : 57 fa f9 fb 05 CallProperty1 r1, r2, r0, [5]
48 S> 0x1897b5c16f1e @ 28 : 25 fb Ldar r0
0x1897b5c16f20 @ 30 : 4a 07 Inc [7]
0x1897b5c16f22 @ 32 : 26 fb Star r0
0x1897b5c16f24 @ 34 : 85 1e 00 JumpLoop [30], [0] (0x1897b5c16f06 @ 4)
0x1897b5c16f27 @ 37 : 0d LdaUndefined
77 S> 0x1897b5c16f28 @ 38 : a4 Return
根据参考文章中的带a的单词,一般是用来检查累加器,那么可以尝试理解一下:
StackCheck // 检查堆栈
LdaZero // 累加器置0,acc = 0
Star r0 // 将累加器的值赋给r0,r0 = 0
LdaSmi [10] // 累加器置为常数10,acc = 10
TestLessThan r0, [0] // 比较r0和acc
JumpIfFalse [28] (0x1897b5c16f27 @ 37) // 为false,根据后面的地址会跳到LdaUndefined这一行
StackCheck // 检查堆栈
LdaGlobal [0], [1] // 累计器置为全局变量console
Star r2 // 将累加器的值赋给r2, r2 = console
LdaNamedProperty r2, [1], [3] // 读取r2的属性赋值acc,本来理解的是后面的1、3指的是.log,log,但是我试了其他的字节码,这个指针会变,所以这一块我也不是奔清楚
Star r1 // r1 = acc, 也就是console.log
CallProperty1 r1, r2, r0, [5] // 这是调用输出,5不清楚代表的什么
Ldar r0 // acc = r0,
Inc [7] // acc++
Star r0 // r0 = acc
JumpLoop [30], [0] (0x1897b5c16f06 @ 4) // 循环,跳到LdaSmi [10]这一行
LdaUndefined // 累加器置为undefined
Return // 返回累加器中的值
在理解产生的字节码的时候,比较迷惑的是[数字]
, 不知道代表什么意思,LdaSmi [10]
这个是参考文章中有,LdaNamedProperty r2, [1], [3]
这个文章中也有,1和3代表索引,但是再尝试理解另一个函数的字节码时,也是console.log
,但是索引就变了==
刚开始有一个错误理解,每种语言的字节码应该会大同小异,为此,我还找了一些java的字节码看,但是对比下来,用处不大。还是看不懂,一顿搜索,终于找到了一篇好文(下面参考文章的第一个),那个文章是为了对比let和var的效能问题,总算有了眉目。
借用维基中的一句话:「理解字节码以及理解Java编译器如何生成Java字节码与学习汇编知识对于C/C++程序员有一样的意义。」
参考文章: