1. 程式人生 > >JVM詳解之:HotSpot VM中的Intrinsic methods

JVM詳解之:HotSpot VM中的Intrinsic methods

[toc] # 簡介 內建方法是什麼呢?它和inline method有什麼關係呢?內建方法是怎麼實現的呢?所有的問題都可以在本文找到答案。 # 什麼是Intrinsic Methods 什麼是內建方法呢? 維基百科給出的定義是這樣的: 在計算機軟體中,按照編譯器理論,固有方法(或內建方法)是可在給定程式語言中使用的方法,該程式語言的實現由編譯器專門處理。通常,它可以將自動生成的指令序列替換為原始方法呼叫,類似於內聯方法。與內聯方法不同,編譯器對內建方法有深入的瞭解,因此可以針對給定情況更好地整合和優化它。 實現內建方法的編譯器通常僅在程式請求優化時才啟用它們,否則會退回到語言執行時環境提供的預設實現。 所以總結一下,內建方法就是編譯器內建的方法實現。 # 內建方法的特點 內建方法有什麼特點呢?我們在這裡總結一下。 ## 多樣性 因為內建方法是在編譯器內部實現的,所以不同的虛擬機器,其內建方法是不一樣的。 我們不能直接說哪個方法是內建方法,因為不同的JVM是不同的。 ## 相容性 內建方法是在需要的時候才會使用的,如果在不需要的時候則會回退到普通的方法實現,也就是java程式碼的實現。 所以在java原始碼級別來看,內建方法和非內建方法是一樣的。他們的區別在於JVM的實現。 ## java語義的擴充套件 有些方法用普通的java程式碼是無法實現的。比如sun.misc.Unsafe.compareAndSwapInt()。 我們只能使用JNI或者內建方法來對其實現。所以內建方法可以實現對java語義的擴充套件。 一般來說,JDK和核心庫中,能使用內建方法優化都已經優化了。所以我們在平時的程式碼呼叫中,一定要儘可能的使用JDK的公共API和核心庫,這樣才能充分利用內建方法的特性,從而提升程式效率。 # Hotspot VM中的內建方法 那麼對於Hotspot VM來說,內建的方法有哪些呢? Hotspot VM中所有的內建方法都在src/share/vm/classfile/vmSymbols.hpp類中: ![](https://img-blog.csdnimg.cn/20200625173025105.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_0,text_aHR0cDovL3d3dy5mbHlkZWFuLmNvbQ==,size_35,color_8F8F8F,t_70) 上圖我只截取了部分標記為intrinsic方法的類的說明。 可以看到java.lang.Math中大部分的方法都是intrinsic的方法。 怎麼檢視我們程式碼中呼叫的方法是不是intrinsic方法呢? 很簡單,在java命令之前加上這些引數即可: ~~~java -XX:+UnlockDiagnosticVMOptions -XX:+PrintCompilation -XX:+PrintInlining ~~~ 舉個最常用的檢視java版本的例子: ~~~java java -XX:+UnlockDiagnosticVMOptions -XX:+PrintCompilation -XX:+PrintInlining version ~~~ 看下輸出結果: ![](https://img-blog.csdnimg.cn/20200625173513830.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_0,text_aHR0cDovL3d3dy5mbHlkZWFuLmNvbQ==,size_35,color_8F8F8F,t_70) 從結果可以很清楚的看到,java.lang.System.arraycopy方法是內建方法。 另外我們可以通過更加底層的組合語言來檢視,再新增 ~~~ -XX:+PrintAssembly ~~~ 我們看下輸出結果: ![](https://img-blog.csdnimg.cn/2020062517531637.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_0,text_aHR0cDovL3d3dy5mbHlkZWFuLmNvbQ==,size_35,color_8F8F8F,t_70) 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()方法: ~~~java @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): ~~~java https://gist.github.com/rednaxelafx/2830194 ~~~ C2級別修改(Example (XS) of adding an intrinsic method to HotSpot C2. Patch against HS20-b12): ~~~java 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的引數: ~~~java -XX:+UnlockExperimentalVMOptions -XX:+UseJVMCICompiler ~~~ 通過Graal,我們可以用java來實現Intrinsic方法,想想就讓人興奮。 # 總結 Intrinsic方法是一個非常有用的特性,希望大家能夠喜歡。 > 本文作者:flydean程式那些事 > > 本文連結:[http://www.flydean.com/jvm-intrinsic-method/](http://www.flydean.com/jvm-intrinsic-method/) > > 本文來源:flydean的部落格 > > 歡迎關注我的公眾號:程式那些事,更多精彩等著您!