JVM詳解之:HotSpot VM中的Intrinsic methods

簡介

內置方法是什麼呢?它和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類中:

上圖我只截取了部分標記為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):

//gist.github.com/rednaxelafx/2830194

C2級別修改(Example (XS) of adding an intrinsic method to HotSpot C2. Patch against HS20-b12):

//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方法是一個非常有用的特性,希望大家能夠喜歡。

本文作者:flydean程序那些事

本文鏈接://www.flydean.com/jvm-intrinsic-method/

本文來源:flydean的博客

歡迎關注我的公眾號:程序那些事,更多精彩等着您!

Tags: