原文連接:http://www.cubrid.org/blog/dev-platform/understanding-jvm-internalsjava
每一個使用Java的開發者都知道Java字節碼是在JRE中運行(JRE: Java 運行時環境)。JVM則是JRE中的核心組成部分,承擔分析和執行Java字節碼的工做,而Java程序員一般並不須要深刻了解JVM運行狀況就能夠開發出大型應用和類庫。儘管如此,若是你對JVM有足夠了解,就會對Java有更好的掌握,而且能解決一些看起來簡單但又還沒有解決的問題。程序員
因此,在本篇文章中,我將會介紹JVM工做原理,內部結構,Java字節碼的執行及指令的執行順序,並會介紹一些常見的JVM錯誤及其解決方案。最後會簡單介紹下Java SE7帶來的新特性。apache
JRE由Java API和JVM組成,JVM經過類加載器(Class Loader)加類Java應用,並經過Java API進行執行。編程
虛擬機(VM: Virtual Machine)是經過軟件模擬物理機器執行程序的執行器。最初Java語言被設計爲基於虛擬機器在而非物理機器,重而實現WORA(一次編寫,處處運行)的目的,儘管這個目標幾乎被世人所遺忘。因此,JVM能夠在全部的硬件環境上執行Java字節碼而無須調整Java的執行模式。數組
JVM的基本特性:緩存
Sun 公司開發了Java語言,但任何人均可以在遵循JVM規範的前提下開發和提供JVM實現。因此目前業界有多種不一樣的JVM實現,包括Oracle Hostpot JVM和IBM JVM。Google公司使用的Dalvik VM也是一種JVM實現,儘管其並未徹底遵循JVM規範。與基於棧機制的Java 虛擬機不一樣的是Dalvik VM是基於寄存器的,Java 字節碼也被轉換爲Dalvik VM使用的寄存器指令集。安全
JVM使用Java字節碼—一種運行於Java(用戶語言)和機器語言的中間語言,以達到WORA的目的。Java字節碼是部署Java程序的最小單元。性能優化
在介紹Java 字節碼以前,咱們先來看一下什麼是字節碼。下面涉及的案例是曾在一個真實的開發場景中遇到過的情境。服務器
一個曾運行無缺的程序在更新了類庫後卻不能再次運行,並拋出了以下異常:網絡
1 Exception in thread "main" java.lang.NoSuchMethodError: com.nhn.user.UserAdmin.addUser(Ljava/lang/String;)V 2 at com.nhn.service.UserService.add(UserService.java:14) 3 at com.nhn.service.UserService.main(UserService.java:19)
程序代碼以下,並在更新類庫以前不曾對這段代碼作過變動:
1 // UserService.java 2 … 3 public void add(String userName) { 4 admin.addUser(userName); 5 }
類庫中更新過的代碼先後對好比下:
1 // UserAdmin.java - Updated library source code 2 … 3 public User addUser(String userName) { 4 User user = new User(userName); 5 User prevUser = userMap.put(userName, user); 6 return prevUser; 7 } 8 // UserAdmin.java - Original library source code 9 … 10 public void addUser(String userName) { 11 User user = new User(userName); 12 userMap.put(userName, user); 13 }
簡單來講就是addUser()方法在更新以後返回void而在更新以後返回了User類型實例。而程序代碼由於不關心addUser的返回值,因此在使用的過程當中並未作過改變。
初看起來,com.mhn.user.UserAdmin.addUser()依然存在,但爲何會出現NoSuchMethodError?
主要緣由是程序代碼在更新類庫時並未從新編譯代碼,也就是說,雖然程序代碼看起來依然是在調用addUser方法而不關心其返回值,而對編譯的類文件來講,他是要明確知道調用方法的返回值類型的。
能夠經過下面的異常信息說明這一點:
java.lang.NoSuchMethodError: com.nhn.user.UserAdmin.addUser(Ljava/langString;)V
NoSuchMethodError 是由於"com.nhn.user.UserAdmin.addUser(Ljava/lang/String;)V"方法找不到引發的。看一下"Ljava/lang/String;"和後面的"V"。在Java字節碼錶示中,"L;"表示類的實例。因此上面的addUser方法須要一個java/lang/String對象做爲參數。就這個案例中,類庫中的addUser()方法的參數未發生變化,因此參數是正常的。再看一下異常信息中最後面的"V",它表示方法的返回值類型。在Java字節碼錶示中,"V"意味着該方法沒有返回值。因此上面的異常信息就是說須要一個java.lang.String參數且沒有任何返回值的com.nhn.user.UserAdmin.addUser方法找不到。
由於程序代碼是使用以前版本的類庫進編譯的,class文件中定義的是應該調用返回"V"類型的方法。然而,在改變類庫後,返回"V"類型的方法已不存在,取而代之的是返回類型爲"Lcom/nhn/user/User;"的方法。因此便發生了上面看到的NoSuchMethodError。
註釋
由於開發者未針對新類庫從新編譯程序代碼,因此發生了錯誤。儘管如此,類庫提供者卻也要爲此負責。由於以前沒有返回值的addUser()方法既然是public方法,但後面卻改爲了會返回user實現,這意味着方法簽名發生了明顯的變化。這意味了該類庫不能對以前的版本進行兼容,因此類庫提供者必須事前對此進行通知。
咱們從新回到Java 字節碼,Java 字節碼是JVM的基本元素,JVM自己就是一個用於執行Java字節碼的執行器。Java編譯器並不會把像C/C++那樣把高級語言轉爲機器語言(CPU執行指令),而是把開發者能理解的Java語言轉爲JVM理解的Java字節碼。由於Java字節碼是平臺無關的,因此它能夠在安裝了JVM(準確的說,是JRE環境)的任何硬件環境執行,即便它們的CPU和操做系統各不相同(因此在Windows PC機上開發和編譯的class文件在不作任何調整的狀況下就能夠在Linux機器上執行)。編譯後文件的大小與源文件大小基本一致,因此比較容易經過網絡傳輸和執行Java字節碼。
Java class文件自己是基於二進制的文件,因此咱們很難直觀的理解其中的指令。爲了管理這些class 文件, JVM提供了javap命令來對二進制文件進行反編譯。執行javap獲得的是直觀的java指令序列。在上面的案例中,經過對程序代碼執行javap -c就可獲得應用中的UserService.add()方法的指令序列,以下:
1 public void add(java.lang.String); 2 Code: 3 0: aload_0 4 1: getfield #15; //Field admin:Lcom/nhn/user/UserAdmin; 5 4: aload_1 6 5: invokevirtual #23; //Method com/nhn/user/UserAdmin.addUser:(Ljava/lang/String;)V 7 8: return
在上面的Java指令中,addUser()方法是在第五行被調用,即"5: invokevirtual #23"。這句的意思是索引位置爲23的方法會被調用,方法的索引位置是由javap程序標註的。invokevirtual是Java 字節碼中最經常使用到的一個操做碼,用於調用一個方法。另外,在Java字節碼中有4個表示調用方法的操做碼: invokeinterface, invokespecial, invokestatic, invokevirtual 。他們每一個的含義以下:
Java 字節碼的指令集包含操做碼(OpCode)和操做數(Operand)。像invokevirtual這樣的操做碼須要一個2字節長度的操做數。
對上面案例中的程序代碼,若是在更新類庫後從新編譯程序代碼,而後咱們再反編譯字節碼將看到以下結果:
1 public void add(java.lang.String); 2 Code: 3 0: aload_0 4 1: getfield #15; //Field admin:Lcom/nhn/user/UserAdmin; 5 4: aload_1 6 5: invokevirtual #23; //Method com/nhn/user/UserAdmin.addUser:(Ljava/lang/String;)Lcom/nhn/user/User; 7 8: pop 8 9: return
如上咱們看到#23對應的方法變成了具備返回值類型"Lcom/nhn/user/User;"的方法。
在上面的反編譯結果中,代碼前面的數字是具備什麼含義?
它是一個一字節數字,也許正所以JVM執行的代碼被稱爲「字節碼」。像 aload0, getfield_ 和 invokevirtual 都被表示爲一個單字節數字。(aload_0 = 0x2a, getfiled = 0xb4, invokevirtual = 0xb6)。所以Java字節碼錶示的最大指令碼爲256。
像aload0和aload1這樣的操做碼不須要任何操做數,所以aload_0的下一個字節就是下一個指令的操做碼。而像getfield和invokevirtual這樣的操做碼卻須要一個2字節的操做數,所以第一個字節裏的第二個指令getfield指令的一下指令是在第4個字節,其中跳過了2個字節。經過16進制編輯器查看字節碼以下:
2a b4 00 0f 2b b6 00 17 57 b1
在Java字節碼中,類實例表示爲"L;",而void表示爲"V",相似的其餘類型也有各自的表示。下表列出了Java字節碼中類型表示。
表1: Java字節碼裏的類型表示
Java 字節碼 | 類型 | 描述 |
---|---|---|
B | byte | 單字節 |
C | char | Unicode字符 |
D | double | 雙精度浮點數 |
F | float | 單精度浮點數 |
I | int | 整型 |
J | long | 長整型 |
L | 引用 | classname類型的實例 |
S | short | 短整型 |
Z | boolean | 布爾類型 |
[ | 引用 | 一維數組 |
表2: Java代碼的字節碼示例
java 代碼 | Java 字節碼錶示 |
---|---|
double d[][][] | [[[D |
Object mymethod(int i, double d, Thread t) | mymethod(I,D,Ljava/lang/Thread;)Ljava/lang/Object; |
在《Java虛擬機技術規範第二版》的4.3 描述符(Descriptors)章節中有關於此的詳細描述,在第6章"Java虛擬機指令集"中介紹了更多不一樣的指令。
在解釋類文件格式以前,先看一個在Java Web應用中常常發生的問題。
在Tomcat環境裏編寫和運行JSP時,JSP文件未被執行,並伴隨着以下錯誤:
Servlet.service() for servlet jsp threw exception org.apache.jasper.JasperException: Unable to compile class for JSP Generated servlet error: The code of method _jspService(HttpServletRequest, HttpServletResponse) is exceeding the 65535 bytes limit"
對於不一樣的Web應用容器,上面的錯誤信息會有些微差別,但核心信息是一致的,即65535字節的限制。這個限制是JVM定義的,用於規定方法的定義不能大於65535個字節。
下面我將先介紹65535個的字節限制,而後詳細說明爲何要有這個限制。
Java字節碼中,"goto"和"jsr"指令分別表示分支和跳轉。
goto [branchbyte1] [branchbyte2] jsr [branchbyte1] [branchbyte2]
這兩個操做指令都跟着一個2字節的操做數,而2個字節能表示的最大偏移量只能是65535。然而爲了支持更大範圍的分支,Java字節碼又分別定義了"gotow" 和 "jsrw" 用於接收4個字節的分支偏移量。
goto_w [branchbyte1] [branchbyte2] [branchbyte3] [branchbyte4] jsr_w [branchbyte1] [branchbyte2] [branchbyte3] [branchbyte4]
受這兩個指令所賜,分支能表示的最大偏移遠遠超過了65535,這麼說來java 方法就不會再有65535個字節的限制了。然而,因爲Java 類文件的各類其餘限制,java方法的定義仍然不可以超過65535個字節的限制。下面咱們經過對類文件的解釋來看看java方法不能超過65535字節的其餘緣由。
Java類文件的大致結構以下:
ClassFile { u4 magic; u2 minor_version; u2 major_version; u2 constant_pool_count; cp_info constant_pool[constant_pool_count-1]; u2 access_flags; u2 this_class; u2 super_class; u2 interfaces_count; u2 interfaces[interfaces_count]; u2 fields_count; field_info fields[fields_count]; u2 methods_count; method_info methods[methods_count]; u2 attributes_count; attribute_info attributes[attributes_count];}
上面的文件結構出自《Java虛擬機技術規範第二版》的4.1節"類文件結構"。
以前講過的UserService.class文件的前16個字節的16進製表示以下:
ca fe ba be 00 00 00 32 00 28 07 00 02 01 00 1b
咱們經過對這一段符號的分析來了解一個類文件的具體格式。
javap程序把class文件格式以可閱讀的方式輸出來。在對UserService.class文件使用"javap -verbose"命令分析時,輸出內容以下:
Compiled from "UserService.java" public class com.nhn.service.UserService extends java.lang.Object SourceFile: "UserService.java" minor version: 0 major version: 50 Constant pool:const #1 = class #2; // com/nhn/service/UserService const #2 = Asciz com/nhn/service/UserService; const #3 = class #4; // java/lang/Object const #4 = Asciz java/lang/Object; const #5 = Asciz admin; const #6 = Asciz Lcom/nhn/user/UserAdmin;;// … omitted - constant pool continued … { // … omitted - method information … public void add(java.lang.String); Code: Stack=2, Locals=2, Args_size=2 0: aload_0 1: getfield #15; //Field admin:Lcom/nhn/user/UserAdmin; 4: aload_1 5: invokevirtual #23; //Method com/nhn/user/UserAdmin.addUser:(Ljava/lang/String;)Lcom/nhn/user/User; 8: pop 9: return LineNumberTable: line 14: 0 line 15: 9 LocalVariableTable: Start Length Slot Name Signature 0 10 0 this Lcom/nhn/service/UserService; 0 10 1 userName Ljava/lang/String; // … Omitted - Other method information … }
因爲篇幅緣由,上面只抽取了部分輸出結果。在所有的輸出信息中,會爲你展現包括常量池和每一個方法內容等各類信息。
方法的65535個字節的限制受到了結構體method_info的影響。如上面"javap -verbose"的輸出所示,結構體methodinfo包括代碼(Code)、行號表(LineNumberTable)以及本地變量表(LocalVariableTable)。其中行號表、本地變量表以及代碼裏的異常表(exceptiontable)的總長度爲一個固定2字節的值。所以方法的大小不能超過行號表、本地變量表、異常表的長度,即不能超過65535個字節。
儘管不少人抱怨方法的大小限制,JVM規範也聲稱將會對此大小進行擴充,然而到目前爲止並無明確的進展。由於JVM技術規範裏定義要把幾乎整個類文件的內容都加載到方法區,所以若是方法長度將會對程序的向後兼容帶來極大的挑戰。
對於一個由Java編譯器錯誤而致使的錯誤的類文件將發生怎樣的狀況?若是是在網絡傳輸或文件複製過程當中,類文件被損壞又將發生什麼?
爲了應對這些場景,Java類加載器的加載過程被設計爲一個很是嚴謹的處理過程。JVM規範詳細描述了這個過程。
註釋
咱們如何驗證JVM成功執行了類文件的驗證過程?如何驗證不一樣的JVM實現是否符合JVM規範?爲此,Oracle提供了專門的測試工具:TCK(Technology Compatibility Kit)。TCK經過執行大量的測試用例(包括大量經過不一樣方式生成的錯誤類文件)來驗證JVM規範。只有經過TCK測試的JVM才能被稱做是JVM。
相似TCK,還有一個JCP(Java Community Process; http://jcp.org),用於驗證新的Java技術規範。對於一個JCP,必須具備詳細的文檔,相關的實現以及提交給JSR(Java Specification Request)的TCK測試。若是用戶想像JSR同樣使用新的Java技術,那他必須先從RI提供者那裏獲得許可,或者本身直接實現它並對之進行TCK測試。
Java程序的執行過程以下圖所示:
圖1: Java代碼執行過程
類加載器把Java字節碼載入到運行時數據區,執行引擎負責Java字節碼的執行。
Java提供了動態加載的特性,只有在運行時第一次遇到類時纔會去加載和連接,而非在編譯時加載它。JVM的類加載器負責類的動態加載過程。Java類加載器的特色以下:
每一個類加載器都有本身的空間,用於存儲其加載的類信息。當類加載器須要加載一個類時,它經過FQCN)(Fully Quanlified Class Name: 全限定類名)的方式先在本身的存儲空間中檢測此類是否已存在。在JVM中,即使具備相同FQCN的類,若是出如今了兩個不一樣的類加載器空間中,它們也會被認爲是不一樣的。存在於不一樣的空間意味着類是由不一樣的加載器加載的。
下圖解釋了類加載器的代理模型:
圖2: 類加載器的代理模型
當JVM請示類加載器加載一個類時,加載器老是按照從類加載器緩存、父類加載器以及本身加載器的順序查找和加載類。也就是說加載器會先從緩存中判斷此類是否已存在,若是不存在就請示父類加載器判斷是否存在,若是直到Bootstrap類加載器都不存在該類,那麼當前類加載器就會從文件系統中找到類文件進行加載。
像Web應用服務器(WAS: Web Application Server)等框架經過使用用戶自定義加載器使Web應用和企業級應用能夠隔離開在各自的類加載空間獨自運行。也就是說能夠經過類加載器的代理模型來保證應用的獨立性。不一樣的WAS在自定義類加載器時會有略微不一樣,但都不外乎使用加載器的層次結構原理。
若是一個類加載器發現了一個未加載的類,則該類的加載和連接過程以下圖:
圖3: 類加載步驟
每一步的具體描述以下:
JVM規範定義了規則,但也容許在運行時靈活處理。
圖4: 運行時數據區結構
運行時數據區是JVM程序運行時在操做系統上分配的內存區域。運行時數據區又可細分爲6個部分,即:爲每一個線程分別建立的PC寄存器、JVM棧、本地方法棧和被全部線程共用的數據堆、方法區和運行時常量池。
JVM 棧:每一個線程都有一個JVM棧,並跟隨線程的啓動而建立。其中存儲的數據無素稱爲棧幀(Stack Frame)。JVM會每把棧楨壓入JVM棧或從中彈出一個棧幀。若是有任何異常拋出,像printStackTrace()方法輸出的棧跟蹤信息的每一行表示一個棧幀。
圖5: JVM棧結構
本地方法棧:爲非Java編寫的本地代程定義的棧空間。也就是說它基本上是用於經過JNI(Java Native Interface)方式調用和執行的C/C++代碼。根據具體狀況,C棧或C++棧將會被建立。
方法區:方法區是被全部線程共用的內存空間,在JVM啓動時建立。它存儲了運行時常量池、字段和方法信息、靜態變量以及被JVM載入的全部類和接口的方法的字節碼。不一樣的JVM提供者在實現方法區時會一般有不一樣的形式。在Oracle的Hotspot JVM裏方法區被稱爲Permanent Area(永久區)或Permanent Generation(PermGen, 永久代)。JVM規範並對方法區的垃圾回收未作強制限定,所以對於JVM實現者來講,方法區的垃圾回收是可選操做。
運行時常量池:一個存儲了類文件格式中的常量池表的內存空間。這部分空間雖然存在於方法區內,但卻在JVM操做中扮演着舉足輕重的角色,所以JVM規範單獨把這一部分拿出來描述。除了每一個類或接口中定義的常量,它還包含了全部對方法和字段的引用。所以當須要一個方法或字段時,JVM經過運行時常量池中的信息從內存空間中來查找其相應的實際地址。
數據堆:堆中存儲着全部的類實例或對象,而且也是垃圾回收的目標場所。當涉及到JVM性能優化時,一般也會說起到數據堆空間的大小設置。JVM提供者能夠決定劃分堆空間或者不執行垃圾回收。
咱們再回到先前討論的反編譯過的字節碼中:
public void add(java.lang.String); Code: 0: aload_0 1: getfield #15; //Field admin:Lcom/nhn/user/UserAdmin; 4: aload_1 5: invokevirtual #23; //Method com/nhn/user/UserAdmin.addUser:(Ljava/lang/String;)Lcom/nhn/user/User; 8: pop 9: return
比較一下上面反編譯過的字節碼和咱們常見的基於x86架構的機器碼的區別,雖然它們着類似的格式、操做碼,但有一個明顯的區別:Java字節碼中沒有寄存器名稱、內存地址或者操做數的偏移位置。正如前所述,JVM使用的是棧模型,所以它並不須要x86架構中使用的寄存器。由於JVM本身管理內存,因此Java字節碼中使用像1五、23這樣的索引值而非直接的內存地址。上面的15和23指向的是當前類的常量池中的位置(即UserService類)。也就是JVM爲每一個類建立一個常量池,並在常量池中存儲真實對象的引用。
上面每行代碼的解釋以下:
下圖將幫忙容易理解上面的文字解釋:
圖6: 從運行時數據區加載Java字節碼示例
做爲示例,上面的方法中本地變量數組中的值不曾有任何改變,因此上圖中咱們只看到操做數棧的變化。實際上,在大多數場景中本地變量數組也是被髮生變化的。數據經過加載指令(aload, iload)和存儲指令(astore, istore)在本地變量數組和操做數棧之間發生變化和移動。
在本章節咱們對運行時常量池和JVM棧做了清晰的介紹。在JVM運行時,每一個類的實例被分配到數據堆上,類信息(包括User, UserAdmin, UserService, String)等被存儲在方法區。
JVM經過類加載器把字節碼載入運行時數據區是由執行引擎執行的。執行引擎以指令爲單位讀入Java字節碼,就像CPU一個接一個的執行機器命令同樣。每一個字節碼命令包含一字節的操做碼和可選的操做數。執行引擎讀取一個指令並執行相應的操做數,而後去讀取並執行下一條指令。
儘管如此,Java字節碼仍是以一種能夠理解的語言編寫的,而不像那些機器直接執行的沒法讀懂的語言。因此JVM的執行引擎必需要把字節碼轉換爲能被機器執行的語言指令。執行引擎有兩種經常使用的方法來完成這一工做:
然而,即時編譯器在編譯代碼時比逐一解釋和執行每條指令更耗時,因此若是代碼只會被執行一次,解釋執行可能會具備更好的性能。因此JVM經過檢查方法的執行頻率,而後只對達到必定頻率的方法纔會作即時編譯。
圖7: Java編譯器和即時編譯器
JVM規範中並未強行約束執行引擎如何運行。因此不一樣的JVM在實現各類的執行引擎時經過各類技術手段並引入多種即時編譯器來提高性能。
大部分的即時編譯器運行流程以下圖:
圖8: 即時編譯器
即時編譯器先把字節碼轉爲一種中間形式的表達式(IR: Itermediate Representation),並對之進行優化,而後再把這種表達式轉爲本地代碼。
Oracel Hotspot VM使用的即時編譯器稱爲Hotspot編譯器。之因此稱爲Hotspot是由於Hotspot Compiler會根據分析找到具備更高編譯優先級的熱點代碼,而後所這些熱點代碼轉爲本地代碼。若是一個被編譯過的方法再也不被頻繁調用,也即再也不是熱點代碼,Hotspot VM會把這些本地代碼從緩存中刪除並對其再次使用解釋器模式執行。Hotspot VM有Server VM和Client VM以後,它們所使用的即時編譯器也有所不一樣。
圖9: Hotspot ClientVM 和Server VM
Client VM和Server VM使用相同的運行時環境,如上圖所示,它們的區別在於使用了不一樣的即時編譯器。Server VM經過使用多種更爲複雜的性能優化技術從而具備更好的表現。
IBM VM在他的IBM JDK6中引入了AOT(Ahead-Of-Time) 編譯器技術。經過此種技術使得多個JVM之間能經過共享緩存分享已編譯的本地代碼。也就是說經過AOT編譯器編譯的代碼能被其餘JVM直接使用而無須再次編譯。另外IBM JVM經過使用AOT編譯器把代碼預編譯爲JXE(Java Executable)文件格式從而提供了一種快速執行代碼的方式。
大多數的Java性能提高都是經過優化執行引擎的性能實現的。像即時編譯等各類優化技術被不斷的引入,從而使得JVM性能獲得了持續的優化和提高。老舊的JVM與最新的JVM之間最大的差別其實就來自於執行引擎的提高。
Hotspot編譯器從Java 1.3開始便引入到了Oracle Hotspot VM中,而即時編譯器從Android 2.2開始便被引入到了Android Dalvik VM中。
註釋
像其餘使用了像字節碼同樣的中間層語言的編譯語言,VM在執行中間層字節碼時也像JVM執行字節碼同樣,引入了即時編譯等技術來提升VM的執行效率。像Microsoft的.Net語言,其運行時的VM叫作CLR(Common Language Runtime)。CLR執行一種相似字節碼的語言CIL(Common Intermediate Language)。CLR同時提供了AOT編譯器和即時編譯器。由於若是使用C#或VB.NET編寫程序,編譯器會把源碼編譯成CIL,CLR經過使用即時編譯器來執行CIL。CLR也有垃圾回收,而且和JVM同樣也是以基於棧的方式運行。
雖然使用Java並不須要瞭解Java是如何被創造出來的,而且不少程序員在並無深刻研究JVM的狀況下依然開發出了不少偉大的應用和類庫。可是若是可以瞭解JVM,就能對Java 有更多深刻的提升,並在解決文中案例問題場景時有所幫助。
除了上文所述,JVM還有不少特性和技術細節。JVM技術規範爲JVM開發者提供了靈活的規範空間,以幫忙開發者能使用多種技術手段創造出具備更好性能的JVM實現。另外雖然垃圾回收手術已被不少具備相似VM能力的編程語言做爲經常使用的性能提高的新手段,但因有不少對其詳細介紹的資料,因此這裏沒有深刻講解。
原文做者:Se Hoon Park,消息平臺開發團隊,NHN公司。