JVM虛擬機(jī)?!狫AVA方法的消亡史-創(chuàng)新互聯(lián)

引子

這是由一個(gè)“無(wú)聊”的問(wèn)題引發(fā)的故事:方法ipp和ppi分別會(huì)打印什么結(jié)果?

網(wǎng)站建設(shè)哪家好,找成都創(chuàng)新互聯(lián)公司!專注于網(wǎng)頁(yè)設(shè)計(jì)、網(wǎng)站建設(shè)、微信開(kāi)發(fā)、小程序開(kāi)發(fā)、集團(tuán)企業(yè)網(wǎng)站建設(shè)等服務(wù)項(xiàng)目。為回饋新老客戶創(chuàng)新互聯(lián)還提供了新余免費(fèi)建站歡迎大家使用!
public class Opcode {	public static void main(String[] args) {
		System.out.println("hello wang ni ma");
	}	public void ipp(){		int i = 0;
		i = i++;
		System.out.println(i);
	}	public void ppi(){		int i = 0;
		i = ++i;
		System.out.println(i);
	}
}

當(dāng)然了,把兩個(gè)方法放在一起,憑借些許的邏輯思維分析,可以很快給出答案: 0 1

那JVM為什么會(huì)執(zhí)行出這樣的結(jié)果呢,本文將結(jié)合 字節(jié)碼 和 虛擬機(jī)棧 做出解釋。

番外

javap 反匯編器

    javap是JDK自帶的反匯編器,可以查看java編譯器為我們生成的字節(jié)碼。通過(guò)它,我們可以對(duì)照源代碼和字節(jié)碼,從而了解很多編譯器內(nèi)部的工作。

java字節(jié)碼指令集

    Java 程序編譯之后就變成了一條條字節(jié)碼指令,其形式類(lèi)似匯編,但和匯編有不同之處:

  •     匯編指令的操作數(shù)存放在數(shù)據(jù)段和寄存器中,可通過(guò)存儲(chǔ)器或寄存器尋址找到需要的操作數(shù);

  •     Java 字節(jié)碼指令的操作數(shù)存放在操作數(shù)棧中(可以理解為JVM內(nèi)部虛擬寄存器),當(dāng)執(zhí)行某條帶 n 個(gè)操作數(shù)的指令時(shí),就從棧頂取 n 個(gè)操作數(shù),然后把指令的計(jì)算結(jié)果(如果有的話)入棧。

    由于操作數(shù)棧是內(nèi)存空間,所以字節(jié)碼指令不必?fù)?dān)心不同機(jī)器上寄存器以及機(jī)器指令的差別,從而做到了平臺(tái)無(wú)關(guān)。

    Java虛擬機(jī)的指令由一個(gè)字節(jié)長(zhǎng)度的、代表著某種特定操作含義的操作碼(Opcode)以及跟隨其后的零至多個(gè)代表此操作所需參數(shù)的操作數(shù)(Operands)所構(gòu)成。

  列舉本文用到的基本指令:

  • 加載和存儲(chǔ)指令

        將一個(gè)局部變量加載到操作棧:iload_<n>

        將一個(gè)數(shù)值從操作數(shù)棧存儲(chǔ)到局部變量表:istore_<n>

        將一個(gè)常量加載到操作數(shù)棧:iconst_<i>

  • 運(yùn)算指令 (運(yùn)算之后的結(jié)果會(huì)自動(dòng)入棧)

        局部變量自增指令:iinc

    對(duì)于大部分與數(shù)據(jù)類(lèi)型相關(guān)的字節(jié)碼指令,他們的操作碼助記符中都有特殊的字符來(lái)表明專門(mén)為哪種數(shù)據(jù)類(lèi)型服務(wù):

  • i -- int

  • l -- long

  • s -- short

  • b -- byte

  • c -- char

  • f -- float

  • d -- double

  • a -- reference

 正題

  我們用javac編譯上面的Opcode.java,然后“javap -c”查看字節(jié)碼:

    JVM虛擬機(jī)?!狫AVA方法的消亡史

    javap命令加入“-v”可以看到更詳細(xì)的信息(常量池) :

    JVM虛擬機(jī)棧——JAVA方法的消亡史

虛擬機(jī)棧圖解

在看圖之前我們先了解幾個(gè)概念:

  • Java虛擬機(jī)(JVM)是基于棧結(jié)構(gòu)的,其中的“?!敝傅木褪遣僮鲾?shù)棧。

  • 在代碼的實(shí)際運(yùn)行中,每個(gè)線程都會(huì)創(chuàng)建一個(gè)JVM棧存儲(chǔ)棧幀(frame)。每當(dāng)有方法調(diào)用時(shí),frame就會(huì)被創(chuàng)建;當(dāng)這個(gè)方法返回時(shí),frame出棧。

  • 一個(gè)frame由三部分組成:操作數(shù)棧(Oprand Stack)、局部變量表(Local Variable Table)、當(dāng)前方法所在類(lèi)的運(yùn)行常量池的引用(The Reference of Constant Pool)。

    • 局部變量表,存儲(chǔ)的是方法的參數(shù)和局部變量的值。存儲(chǔ)參數(shù)的索引從0開(kāi)始,如果是構(gòu)造方法或者實(shí)例化方法的frame,那么局部變量數(shù)組的“0”處存儲(chǔ)的是“this”引用,然后再?gòu)摹?”開(kāi)始存儲(chǔ)形參和局部變量;如果是靜態(tài)方法的frame,局部變量表不會(huì)存儲(chǔ)“this”引用,而是從“0”開(kāi)始存儲(chǔ)形參和局部變量。

    • 操作數(shù)棧,臨時(shí)存儲(chǔ)參與運(yùn)算的數(shù)值,然后進(jìn)行相關(guān)操作。和局部變量表一樣,操作數(shù)棧也是一個(gè)以字長(zhǎng)為單位的數(shù)組。但是和前者不同的是,它不是通過(guò)索引來(lái)訪問(wèn),而是通過(guò)標(biāo)準(zhǔn)的棧操作壓棧/出棧來(lái)訪問(wèn)的。

    • 常量池,存儲(chǔ)在JVM內(nèi)存線程共享區(qū)的“方法區(qū)”,在類(lèi)初始化的時(shí)候,會(huì)為給出的常量分配一個(gè)常量池,并且為每一個(gè)常量給出引用。

ipp()

    把常量“0”加載到操作數(shù)棧,指令“iconst_0”中的“0”代表int常量“0”

        操作數(shù)棧:[0]

        局部變量表:[this]

JVM虛擬機(jī)棧——JAVA方法的消亡史

“i = i++;”進(jìn)行了四次操作:

    1 “istore_1”將操作數(shù)棧中棧頂?shù)膇nt壓入局部變量表“1”的位置

        操作數(shù)棧:[]

        局部變量表:[this, 0]

    2 “iload_1”將局部變量表“1”處的int加載到操作數(shù)棧

        操作數(shù)棧:[0]

        局部變量表:[this, 0]

    3 “iinc 1, 1”將局部變量表“1”處的int做自增運(yùn)算,結(jié)果自動(dòng)入棧

        操作數(shù)棧:[0]

        局部變量表:[this, 1]

    4 “istore_1”將操作數(shù)?!?”處的int壓入局部變量表

        操作數(shù)棧:[]

        局部變量表:[this, 0]

JVM虛擬機(jī)?!狫AVA方法的消亡史

“System.out.println(i);”進(jìn)行了三次操作:

    1 “getstatic  #2”指向常量池中的第2個(gè)位置,載入“System.out”域

    2 “iload_1”將局部變量表“1”處的int加載到操作數(shù)棧

        操作數(shù)棧:[0]

        局部變量表:[this, 0]

    3 “invokevirtul  #5”指向常量池中的第5個(gè)位置,”調(diào)用實(shí)例方法“println”打印操作數(shù)棧的數(shù)值“0”

JVM虛擬機(jī)?!狫AVA方法的消亡史

至此,ipp()方法的分析完成了,理解之后,反觀ppi()方法的字節(jié)碼信息,有一處不同:

JVM虛擬機(jī)?!狫AVA方法的消亡史

    相當(dāng)于“i = i++”操作是先加載了局部變量表中的“0”到操作數(shù)棧,然后在局部變量表中做自增運(yùn)算;而“i = ++i”是先在局部變量表中做自增運(yùn)算,此時(shí)的值已經(jīng)變成“1”,然后再把局部變量表中的“1”加載到操作數(shù)棧。這也就印證了坊間流傳的“i++是先賦值后運(yùn)算,++i是先運(yùn)算后賦值”這一說(shuō)法。

另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內(nèi)外云服務(wù)器15元起步,三天無(wú)理由+7*72小時(shí)售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國(guó)服務(wù)器、虛擬主機(jī)、免備案服務(wù)器”等云主機(jī)租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡(jiǎn)單易用、服務(wù)可用性高、性價(jià)比高”等特點(diǎn)與優(yōu)勢(shì),專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場(chǎng)景需求。

分享題目:JVM虛擬機(jī)?!狫AVA方法的消亡史-創(chuàng)新互聯(lián)
網(wǎng)站鏈接:http://bm7419.com/article48/didoep.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供響應(yīng)式網(wǎng)站、App設(shè)計(jì)ChatGPT、商城網(wǎng)站、靜態(tài)網(wǎng)站自適應(yīng)網(wǎng)站

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

成都seo排名網(wǎng)站優(yōu)化