Java中short, byte, boolean和char的擴(kuò)展時(shí)機(jī)

擴(kuò)展概述

眾所周知的是,在Java中帕胆,指令的操作碼是由一個(gè)字節(jié)組成的,這意味著操作碼的取值范圍在0-255之間般渡。由此帶來了一個(gè)問題懒豹,對于部分和類型相關(guān)的指令——比如load——來說,并不能做到給每一個(gè)類型都設(shè)計(jì)一個(gè)對應(yīng)的指令驯用。而在Java虛擬機(jī)中脸秽,針對short, byte, boolean和char都是使用int類型的指令來完成的。舉個(gè)例子來說蝴乔,從局部變量表里面加載一個(gè)short類型的數(shù)據(jù)到操作數(shù)棧记餐,將會(huì)使用iload指令。
Java虛擬機(jī)對此有明確的說明薇正。但是這又會(huì)有另外一個(gè)問題:short, byte, boolean和char都是“短”類型片酝,它們的字節(jié)數(shù)都要比int類型的少。所以在使用int類型的指令的時(shí)候挖腰,就需要解決擴(kuò)展的問題雕沿。擴(kuò)展的問題可以分成兩點(diǎn):

  • 如何擴(kuò)展
  • 什么時(shí)候擴(kuò)展

第一個(gè)問題,如何擴(kuò)展猴仑,Java虛擬機(jī)規(guī)范指出short和byte兩種類型將執(zhí)行符號擴(kuò)展审轮,而boolean和char類型將執(zhí)行零擴(kuò)展。
而第二個(gè)問題,什么時(shí)候擴(kuò)展断国,Java虛擬機(jī)規(guī)范只有一句含糊的“可以在編譯期或者運(yùn)行期”贤姆。這似乎意味著虛擬機(jī)的實(shí)現(xiàn)可以自己決定什么時(shí)候擴(kuò)展榆苞。但這有點(diǎn)一廂情愿稳衬,因?yàn)樵谔摂M機(jī)規(guī)范中的指令說明中,其實(shí)就已經(jīng)限定了擴(kuò)展的時(shí)機(jī)坐漏。
本文將主要探討一下在Oracle JDK 1.7.0_80版本下薄疚,使用Hotspot虛擬機(jī)的情況下,Java中short, byte, boolean和char的擴(kuò)展時(shí)機(jī)赊琳。

直覺猜想

一種直覺上的認(rèn)知是街夭,擴(kuò)展會(huì)帶來性能的損耗,因此能夠在編譯期擴(kuò)展完成躏筏,就沒有必要在運(yùn)行期擴(kuò)展板丽。基于這樣的想法趁尼,可以提出一個(gè)猜想:所有的字面量埃碱,都會(huì)在編譯期完成擴(kuò)展。而除了字面量以外酥泞,其余的變量砚殿、參數(shù)等,因?yàn)樵诰幾g期無法確定其值芝囤,因此無法在編譯期完成擴(kuò)展似炎。
早前我在思考這個(gè)問題的時(shí)候陷入了一個(gè)誤區(qū):所有的boolean和char類型的值(包括變量等)都能夠在編譯期完成擴(kuò)展。這是基于這兩個(gè)類型使用無符號擴(kuò)展所產(chǎn)生的悯姊。這兩個(gè)類型執(zhí)行無符號擴(kuò)展意味著羡藐,不論它們的確切值是什么,總可以將高位置為0而完成擴(kuò)展悯许。與之對應(yīng)的是仆嗦,因?yàn)閟hort和byte執(zhí)行符號擴(kuò)展,在編譯期無法確定其符號的情況下岸晦,則不能實(shí)現(xiàn)擴(kuò)展欧啤。
這個(gè)想法之所以錯(cuò)誤是因?yàn)闆]有考慮到程序被編譯成字節(jié)碼之后的本質(zhì)。對于字節(jié)碼來說启上,不存在一個(gè)操作數(shù)邢隧,其高位是確定的(執(zhí)行擴(kuò)展得來的),而低位是不確定的冈在。所有的操作數(shù)倒慧,在編譯期都是確定無誤的。
因此問題最終就歸結(jié)為:是否所有的字面量,都是在編譯期完成擴(kuò)展纫谅?

分析

是否所有的字面量炫贤,都在編譯期完成了擴(kuò)展?
很顯然付秕,答案是否定的兰珍。
這里采用char作為例子來說明:

public class CharExtend {
    private static final char a=0x1234;//4660
    private static char b=0x1357;//4951
    private final char c=0x2468;//9320
    private char d=0x9876;//39030

    public char testChar(char g, final char h){
        char e=0x9753;//38739
        final char f=0x8642;//34370
        b=a+c;
        d=a+c;
        d+=a;
        d+=f;
        b+=d;
        b+=h;
        b+=e;
        g+=b;
        return g;
    }

    public static char testChar1(char i, final char j, final char k){
        char l=0x4567;//17767
        final char m=0xabcd;//43981
        b+=a;
        b+=i;
        b+=j;
        b+=k;
        b+=l;
        b+=m;
        return b;
    }
}

因?yàn)椴淮_定final,static關(guān)鍵字是否會(huì)對擴(kuò)展造成影響询吴,所以需要盡可能的覆蓋這些關(guān)鍵字的所有的組合掠河。
要想確定它們的擴(kuò)展時(shí)間,還需要閱讀它們生成的字節(jié)碼文件猛计。這并不是指閱讀javap命令所生成的內(nèi)容唠摹,而是指,直接讀二進(jìn)制內(nèi)容奉瘤。因?qū)avap命令會(huì)屏蔽掉這個(gè)擴(kuò)展的信息勾拉。舉個(gè)例子來說,假如有一個(gè)字面量1盗温,那么javap解析出來的只會(huì)是1藕赞,但是看不出來它是0x00000001還是0x0001。不過因?yàn)槎M(jìn)制文件讀起來十分困難肌访,可以使用javap解析的內(nèi)容進(jìn)行輔助找默。
這個(gè)類編譯生成的二進(jìn)制內(nèi)容和javap解析得到的內(nèi)容此處就不貼了。下面對a進(jìn)行分析: a的十六進(jìn)制是0x1234吼驶,在二進(jìn)制中出現(xiàn)了三次:

0300 0012 3401 0001 6201 0001 6303 0000
b500 042a 59b4 0004 1112 3460 92b5 0004
0836 04b2 0007 1112 3460 92b3 0007 b200
  • 第一次是出現(xiàn)在常量池惩激,對應(yīng)的二進(jìn)制是0x00001234,其前面的03是常量池中Integer類型的表示蟹演》缱辏可以看到的是,它已經(jīng)被擴(kuò)展了酒请;
  • 第二次和第三次都是出現(xiàn)在0x111234這樣一個(gè)串中骡技,而11是sipush指令。在這兩次出現(xiàn)中a并沒有被擴(kuò)展羞反;

對其余變量布朦、參數(shù)的分析這里不一一說明≈绱埃總體上可以觀察得到:

  • 出現(xiàn)在常量池中的都已經(jīng)被擴(kuò)展了是趴;
  • 出現(xiàn)在類文件的Code屬性中,使用bipush, sipush指令的澄惊,都沒有被擴(kuò)展唆途;
  • 一個(gè)字面量可能同時(shí)出現(xiàn)在常量池和Code中富雅;
  • static的修飾對擴(kuò)展時(shí)機(jī)沒有影響;
  • final修飾的成員變量的字面量肛搬,必然會(huì)出現(xiàn)在常量池中没佑,而作為局部變量或者參數(shù)的修飾,則不具有這種效果温赔;

所以問題可以進(jìn)一步歸結(jié)為:什么因素會(huì)影響編譯器決定將字面量放入常量池蛤奢,或者放入Code屬性?這個(gè)問題让腹,Java虛擬機(jī)規(guī)范沒有太多的信息远剩。唯一和此有關(guān)的就是:對于short, byte, boolean, char以及小的int類型的值來說,可以使用bipush, sipush或者iconst指令骇窍。這三種指令都明確指出,擴(kuò)展是在指令執(zhí)行的時(shí)候同時(shí)進(jìn)行的锥余。而大的值就會(huì)出現(xiàn)在常量池中腹纳。

結(jié)論

第一,是否在編譯期擴(kuò)展和值的大小有關(guān)驱犹,值如果偏大嘲恍,那么編譯器會(huì)在常量池中存有該字面量。而在常量池中字面量雄驹,必然是被擴(kuò)展了的佃牛;
第二,有final關(guān)鍵字修飾的成員變量医舆,其值必然會(huì)被擴(kuò)展俘侠,并且放入常量池;
第三蔬将,對于一個(gè)偏小的值爷速,但是又屬于final修飾的成員變量,那么它會(huì)存有一份擴(kuò)展了的常量池副本霞怀,也會(huì)在Code屬性中保持未擴(kuò)展的形式惫东;
第四,其余情況都只能在運(yùn)行時(shí)擴(kuò)展毙石;

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末廉沮,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子徐矩,更是在濱河造成了極大的恐慌滞时,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件丧蘸,死亡現(xiàn)場離奇詭異漂洋,居然都是意外死亡遥皂,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進(jìn)店門刽漂,熙熙樓的掌柜王于貴愁眉苦臉地迎上來演训,“玉大人,你說我怎么就攤上這事贝咙⊙颍” “怎么了?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵庭猩,是天一觀的道長窟她。 經(jīng)常有香客問我,道長蔼水,這世上最難降的妖魔是什么震糖? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮趴腋,結(jié)果婚禮上吊说,老公的妹妹穿的比我還像新娘。我一直安慰自己优炬,他們只是感情好颁井,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著蠢护,像睡著了一般雅宾。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上葵硕,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天眉抬,我揣著相機(jī)與錄音,去河邊找鬼贬芥。 笑死吐辙,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的蘸劈。 我是一名探鬼主播昏苏,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼威沫!你這毒婦竟也來了贤惯?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤棒掠,失蹤者是張志新(化名)和其女友劉穎孵构,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體烟很,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡颈墅,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年蜡镶,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片恤筛。...
    茶點(diǎn)故事閱讀 39,690評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡官还,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出毒坛,到底是詐尸還是另有隱情望伦,我是刑警寧澤,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布煎殷,位于F島的核電站屯伞,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏豪直。R本人自食惡果不足惜劣摇,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望顶伞。 院中可真熱鬧饵撑,春花似錦、人聲如沸唆貌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽锨咙。三九已至,卻和暖如春追逮,著一層夾襖步出監(jiān)牢的瞬間酪刀,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工钮孵, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留骂倘,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓巴席,卻偏偏與公主長得像历涝,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子漾唉,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內(nèi)容