1. 程式人生 > 實用技巧 >JVM學習-05:JVM之本地方法(Native Method)與本地方法棧(Native Method Stack)

JVM學習-05:JVM之本地方法(Native Method)與本地方法棧(Native Method Stack)

一.本地方法

1.什麼是Native Method

一個Native Method就是一個Java呼叫非Java程式碼的介面。一個Native Method是這樣一個Java方法,該方法的實現由非Java語言實現。

"A native method is a Java method whose implementation is provided by non-java code."在定義一個native method時,並不提供實現體(有些像定義一個Java Interface) ,因為其實現體是由非java語言在外面實現的。本地介面的作用是融合不同的程式語言為Java所用,它的初衷是融合C/C++程式。

識別符號 native 可以與其它Java識別符號連用,但是 abstract 除外。如:

public class IHaveNatives {
    public native void Native1(int x);
    public native static long Native2();
    private native synchronized float Native3(Object o);
    native void Native4(int[] ary) throws Exception;
}    

Navtive 方法是 Java 通過 JNI 直接呼叫本地 C/C++ 庫

,可以認為是 Native 方法相當於 C/C++ 暴露給 Java 的一個介面,Java 通過呼叫這個介面從而呼叫到 C/C++ 方法。當執行緒呼叫 Java 方法時,虛擬機器會建立一個棧幀並壓入 Java 虛擬機器棧。當它呼叫的是 native 方法時,虛擬機器會保持 Java 虛擬機器棧不變,虛擬機器只是簡單地動態連結並直接呼叫指定的 native 方法。

2.為什麼要使用Native Method

  • 與Java環境外互動:有時Java應用需要與Java外面的環境互動,這是本地方法存在的主要原因。Java需要與一些底層系統,如作業系統或某些硬體交換資訊時的情況。本地方法為我們提供了一個非常簡潔的按口,無需去了解Java應用之外的繁瑣的細節。
  • 與作業系統互動:JVM支援著Java語言本身和執行時庫,它是Java程式賴以生存的平臺,它由一個直譯器(解釋位元組碼)和一些連線到原生代碼的庫組成。它經常依賴於一些底層作業系統的支援。通過使用本地方法,我們得以用Java實現了jre的與底層系統的互動,甚至JVM的一些部分就是用C寫的
  • Sun's JavaSun的直譯器是用C實現的,這使得它能像一些普通的C一樣與外部互動。jre大部分是用Java實現的,它也通過一些本地方法與外界互動。例如:類java.lang.Thread的 setPriority() 方法是用Java實現的,但是它實現呼叫的是該類裡的本地方法 setPriority0()。這個本地方法是用C實現的,並被植入JVM內部。

3.JVM怎樣使Native Method跑起來

當一個類第一次被使用到時,這個類的位元組碼會被載入到記憶體,並且只會載入一次。在這個被載入的位元組碼的入口維持著一個該類所有方法描述符的list,這些方法描述符包含這樣一些資訊:方法程式碼存於何處,它有哪些引數,方法的描述符(public之類)等等。

如果一個方法描述符內有native,這個描述符塊將有一個指向該方法的實現的指標。這些實現在一些DLL檔案內,但是它們會被作業系統載入到java程式的地址空間。當一個帶有本地方法的類被載入時,其相關的DLL並未被載入,因此指向方法實現的指標並不會被設定。當本地方法被呼叫之前,這些DLL才會被載入,這是通過呼叫java.system.loadLibrary()實現的。

二.本地方法棧

1.概念

Java虛擬機器棧用於管理Java方法的呼叫,而本地方法棧用於管理本地方法的呼叫

  • 本地方法棧是一個後入先出(Last In First Out)棧,也是執行緒私有的,生命週期隨著執行緒啟動而產生,執行緒結束而消亡。本地方法是使用C語言實現的。
  • 允許被實現成固定或者是可動態擴充套件的記憶體大小。(在記憶體溢位方面是相同的)
    • 如果執行緒請求分配的棧容量超過本地方法棧允許的最大容量, Java虛擬機器將會丟擲一個StackOverflowError異常。
    • 如果本地方法棧可以動態擴充套件,並且在嘗試擴充套件的時候無法申請到足夠的記憶體,或者在建立新的執行緒時沒有足夠的記憶體去建立對應的本地方法棧,那麼Java虛擬機器將會丟擲一個OutOfMemoryError異常。
  • 它的具體做法是Native Method stack中登記native方法,在Execution Engine執行時載入本地方法庫

當某個執行緒呼叫一個本地方法時,它就進入了一個全新的並且不再受虛擬機器限制的世界。它和虛擬機器擁有同樣的許可權

  • 本地方法可以通過本地方法介面來訪問虛擬機器內部的執行時資料區。
  • 它甚至可以直接使用本地處理器中的暫存器。
  • 直接從本地記憶體的堆中分配任意數量的記憶體。

2.本地方法是如何工作的

當一個執行緒呼叫一個本地方法時,本地方法又回撥虛擬機器中的另一個Java方法。下面這幅圖展示了java虛擬機器內部執行緒執行的全景圖。一個執行緒可能在整個生命週期中都執行Java方法,操作他的Java棧;或者他可能毫無障礙地在Java棧和本地方法棧之間跳轉。

該執行緒首先呼叫了兩個Java方法,而第二個Java方法又呼叫了一個本地方法,這樣導致虛擬機器使用了一個本地方法棧。圖中的本地方法棧顯示為 一個連續的記憶體空間。假設這是一個C語言棧,期間有兩個C函式,他們都以包圍在虛線中的灰色塊表示。第一個C函式被第二個Java方法當做本地方法呼叫, 而這個C函式又呼叫了第二個C函式。之後第二個C函式又通過本地方法介面回調了一個Java方法(第三個Java方法)。最終這個Java方法又呼叫了一個Java方法(它成為圖中的當前方法)。

就像其他執行時記憶體區一樣,本地方法棧佔用的記憶體區也不必是固定大小的,他可以根據需要動態擴充套件或者收縮。某些是實現也允許使用者或者程式設計師指定該記憶體區的初始大小以及最大,最小值。

並不是所有的JVM都支援本地方法。因為Java虛擬機器規範並沒有明確要求本地方法棧的使用語言、具體實現方式、資料結構等。如果JVM產品不打算支援native方法,也可以無需實現本地方法棧。在Hotspot JVM中,直接將本地方法棧和虛擬機器棧合二為一

參考連結