国产精品无码一区二区三区太,亚洲一线产区二线产区区别,欧美A区,人妻jealousvue人妻

×

深入理解Java之java虛擬機(jī)干凈利落的規(guī)范總結(jié) 下

  • 作者:新網(wǎng)
  • 來(lái)源:新網(wǎng)
  • 瀏覽:100
  • 2018-05-03 17:56:15

要去正確地實(shí)現(xiàn)一臺(tái)Java虛擬機(jī),就需要正確地讀取class文件中每一條字節(jié)碼指令并且能正確執(zhí)行這些指令所蘊(yùn)含的操作即可。

  t0132608ea0fedf6d64.png

<div>        要去正確地實(shí)現(xiàn)一臺(tái)Java虛擬機(jī),就需要正確地讀取class文件中每一條字節(jié)碼指令并且能正確執(zhí)行這些指令所蘊(yùn)含的操作即可。
  由Java虛擬機(jī)執(zhí)行的每個(gè)方法都會(huì)配有零到多個(gè)異常處理器。異常處理器描述了其在方法代碼中的有效作用范圍(通過(guò)字節(jié)碼偏移量范圍來(lái)描述)、能處理的異常類(lèi)型以及處理異常的代碼所在的位置。要判斷某個(gè)異常處理器是否可以處理某個(gè)具體的異常,需要同時(shí)檢查一場(chǎng)出現(xiàn)的位置是否在異常處理的有效作用范圍內(nèi),以及出現(xiàn)的異常是否是異常處理器聲明可以處理的異常類(lèi)型或其子類(lèi)型。當(dāng)拋出異常時(shí),Java虛擬機(jī)搜索當(dāng)前方法包含的各個(gè)異常處理器,如果能找到可以處理該異常的異常處理器,則將代碼控制權(quán)轉(zhuǎn)向異常處理器中描述的處理異常的分支之中。
  首先簡(jiǎn)單介紹一下main方法中各條字節(jié)碼指令所代表的意思:
  0 : 將int類(lèi)型的常量i壓入操作數(shù)棧中,iconst_0后面跟的那個(gè)0代表常量的值為0;
  1:將一個(gè)int類(lèi)型數(shù)據(jù)由保存到本地變量表,istort_1后面的1代表的是指向當(dāng)前棧幀中局部變量表的索引值;
  2:將一個(gè)值為3的int類(lèi)型的常量壓入操作數(shù)棧中。在這里指的是被除數(shù)3;
  3:從局部變量表加載一個(gè)int類(lèi)型值到操作數(shù)棧中,這里指的是除數(shù)i;
  4:對(duì)兩個(gè)int類(lèi)型的數(shù)據(jù)做除法;
  5:將兩數(shù)相除之后所得到的int類(lèi)型數(shù)據(jù)保存到本地變量表;
  6:假如沒(méi)有發(fā)生異常的話,那么執(zhí)行完goto到第14條語(yǔ)句,函數(shù)正常返回;
  9:假如發(fā)生了除零異常,就執(zhí)行這條指令,將異常對(duì)象保存到局部變量表中;
  10:從局部變量表中加載剛才的那個(gè)異常對(duì)象到操作數(shù)中;
  11:調(diào)用異常對(duì)象的printStackTrace方法
  14:不管是正常完成還是異常完成,最終都會(huì)返回。
  在字節(jié)碼下方可以看到一個(gè)Exception table。那么它是什么東西呢?其實(shí)我們很容易能夠理解它就是異常表,也就是前面我們提過(guò)的異常處理器。我們可以明顯地觀察出,其實(shí)try-catch代碼塊編譯之后似乎沒(méi)有生成任何指令。那么Java語(yǔ)言中的try-catch放到字節(jié)碼當(dāng)中對(duì)應(yīng)什么東西呢?其實(shí)就是對(duì)應(yīng)這個(gè)異常處理器。下面我們來(lái)解讀一下異常處理器:
  在try語(yǔ)句塊的執(zhí)行過(guò)程中如果沒(méi)有拋出異常,那么這個(gè)異常處理器不會(huì)起作用。異常處理器的作用范圍是從字節(jié)碼的第2行到第6行,也就是from-to標(biāo)明的范圍。假如編譯好的代碼里面第2~6句之間有一個(gè)類(lèi)型為java.lang.ArithmeticException的異常實(shí)例被拋出,那么操作將轉(zhuǎn)移至第9句繼續(xù)執(zhí)行,即進(jìn)入catch語(yǔ)句塊的實(shí)踐步驟。假如說(shuō)拋出的異常不是ArithmeticException實(shí)例,那么異常處理器就不能處理該異常,這個(gè)異常將返回給上一級(jí)的調(diào)用者。
  那假如try語(yǔ)句塊包含多個(gè)catch語(yǔ)句塊,在編譯好的代碼中會(huì)出現(xiàn)什么樣的結(jié)果呢?
  如果給定的try語(yǔ)句塊包含多個(gè)catch語(yǔ)句塊,那么在編譯好的代碼中,多個(gè)catch語(yǔ)句塊的內(nèi)容將會(huì)連續(xù)排列,在異常表中也會(huì)有對(duì)應(yīng)的連續(xù)排列的成員,它們的排列順序和源碼中catch語(yǔ)句塊的出現(xiàn)順序一致。main方法在執(zhí)行時(shí),如果try語(yǔ)句塊中拋出了一個(gè)異常,這個(gè)異常將會(huì)被多個(gè)catch語(yǔ)句塊捕獲。假如第一個(gè)catch不能捕獲異常(當(dāng)然這里的第一個(gè)catch語(yǔ)句塊肯定是能處理ArithmeticException,我只是舉個(gè)例子),那么異常將交由第二個(gè)異常處理器來(lái)進(jìn)行處理,這很容易理解。因?yàn)槲以诘诙€(gè)catch語(yǔ)句塊中選擇的是將捕獲的異常拋出,所以在字節(jié)碼的第26行可以看到有一個(gè)athrow指令,在前面的學(xué)習(xí)當(dāng)中我們知道它是拋出異常的意思,其實(shí)也就是對(duì)應(yīng)著Java代碼中的throw new Exception()。在這里,我還要順便介紹一下Java創(chuàng)建一個(gè)對(duì)象的代碼在編譯之后會(huì)產(chǎn)生怎樣的字節(jié)碼。
  其實(shí),剛才我所說(shuō)的throw new Exception()對(duì)應(yīng)athrow字節(jié)碼指令只說(shuō)對(duì)了一半,它在編譯之后不僅僅只產(chǎn)生athrow這一條字節(jié)碼指令。因?yàn)樗€對(duì)應(yīng)著一個(gè)操作,也就是new一個(gè)Exception對(duì)象。Java語(yǔ)言實(shí)例化一個(gè)Exception對(duì)象將會(huì)產(chǎn)生三條字節(jié)碼指令,即上圖中19,22,23三行:
  為什么會(huì)有三條指令呢?dup是做什么的?我們下面一起來(lái)學(xué)習(xí)一下
  由于討論的是創(chuàng)建對(duì)象,所以在代碼throw new Exception()中我們不看throw,只看new Exception()這一部分代碼。
  new Exception()表達(dá)式的作用是:
  創(chuàng)建并默認(rèn)初始化一個(gè)Exception對(duì)象;
  調(diào)用Exceptioon類(lèi)的signature為()V的構(gòu)造器;
  表達(dá)式的值為一個(gè)指向這個(gè)對(duì)象的引用
  對(duì)應(yīng)字節(jié)碼,我們可以看到:
  new Exception()對(duì)應(yīng)上面的1
  invokespecial Exception.()V對(duì)應(yīng)上面的2
  那么3是怎么來(lái)的?
  回歸到字節(jié)碼,我們可以看到new字節(jié)碼指令的作用是創(chuàng)建指定類(lèi)型的對(duì)象實(shí)例、對(duì)其進(jìn)行默認(rèn)初始化,并將指向該實(shí)例的一個(gè)引用壓入操作數(shù)棧頂;
  然后因?yàn)閕nvokespecial會(huì)消耗操作數(shù)棧頂?shù)囊米鳛閭鹘o構(gòu)造器的"this"參數(shù),所以如果我們希望在invokespecial調(diào)用后在操作數(shù)棧頂還維持有一個(gè)指向新建對(duì)象的引用,就得在invokespecial之前先“復(fù)制”一份引用----這就是dup的來(lái)源。
  以上,就是對(duì)創(chuàng)建一個(gè)對(duì)象編譯之后產(chǎn)生的字節(jié)碼的解釋
  編譯finally語(yǔ)句塊
  剛才我們介紹了異常處理在字節(jié)碼層面的細(xì)節(jié),但是我們還需要注意的是----由于finally能夠保證不管發(fā)生任何情況,都能夠執(zhí)行語(yǔ)句塊中的代碼,所以在日常編碼過(guò)程中我們?cè)诳赡馨l(fā)生異常的地方(或者是不會(huì)發(fā)生異常的地方)經(jīng)常使用finally來(lái)釋放某些資源。
  下面我們從虛擬機(jī)層面來(lái)看看如何保證finally語(yǔ)句塊中的代碼一定會(huì)執(zhí)行
  可以看到,其實(shí)編譯器是通過(guò)在每個(gè)分支后面增加冗余代碼的形式來(lái)保證finally語(yǔ)句塊中的代碼一定會(huì)被執(zhí)行。這里和書(shū)上講的有點(diǎn)出入,書(shū)上在講解這一塊的時(shí)候還是用jsr、jsr_w、ret等程序控制轉(zhuǎn)移指令來(lái)解釋的,但是javac在很早之前就不再為finally語(yǔ)句生成jsr和ret指令了。
  如果程序在try語(yǔ)句塊中執(zhí)行了return,那么代碼的行為如下:
  如果有返回值,將返回值保存在局部變量表;
  執(zhí)行跟在后面的冗余finally語(yǔ)句塊中的代碼;
  在finally執(zhí)行完之后,將事先保存在局部變量表中的返回值壓入操作數(shù)棧中之后返回。
  如果在try語(yǔ)句中拋出異常,那么代碼的行為如下:
  將異常保存在局部變量表中
  執(zhí)行finally語(yǔ)句塊中的代碼
  在執(zhí)行完finally語(yǔ)句塊中的代碼后,重新拋出這個(gè)事先保存好的異常。
  Java虛擬機(jī)中的同步(synchronization)使用monitor的進(jìn)入和退出來(lái)實(shí)現(xiàn)的。無(wú)論顯式同步(有明確的monitorenter和monitorexit指令),還是隱式同步(依賴方法調(diào)用和返回指令實(shí)現(xiàn))都是如此。
  在Java語(yǔ)言中,同步用得最多的地方可能是經(jīng)synchronized所修飾的同步方法。同步方法并不是用monitorenter和monitorexit來(lái)實(shí)現(xiàn)的,而是由方法調(diào)用指令讀取運(yùn)行時(shí)常量池中方法的ACC_SYNCHRONIZED標(biāo)志來(lái)隱式實(shí)現(xiàn)的。
  monitorenter和monitorexit指令用于編譯同步語(yǔ)句塊
  編譯器必須確保無(wú)論方法以何種方式完成(正常結(jié)束或者是異常結(jié)束),方法中調(diào)用過(guò)的每條monitorenter指令都必須有對(duì)應(yīng)的monitorexit指令得到執(zhí)行。為了確保在方法異常完成時(shí),monitorenter和monitorexit指令依然可以正確配對(duì)執(zhí)行,編譯器會(huì)自動(dòng)生成一個(gè)異常處理器,這個(gè)異常處理器宣稱自己可以處理所有異常,它的代碼用來(lái)執(zhí)行monitorexit指令。
  到這里深入理解Java之java虛擬機(jī)干凈利落的規(guī)范總結(jié)就結(jié)束了,不足之處還望大家多多包涵!!覺(jué)得收獲的話可以點(diǎn)個(gè)關(guān)注收藏轉(zhuǎn)發(fā)一波喔,謝謝大佬們支持。
 

免責(zé)聲明:本文內(nèi)容由互聯(lián)網(wǎng)用戶自發(fā)貢獻(xiàn)自行上傳,本網(wǎng)站不擁有所有權(quán),也不承認(rèn)相關(guān)法律責(zé)任。如果您發(fā)現(xiàn)本社區(qū)中有涉嫌抄襲的內(nèi)容,請(qǐng)發(fā)送郵件至:operations@xinnet.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),本站將立刻刪除涉嫌侵權(quán)內(nèi)容。

免費(fèi)咨詢獲取折扣

Loading