程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> JAVA編程入門知識 >> Java(JVM)虛擬機結構基礎

Java(JVM)虛擬機結構基礎

編輯:JAVA編程入門知識
  前段時間由於研究原來廣為傳播的String和StringBuffer的性能問題,自己做了幾個小實驗並得出一些結論,但是從網友的反應來看那個研究並沒有起到應有的目的,而且網友也很中肯的提出了自己的意見並對實驗中的一些內容指出了其缺陷,針對他們的反應我又反編譯了代碼來進行對比, <!-- frame contents --> <!-- /frame contents --> 但是幾位網友仍然不是很信服,而且上次實驗的結果和反編譯得到的結論並不能完全吻合,因為反編譯代碼的對比基本上是基於語句的多少,因此這個這個對比也確實不能使人信服,但是這給我的下一步行動指引了方向:研究JVM指令和JVM結構,在對反編譯後的代碼有完全的理解才能給出可能使人信服的結論。
  
  本文以及以後將要寫的一些文章就是我研究JVM規范的一些心得,我希望在和大家共同理解的基礎上進行我們下一輪的深入研究。
  
  好,閒話少說,開始我們的正文。
  
  JVM執行的對象就是大家非常熟悉的class文件,我們也稱為類文件,JVM規范定義的這個編譯完成的代碼文件(雖然並非強制要求是實際的文件)的格式非常的詳實,但是我們這裡只說一些宏觀的內容,以後有機會再研究細節的內容吧。JVM要求的類文件的格式是和硬件和操作系統無關的一種二進制格式,它精確定義了類或者接口的表示,它甚至包含了字節順序這樣的細節,而字節順序在特定平台的目標文件格式中一般都是固定的,不會進行說明。
  
  JVM所支持的數據類型和Java語言規范中定義的幾乎一樣,請注重是幾乎一樣!也就是原始類型和引用類型,他們可以被存儲在變量表中,也可以作為參數傳遞、被方法返回,更通常的就是成為操作的對象。為什麼和Java語言規范中定義的不完全一樣呢?因為JVM中有一種Java語言所沒有的原始類型:返回地址類型(returnAddress type)。該類型是jsr, ret以及jsr_w指令需要使用到的,它的值是JVM指令的操作碼的指針,並且它的值是不能被運行中的程序所修改的。
  
  另外需要提到的就是布爾類型的值,雖然在Java語言中它是完全獨立的值,但是在JVM中只提供了對它的有限支持,表現在:
  
  沒有單獨的操作布爾類型的指令,源代碼中的布爾類型的操作在編譯以後是作為int類型的值進行操作的。
  
  JVM直接支持布爾數組,newarray指令可以創建布爾數組,而它的訪問和修改操作卻是使用byte類型的數組的操作指令進行的:baload,bastore。(在JDK1.0,1,1以及1.2中,布爾數組被編碼為byte數組,每個元素是8位)JVM用1代表true,用0代表false,編譯器將源代碼中的布爾類型映射為JVM中的int類型,而且必須和JVM的要求一致。
  
  另外JVM規范中對於浮點類型的數據有大段的說明,我沒有怎麼看,主要是討論JVM的浮點型和IEEE 754的關系的。
  
  關於類型的另外一個需要提一下的是類型檢查。JVM期望幾乎所有的類型檢查已經在運行之前完成了(通常是由編譯器進行檢查的)而不用JVM自己來檢查。原始類型的值不需要被標記或者在運行時被檢查以確定他們的類型,同樣他們也不用和引用類型的值進行區分,區分工作是由JVM的指令集來完成的,JVM的指令集使用不同指令來區分它要操作的值的類型,例如iadd, ladd, fadd以及dadd是用於將兩個數字相加並產生數字類型結果的所有JVM指令,但是每個指令都是針對特定類型的,分別對應int, long, float以及double。
  
  JVM包含對對象的顯式支持。類是動態分配的類實例或者是一個數組,JVM中的引用類型就是對一個對象的引用,引用類型的值可以想象為對象的指針,一個對象同時可能存在多個對它的引用,對象總是通過引用被操作、傳遞或者測試的。
  
  對於引用類型,需要提及的一點就是關於null,它最初是沒有運行時類型的,但是它可以被轉換為任何類型,而且對於null,JVM並沒有要求任何具體的值與之對應。
  
  說完上面這些,我們就開始進入我學習JVM時最想了解的部分了,大家可要打起精神哦。
  
  JVM為運行一個程序定義了幾種數據區(Data Area),包括:pc寄存器、JVM堆棧、堆、方法區(Method Area)、運行時常量池(Runtime Constant Pool)以及本機方法堆棧(Native Method Stacks),這些數據區根據其生存期可以分為兩種,一種就是和JVM的生存期相同(包括堆和方法區),一種和線程的生存期相同(其它的),和JVM生存期相同的數據區在JVM啟動的時候被創建並在JVM退出的時候被銷毀,而和線程生存期相同的數據區是每個線程一個的,他們在線程創建的時候被創建,在線程被銷毀的時候被銷毀。
  
  由於JVM可以同時支持運行多個線程,因此每個線程必然需要各自的PC(program counter)寄存器,無論從什麼角度講,每個JVM線程只能在一個時間只能執行一個方法,該方法也就是線程的當前方法,假如該方法不是本機方法,那麼PC寄存器保存的就是當前指令(JVM的指令)的地址,假如是當前方法是本機方法,PC寄存器的值就沒有被定義。JVM的PC寄存器的大小足夠大,可以容納一個returnAddress類型或者特定平台的本機指針。
  
  每個JVM線程還擁有一個私有的JVM堆棧,它存儲幀(下一篇文章會講到)。JVM堆棧和像C這樣的傳統編程語言中的堆棧是類似的,它保存局部變量和部分結果,並且在方法調用和返回中也擔任一些職責。因為除了對幀的壓入和彈出操作外,對JVM堆棧不能直接進行操作,因此幀可能是在堆上分配的。假如一個線程中計算所需的JVM堆棧大於答應的大小,JVM會拋出StackOverflowError錯誤,假如JVM堆棧是可以動態伸縮的,假如需要擴展,但是又沒有足夠的內存可用或者沒有足夠的內存為一個新線程創建JVM堆棧,JVM會拋出OutOfMemoryError錯誤。
  
 
  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved