從基本結構到Java 7新特性(8)

發表于:2013-01-05來源:ImportNew作者:朱偉杰點擊數: 標簽:java
把上面的反匯編代碼和我們平時所見的x86架構的匯編代碼相比較,我們會發現這兩者的結構有點相似,都使用了操作碼;不過,有一點不同的地方是Java字節

  把上面的反匯編代碼和我們平時所見的x86架構的匯編代碼相比較,我們會發現這兩者的結構有點相似,都使用了操作碼;不過,有一點不同的地方是Java字節碼并不會在操作數里寫入寄存器的名稱、內存地址或者偏移量。之前已經說過,JVM用的是棧,它不會使用寄存器。和使用寄存器的x86架構不同,它自己負責內存的管理。它用索引例如15和23來代替實際的內存地址。15和23都是當前類(這里是UserService類)的常量池里的索引。簡而言之,JVM為每個類創建了一個常量池,并且這個常量池里保存了實際目標的引用。

  每行反匯編代碼的解釋如下:

  aload_0:把局部變量數組中索引為#0的變量添加到操作數棧上。索引#0所表示的變量是this,即是當前實例的引用。

  getfield #15:把當前類的常量池里的索引為#15的變量添加到操作數棧。這里添加的是UserAdmin的admin成員變量。因為admin變量是個類的實例,因此添加的是一個引用。

  aload_1:把局部變量數組里的索引為#1的變量添加到操作數棧。來自局部變量數組里的索引為1的變量是方法的一個參數。因此,在調用add()方法的時候,會把userName指向的String的引用添加到操作數棧上。

  invokevirtual #23:調用當前類的常量池里的索引為#23的方法。這個時候,通過getfile和aload_1添加到操作數棧上的引用都被作為方法的參數。當方法運行完成并且返回時,它的返回值會被添加到操作數棧上。

  pop:把通過invokevirtual調用的方法的返回值從操作數棧里彈出來。你可以看到,在前面的例子里,用老的類庫編譯的那段代碼是沒有返回值的。簡而言之,正因為之前的代碼沒有返回值,所以沒必要吧把返回值從操作數棧上給彈出來。

  return:結束當前方法調用

  下圖可以幫助你更好地理解上面的內容。

  圖 6: Java字節碼裝載到運行時數據區示例

  順便提一下,在這個方法里,局部變量數組沒有被修改。所以上圖只顯示了操作數棧的變化。不過,大部分的情況下,局部變量數組也是會改變的。局部變量數組和操作數棧之間的數據傳輸是使用通過大量的load指令(aload,iload)和store指令(astore,istore)來實現的。

  在這個圖里,我們簡單驗證了運行時常量池和JVM棧的描述。當JVM運行的時候,每個類的實例都會在堆上進行分配,User,UserAdmin,UserService以及String等類的信息都會保存在方法區。

  執行引擎(Execution Engine)

  通過類裝載器裝載的,被分配到JVM的運行時數據區的字節碼會被執行引擎執行。執行引擎以指令為單位讀取Java字節碼。它就像一個CPU一樣,一條一條地執行機器指令。每個字節碼指令都由一個1字節的操作碼和附加的操作數組成。執行引擎取得一個操作碼,然后根據操作數來執行任務,完成后就繼續執行下一條操作碼。

  不過Java字節碼是用一種人類可以讀懂的語言編寫的,而不是用機器可以直接執行的語言。因此,執行引擎必須把字節碼轉換成可以直接被JVM執行的語言。字節碼可以通過以下兩種方式轉換成合適的語言。

  解釋器:一條一條地讀取,解釋并且執行字節碼指令。因為它一條一條地解釋和執行指令,所以它可以很快地解釋字節碼,但是執行起來會比較慢。這是解釋執行的語言的一個缺點。字節碼這種“語言”基本來說是解釋執行的。

  即時(Just-In-Time)編譯器:即時編譯器被引入用來彌補解釋器的缺點。執行引擎首先按照解釋執行的方式來執行,然后在合適的時候,即時編譯器把整段字節碼編譯成本地代碼。然后,執行引擎就沒有必要再去解釋執行方法了,它可以直接通過本地代碼去執行它。執行本地代碼比一條一條進行解釋執行的速度快很多。編譯后的代碼可以執行的很快,因為本地代碼是保存在緩存里的。

  不過,用JIT編譯器來編譯代碼所花的時間要比用解釋器去一條條解釋執行花的時間要多。因此,如果代碼只被執行一次的話,那么最好還是解釋執行而不是編譯后再執行。因此,內置了JIT編譯器的JVM都會檢查方法的執行頻率,如果一個方法的執行頻率超過一個特定的值的話,那么這個方法就會被編譯成本地代碼。

  圖 7:Java編譯器和JIT編譯器

  JVM規范沒有定義執行引擎該如何去執行。因此,JVM的提供者通過使用不同的技術以及不同類型的JIT編譯器來提高執行引擎的效率。

  大部分的JIT編譯器都是按照下圖的方式來執行的:

  圖 8: JIT編譯器

  JIT編譯器把字節碼轉換成一個中間層表達式,一種中間層的表示方式,來進行優化,然后再把這種表示轉換成本地代碼。

  Oracle Hotspot VM使用一種叫做熱點編譯器的JIT編譯器。它之所以被稱作”熱點“是因為熱點編譯器通過分析找到最需要編譯的“熱點”代碼,然后把熱點代碼編譯成本地代碼。如果已經被編譯成本地代碼的字節碼不再被頻繁調用了,換句話說,這個方法不再是熱點了,那么Hotspot VM會把編譯過的本地代碼從cache里移除,并且重新按照解釋的方式來執行它。Hotspot VM分為Server VM和Client VM兩種,這兩種VM使用不同的JIT編譯器。

  Figure 9: Hotspot Client VM and Server VM.

  Client VM 和Server VM使用完全相同的運行時,不過如上圖所示,它們所使用的JIT編譯器是不同的。Server VM用的是更高級的動態優化編譯器,這個編譯器使用了更加復雜并且更多種類的性能優化技術。

  IBM 在IBM JDK 6里不僅引入了JIT編譯器,它同時還引入了AOT(Ahead-Of-Time)編譯器。它使得多個JVM可以通過共享緩存來共享編譯過的本地代碼。簡而言之,通過AOT編譯器編譯過的代碼可以直接被其他JVM使用。除此之外,IBM JVM通過使用AOT編譯器來提前把代碼編譯器成JXE(Java EXecutable)文件格式來提供一種更加快速的執行方式。

原文轉自:http://www.anti-gravitydesign.com

国产97人人超碰caoprom_尤物国产在线一区手机播放_精品国产一区二区三_色天使久久综合给合久久97