JAVA字节码指令iload_<n>为什么只有0到3?
点击上方“朱小厮的博客”,选择“设为星标”后台回复"书",获取后台回复“k8s”,可领取k8s资料来源:r6d.cn/ZxLw这是Java字节码上针对字节码大小的一个早期...
点击上方“朱小厮的博客”,选择“设为星标”
后台回复"书",获取
后台回复“k8s”,可领取k8s资料
来源:r6d.cn/ZxLw
这是Java字节码上针对字节码大小的一个早期优化。从现在的角度看它可能算是一种过早优化(premature optimization)了。
Java字节码指令集里,大部分跟局部变量打交道的指令(例如<type>load、<type>store)都有完整版:
<type>load n
例如iload 5,以及针对头4个局部变量/参数的缩写版:
<type>load_<n>
例如iload_0,这样两个版本。其中,缩写版,正如标题所说,只有0~3的范围。
它们的区别是,前者有显式的“操作数”(operand),而后者是把操作数融合到了操作码(opcode)里面。看iload与iload_<n>的例子就很清楚:
iload的指令格式是:Chapter 6. The Java Virtual Machine Instruction Set[1]
iload index
其中"iload"是opcode,其值为21(0x15),而后面跟着一个unsigned byte作为index来指定局部变量的下标。另外还有wide版,如果在iload前面带有wide前缀的话,则格式为:
wide iload index1 index2
其中wide、iload、index1、index2各自为一个字节,而 (index1 << 8) | index2 构成指令局部变量下标的操作数。
iload_的指令格式则是:Chapter 6. The Java Virtual Machine Instruction Set[2]
iload_<n>
其中iload_<n>自身就是opcode,它可能的取值为:iload_0 = 26 (0x1a)iload_1 = 27 (0x1b)iload_2 = 28 (0x1c)iload_3 = 29 (0x1d)这样的话,针对头4个局部变量,iload_<n>就可以只用一个字节的opcode来表达整条指令,比使用完整版的iload要少一个字节。使用缩写版指令不但可以让字节码的大小减少,还可以让解释器(注意!只是解释器)的性能提升。因为解释器通常都会有这样的结构:
while (true) {
opcode = *program_counter++; // fetch opcode:
// 1 memory read, 1 memory write
switch (opcode) { // dispatch opcode
case some_instruction:
operands = decode_operands(); // decode operands:
// 1~n memory reads
perform_operation(operands); // actual operation
program_counter += size_of_some_instruction; // 1 memory read, 1 memory write
break;
}
}
(解释器有各种优化方式,上面的形式是最简单的switch-threading,但 fetch-dispatch/decode-execute 的组成部分总是存在的)当使用缩写版指令时,decode_operands()就不需要做任何额外的内存读,因为operand已经隐藏在opcode里了,于是就会比完整版指令要快一些。
至于为啥选择0~3的范围来做缩短版,我不知道当初JVM原始设计的过程中具体发生了怎样的讨论和设计取舍,但一种可以想像的可能性是:最初的JVM的解释器已经写好了,看看1个字节的opcode能表达的256个opcode中已经用了多少个,然后再想想剩下的空余的那些可以用来做怎样的局部优化。
大概是正好发现,如果用0~3的话可以基本上把opcode范围用满(JVM规范里使用了的opcode范围比Sun最初的JVM内部所使用的opcode范围要小一些,因为Sun JVM使用了一些quick_系字节码并没有作为规范的一部分,而是在第一版JVM规范里作为额外的讲解说剩余的编码空间可以用来做quick_系指令的优化),如果用例如说0~4的话就把1字节opcode编码空间用超了,而0~2的话则用不满。
就这样而已。
于是早期的坊间传说的Java程序性能优化指引中,有一条是说:Java方法应该尽量只使用不超过4个参数+局部变量,最频繁使用的局部变量应该放在前面,来想办法使用上Java字节码的这个缩写版指令优化。
然而后来JIT编译器成为主流后,这种优化指引就完全没有用了。JIT编译器根本不在乎输入的字节码是完整版还是缩写版,都一样对待。
参考资料
[1]
Chapter 6. The Java Virtual Machine Instruction Set: https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5.iload
[2]Chapter 6. The Java Virtual Machine Instruction Set: https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5.iload_n
想知道更多?扫描下面的二维码关注我
后台回复"技术",加入技术群
后台回复“k8s”,可领取k8s资料
【精彩推荐】
原创|OpenAPI标准规范
中台不是万能药,关于中台的思考和尝试
ClickHouse到底是什么?为什么如此牛逼!
原来ElasticSearch还可以这么理解
面试官:InnoDB中一棵B+树可以存放多少行数据?
微服务下如何解耦?对于已经紧耦合下如何重构?
如何构建一套高性能、高可用、低成本的视频处理系统?
架构之道:分离业务逻辑和技术细节
星巴克不使用两阶段提交
点个赞+在看,少个 bug ????
更多推荐
所有评论(0)