JVM詳解之:HotSpot VM中的Intrinsic methods
簡介
內建方法是什麼呢?它和inline method有什麼關係呢?內建方法是怎麼實現的呢?所有的問題都可以在本文找到答案。
什麼是Intrinsic Methods
什麼是內建方法呢?
維基百科給出的定義是這樣的:
在計算機軟體中,按照編譯器理論,固有方法(或內建方法)是可在給定程式語言中使用的方法,該程式語言的實現由編譯器專門處理。通常,它可以將自動生成的指令序列替換為原始方法呼叫,類似於內聯方法。與內聯方法不同,編譯器對內建方法有深入的瞭解,因此可以針對給定情況更好地整合和優化它。
實現內建方法的編譯器通常僅在程式請求優化時才啟用它們,否則會退回到語言執行時環境提供的預設實現。
所以總結一下,內建方法就是編譯器內建的方法實現。
內建方法的特點
內建方法有什麼特點呢?我們在這裡總結一下。
01
多樣性
因為內建方法是在編譯器內部實現的,所以不同的虛擬機器,其內建方法是不一樣的。
我們不能直接說哪個方法是內建方法,因為不同的JVM是不同的。
02
相容性
內建方法是在需要的時候才會使用的,如果在不需要的時候則會回退到普通的方法實現,也就是java程式碼的實現。
所以在java原始碼級別來看,內建方法和非內建方法是一樣的。他們的區別在於JVM的實現。
03
java語義的擴充套件
有些方法用普通的java程式碼是無法實現的。比如
sun.misc.Unsafe.compareAndSwapInt()
我們只能使用JNI或者內建方法來對其實現。所以內建方法可以實現對java語義的擴充套件。
一般來說,JDK和核心庫中,能使用內建方法優化都已經優化了。所以我們在平時的程式碼呼叫中,一定要儘可能的使用JDK的公共API和核心庫,這樣才能充分利用內建方法的特性,從而提升程式效率。
Hotspot VM中的內建方法
那麼對於Hotspot VM來說,內建的方法有哪些呢?
Hotspot VM中所有的內建方法都在src/share/vm/classfile/vmSymbols.hpp類中:
上圖我只截取了部分標記為intrinsic方法的類的說明。
可以看到java.lang.Math中大部分的方法都是intrinsic的方法。
怎麼檢視我們程式碼中呼叫的方法是不是intrinsic方法呢?
很簡單,在java命令之前加上這些引數即可:
-XX:+UnlockDiagnosticVMOptions -XX:+PrintCompilation -XX:+PrintInlining
舉個最常用的檢視java版本的例子:
java -XX:+UnlockDiagnosticVMOptions -XX:+PrintCompilation -XX:+PrintInlining version
看下輸出結果:
從結果可以很清楚的看到,java.lang.System.arraycopy方法是內建方法。
-XX:+PrintAssembly
我們看下輸出結果:
invokestatic意味著該方法就是intrinsified方法。
intrinsic方法和內聯方法
內聯方法就是把呼叫方函式程式碼”複製”到呼叫方函式中,減少因函式呼叫開銷的技術。
intrinsic方法大部分都是內聯方法。
intrinsic方法的實現
前面我們提到了內建方法是在編譯器實現的。
在Hotspot VM中其實有3中編譯器。
第一種就是javac將java原始碼編譯成為位元組碼。
在這一層,只有一些math方法和bootstrapping的MethodHandle是在這一層實現的。
第二種就是在JIT的Client Compiler (C1)。
第三種就是在JIT的Server Compiler (C2)。
舉一個例子,我們看一下java.lang.System.currentTimeMillis()方法:
@HotSpotIntrinsicCandidate
public static native long currentTimeMillis();
JDK原始碼使用了HotSpotIntrinsicCandidate註解。這個註解只是表示該方法可能會被用於Intrinsic,而並不意味著一定使用Intrinsic。
這個方法在Interpreter級別是沒有intrinsified。因為這是一個native方法,所以會通過JNI呼叫底層的C++實現。
而在C1和C2級別,會使用intrinsified, 直接呼叫os::javaTimeMillis()。
好處就是減少了JNI的使用,提升效率。
好了問題來了,我們可以自己實現intrinsified方法嗎?
答案是可以,不過需要修改底層的JVM實現。
這裡有兩個具體的例子,感興趣的大家可以自行研究。
C1級別修改(First cut: C1 Class.isInstance intrinsic):
https://gist.github.com/rednaxelafx/2830194
C2級別修改(Example (XS) of adding an intrinsic method to HotSpot C2. Patch against HS20-b12):
https://gist.github.com/rednaxelafx/1986224
Graal
因為Hotspot VM是用C++編寫的,如果要新增Intrinsic方法,對於那些不熟悉C++的朋友來說就太難了。
沒關係,Oracle開發了一個專案叫做Graal。
Graal是一個用java編寫的新款JIT編譯器。
Graal是基於Java的JIT編譯器,是JDK 9中引入的實驗性Ahead-of-Time(AOT)編譯器的基礎。
開啟Graal的引數:
-XX:+UnlockExperimentalVMOptions -XX:+UseJVMCICompiler
通過Graal,我們可以用java來實現Intrinsic方法,想想就讓人興奮。
總結
Intrinsic方法是一個非常有用的特性,希望大家能夠喜歡。