JAVA字节码指令iload_n为什么只有0到3?
點(diǎn)擊上方“朱小廝的博客”,選擇“設(shè)為星標(biāo)”
后臺(tái)回復(fù)"書(shū)",獲取
后臺(tái)回復(fù)“k8s”,可領(lǐng)取k8s資料
來(lái)源:r6d.cn/ZxLw
這是Java字節(jié)碼上針對(duì)字節(jié)碼大小的一個(gè)早期優(yōu)化。從現(xiàn)在的角度看它可能算是一種過(guò)早優(yōu)化(premature optimization)了。
Java字節(jié)碼指令集里,大部分跟局部變量打交道的指令(例如<type>load、<type>store)都有完整版:
<type>load?n例如iload 5,以及針對(duì)頭4個(gè)局部變量/參數(shù)的縮寫(xiě)版:
<type>load_<n>例如iload_0,這樣兩個(gè)版本。其中,縮寫(xiě)版,正如標(biāo)題所說(shuō),只有0~3的范圍。
它們的區(qū)別是,前者有顯式的“操作數(shù)”(operand),而后者是把操作數(shù)融合到了操作碼(opcode)里面??磇load與iload_<n>的例子就很清楚:
iload的指令格式是:Chapter 6. The Java Virtual Machine Instruction Set[1]
iload?index其中"iload"是opcode,其值為21(0x15),而后面跟著一個(gè)unsigned byte作為index來(lái)指定局部變量的下標(biāo)。另外還有wide版,如果在iload前面帶有wide前綴的話,則格式為:
wide?iload?index1?index2其中wide、iload、index1、index2各自為一個(gè)字節(jié),而 (index1 << 8) | index2 構(gòu)成指令局部變量下標(biāo)的操作數(shù)。
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)這樣的話,針對(duì)頭4個(gè)局部變量,iload_<n>就可以只用一個(gè)字節(jié)的opcode來(lái)表達(dá)整條指令,比使用完整版的iload要少一個(gè)字節(jié)。使用縮寫(xiě)版指令不但可以讓字節(jié)碼的大小減少,還可以讓解釋器(注意!只是解釋器)的性能提升。因?yàn)榻忉屍魍ǔ6紩?huì)有這樣的結(jié)構(gòu):
while?(true)?{opcode?=?*program_counter++;????//?fetch?opcode://???1?memory?read,?1?memory?writeswitch?(opcode)?{???????????????//?dispatch?opcodecase?some_instruction:operands?=?decode_operands();?//?decode?operands://???1~n?memory?readsperform_operation(operands);??//?actual?operationprogram_counter?+=?size_of_some_instruction;?//?1?memory?read,?1?memory?writebreak;} }(解釋器有各種優(yōu)化方式,上面的形式是最簡(jiǎn)單的switch-threading,但 fetch-dispatch/decode-execute 的組成部分總是存在的)當(dāng)使用縮寫(xiě)版指令時(shí),decode_operands()就不需要做任何額外的內(nèi)存讀,因?yàn)閛perand已經(jīng)隱藏在opcode里了,于是就會(huì)比完整版指令要快一些。
至于為啥選擇0~3的范圍來(lái)做縮短版,我不知道當(dāng)初JVM原始設(shè)計(jì)的過(guò)程中具體發(fā)生了怎樣的討論和設(shè)計(jì)取舍,但一種可以想像的可能性是:最初的JVM的解釋器已經(jīng)寫(xiě)好了,看看1個(gè)字節(jié)的opcode能表達(dá)的256個(gè)opcode中已經(jīng)用了多少個(gè),然后再想想剩下的空余的那些可以用來(lái)做怎樣的局部?jī)?yōu)化。
大概是正好發(fā)現(xiàn),如果用0~3的話可以基本上把opcode范圍用滿(JVM規(guī)范里使用了的opcode范圍比Sun最初的JVM內(nèi)部所使用的opcode范圍要小一些,因?yàn)镾un JVM使用了一些quick_系字節(jié)碼并沒(méi)有作為規(guī)范的一部分,而是在第一版JVM規(guī)范里作為額外的講解說(shuō)剩余的編碼空間可以用來(lái)做quick_系指令的優(yōu)化),如果用例如說(shuō)0~4的話就把1字節(jié)opcode編碼空間用超了,而0~2的話則用不滿。
就這樣而已。
于是早期的坊間傳說(shuō)的Java程序性能優(yōu)化指引中,有一條是說(shuō):Java方法應(yīng)該盡量只使用不超過(guò)4個(gè)參數(shù)+局部變量,最頻繁使用的局部變量應(yīng)該放在前面,來(lái)想辦法使用上Java字節(jié)碼的這個(gè)縮寫(xiě)版指令優(yōu)化。
然而后來(lái)JIT編譯器成為主流后,這種優(yōu)化指引就完全沒(méi)有用了。JIT編譯器根本不在乎輸入的字節(jié)碼是完整版還是縮寫(xiě)版,都一樣對(duì)待。
參考資料
[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
想知道更多?掃描下面的二維碼關(guān)注我
后臺(tái)回復(fù)"技術(shù)",加入技術(shù)群
后臺(tái)回復(fù)“k8s”,可領(lǐng)取k8s資料
【精彩推薦】
原創(chuàng)|OpenAPI標(biāo)準(zhǔn)規(guī)范
中臺(tái)不是萬(wàn)能藥,關(guān)于中臺(tái)的思考和嘗試
ClickHouse到底是什么?為什么如此牛逼!
原來(lái)ElasticSearch還可以這么理解
面試官:InnoDB中一棵B+樹(shù)可以存放多少行數(shù)據(jù)?
微服務(wù)下如何解耦?對(duì)于已經(jīng)緊耦合下如何重構(gòu)?
如何構(gòu)建一套高性能、高可用、低成本的視頻處理系統(tǒng)?
架構(gòu)之道:分離業(yè)務(wù)邏輯和技術(shù)細(xì)節(jié)
星巴克不使用兩階段提交
點(diǎn)個(gè)贊+在看,少個(gè) bug?????
總結(jié)
以上是生活随笔為你收集整理的JAVA字节码指令iload_n为什么只有0到3?的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 5分钟了解CDN 加速原理 | +新书推
- 下一篇: 醍醐灌顶 | 我们谈论的Exactly