欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

關于Java虛擬機HotSpot

 更新時間:2021年11月03日 14:56:59   作者:鳩摩  
這篇文章主要介紹了關于Java虛擬機HotSpot,在Java類中的一些方法會被由C/C++編寫的HotSpot虛擬機的C/C++函數(shù)調用,不過由于Java方法與C/C++函數(shù)的調用約定不同,所以并不能直接調用,需要JavaCalls::call()這個函數(shù)輔助調用,下面我們來看看文章對內容的具體介紹

我們寫的主類中的main()方法是如何被Java虛擬機調用到的?在Java類中的一些方法會被由C/C++編寫的HotSpot虛擬機的C/C++函數(shù)調用,不過由于Java方法與C/C++函數(shù)的調用約定不同,所以并不能直接調用,需要JavaCalls::call()這個函數(shù)輔助調用。(我把由C/C++編寫的叫函數(shù),把Java編寫的叫方法,后續(xù)也會延用這樣的叫法)如下圖所示。

從C/C++函數(shù)中調用的一些Java方法主要有:

  • (1)Java主類中的main()方法;
  • (2)Java主類裝載時,調用JavaCalls::call()函數(shù)執(zhí)行checkAndLoadMain()方法;
  • (3)類的初始化過程中,調用JavaCalls::call()函數(shù)執(zhí)行的Java類初始化方法<clinit>,可以查看JavaCalls::call_default_constructor()函數(shù),有對<clinit>方法的調用邏輯;
  • (4)我們先省略main方法的執(zhí)行流程(其實main方法的執(zhí)行也是先啟動一個JavaMain線程,套路都是一樣的),單看某個JavaThread的啟動過程。JavaThread的啟動最終都要通過一個native方法java.lang.Thread#start0()方法完成的,這個方法經過解釋器的native_entry入口,調用到了JVM_StartThread()函數(shù)。其中的static void thread_entry(JavaThread* thread, TRAPS)函數(shù)中會調用JavaCalls::call_virtual()函數(shù)。JavaThread最終會通過JavaCalls::call_virtual()函數(shù)來調用字節(jié)碼中的run()方法;
  • (5)在SystemDictionary::load_instance_class()這個能體現(xiàn)雙親委派的函數(shù)中,如果類加載器對象不為空,則會調用這個類加載器的loadClass()函數(shù)(通過call_virtual()函數(shù)來調用)來加載類。

當然還會有其它方法,這里就不一一列舉了。通過JavaCalls::call()JavaCalls::call_helper()等函數(shù)調用Java方法,這些函數(shù)定義在JavaCalls類中,

這個類的定義如下:

從C/C++函數(shù)中調用的一些Java方法主要有:

  • (1)Java主類中的main()方法;
  • (2)Java主類裝載時,調用JavaCalls::call()函數(shù)執(zhí)行checkAndLoadMain()方法;
  • (3)類的初始化過程中,調用JavaCalls::call()函數(shù)執(zhí)行的Java類初始化方法<clinit>,可以查看JavaCalls::call_default_constructor()函數(shù),有對<clinit>方法的調用邏輯;
  • (4)我們先省略main方法的執(zhí)行流程(其實main方法的執(zhí)行也是先啟動一個JavaMain線程,套路都是一樣的),單看某個JavaThread的啟動過程。JavaThread的啟動最終都要通過一個native方法java.lang.Thread#start0()方法完成的,這個方法經過解釋器的native_entry入口,調用到了JVM_StartThread()函數(shù)。其中的static void thread_entry(JavaThread* thread, TRAPS)函數(shù)中會調用JavaCalls::call_virtual()函數(shù)。JavaThread最終會通過JavaCalls::call_virtual()函數(shù)來調用字節(jié)碼中的run()方法;
  • (5)在SystemDictionary::load_instance_class()這個能體現(xiàn)雙親委派的函數(shù)中,如果類加載器對象不為空,則會調用這個類加載器的loadClass()函數(shù)(通過call_virtual()函數(shù)來調用)來加載類。

當然還會有其它方法,這里就不一一列舉了。通過JavaCalls::call() 、JavaCalls::call_helper()等函數(shù)調用Java方法,這些函數(shù)定義在JavaCalls類中,

這個類的定義如下:

源代碼位置:openjdk/hotspot/src/share/vm/runtime/javaCalls.hpp
 
class JavaCalls: AllStatic {
  static void call_helper(JavaValue* result, methodHandle* method, JavaCallArguments* args, TRAPS);
 public:
  
  static void call_default_constructor(JavaThread* thread, methodHandle method, Handle receiver, TRAPS);
 
  // 使用如下函數(shù)調用Java中一些特殊的方法,如類初始化方法<clinit>等
  // receiver表示方法的接收者,如A.main()調用中,A就是方法的接收者
  static void call_special(JavaValue* result, KlassHandle klass, Symbol* name,Symbol* signature, JavaCallArguments* args, TRAPS);
  static void call_special(JavaValue* result, Handle receiver, KlassHandle klass,Symbol* name, Symbol* signature, TRAPS); 
  static void call_special(JavaValue* result, Handle receiver, KlassHandle klass,Symbol* name, Symbol* signature, Handle arg1, TRAPS);
  static void call_special(JavaValue* result, Handle receiver, KlassHandle klass,Symbol* name, Symbol* signature, Handle arg1, Handle arg2, TRAPS);
 
  // 使用如下函數(shù)調用動態(tài)分派的一些方法
  static void call_virtual(JavaValue* result, KlassHandle spec_klass, Symbol* name,Symbol* signature, JavaCallArguments* args, TRAPS);
  static void call_virtual(JavaValue* result, Handle receiver, KlassHandle spec_klass,Symbol* name, Symbol* signature, TRAPS); 
  static void call_virtual(JavaValue* result, Handle receiver, KlassHandle spec_klass,Symbol* name, Symbol* signature, Handle arg1, TRAPS);
  static void call_virtual(JavaValue* result, Handle receiver, KlassHandle spec_klass,Symbol* name, Symbol* signature, Handle arg1, Handle arg2, TRAPS);
 
  // 使用如下函數(shù)調用Java靜態(tài)方法
  static void call_static(JavaValue* result, KlassHandle klass,Symbol* name, Symbol* signature, JavaCallArguments* args, TRAPS);
   static void call_static(JavaValue* result, KlassHandle klass,Symbol* name, Symbol* signature, TRAPS);
  static void call_static(JavaValue* result, KlassHandle klass,Symbol* name, Symbol* signature, Handle arg1, TRAPS);
  static void call_static(JavaValue* result, KlassHandle klass,Symbol* name, Symbol* signature, Handle arg1, Handle arg2, TRAPS);
 
  // 更低一層的接口,如上的一些函數(shù)可能會最終調用到如下這個函數(shù)
  static void call(JavaValue* result, methodHandle method, JavaCallArguments* args, TRAPS);
};


如上的函數(shù)都是自解釋的,通過名稱我們就能看出這些函數(shù)的作用。其中JavaCalls::call()函數(shù)是更低一層的通用接口。Java虛擬機規(guī)范定義的字節(jié)碼指令共有5個,分別為invokestatic 、invokedynamic 、invokestatic 、invokespecialinvokevirtual幾種方法調用指令。這些call_static() 、call_virtual()函數(shù)內部調用了call()函數(shù)。這一節(jié)我們先不介紹各個方法的具體實現(xiàn)。下一篇將詳細介紹。

我們選一個重要的main()方法來查看具體的調用邏輯。如下基本照搬R大的內容,不過我略做了一些修改,如下:

假設我們的Java主類的類名為JavaMainClass,下面為了區(qū)分java launcher里C/C++的main()Java層程序里的main(),把后者寫作JavaMainClass.main()方法。

從剛進入C/C++的main()函數(shù)開始:

啟動并調用HotSpot虛擬機的main()函數(shù)的線程執(zhí)行的主要邏輯如下:

main()
-> //... 做一些參數(shù)檢查
-> //... 開啟新線程作為main線程,讓它從JavaMain()函數(shù)開始執(zhí)行;該線程等待main線程執(zhí)行結束


在如上線程中會啟動另外一個線程執(zhí)行JavaMain()函數(shù),如下:

JavaMain()
-> //... 找到指定的JVM
-> //... 加載并初始化JVM
-> //... 根據Main-Class指定的類名加載JavaMainClass
-> //... 在JavaMainClass類里找到名為"main"的方法,簽名為"([Ljava/lang/String;)V",修飾符是public的靜態(tài)方法
-> (*env)->CallStaticVoidMethod(env, mainClass, mainID, mainArgs); // 通過JNI調用JavaMainClass.main()方法


以上步驟都還在java launcher的控制下;當控制權轉移到JavaMainClass.main()方法之后就沒java launcher什么事了,等JavaMainClass.main()方法返回之后java launcher才接手過來清理和關閉JVM。

下面看一下調用Java主類main()方法時會經過的主要方法及執(zhí)行的主要邏輯,如下:

// HotSpot VM里對JNI的CallStaticVoidMethod的實現(xiàn)。留意要傳給Java方法的參數(shù)
// 以C的可變長度參數(shù)傳入,這個函數(shù)將其收集打包為JNI_ArgumentPusherVaArg對象
-> jni_CallStaticVoidMethod()  
 
     // 這里進一步將要傳給Java的參數(shù)轉換為JavaCallArguments對象傳下去    
     -> jni_invoke_static()  
       
        // 真正底層實現(xiàn)的開始。這個方法只是層皮,把JavaCalls::call_helper()
        // 用os::os_exception_wrapper()包裝起來,目的是設置HotSpot VM的C++層面的異常處理
        -> JavaCalls::call()   
     
           -> JavaCalls::call_helper()
              -> //... 檢查目標方法是否為空方法,是的話直接返回
              -> //... 檢查目標方法是否“首次執(zhí)行前就必須被編譯”,是的話調用JIT編譯器去編譯目標方法
              -> //... 獲取目標方法的解釋模式入口from_interpreted_entry,下面將其稱為entry_point
              -> //... 確保Java棧溢出檢查機制正確啟動
              -> //... 創(chuàng)建一個JavaCallWrapper,用于管理JNIHandleBlock的分配與釋放,
                 // 以及在調用Java方法前后保存和恢復Java的frame pointer/stack pointer

              //... StubRoutines::call_stub()返回一個指向call stub的函數(shù)指針,
              // 緊接著調用這個call stub,傳入前面獲取的entry_point和要傳給Java方法的參數(shù)等信息
              -> StubRoutines::call_stub()(...) 
                 // call stub是在VM初始化時生成的。對應的代碼在
                 // StubGenerator::generate_call_stub()函數(shù)中
                 -> //... 把相關寄存器的狀態(tài)調整到解釋器所需的狀態(tài)
                 -> //... 把要傳給Java方法的參數(shù)從JavaCallArguments對象解包展開到解釋模
                    // 式calling convention所要求的位置
                 -> //... 跳轉到前面?zhèn)魅氲膃ntry_point,也就是目標方法的from_interpreted_entry

                    -> //... 在-Xcomp模式下,實際跳入的是i2c adapter stub,將解釋模式calling convention
                       // 傳入的參數(shù)挪到編譯模式calling convention所要求的位置
                           -> //... 跳轉到目標方法被JIT編譯后的代碼里,也就是跳到 nmethod 的 VEP 所指向的位置
                                -> //... 正式開始執(zhí)行目標方法被JIT編譯好的代碼 <- 這里就是"main()方法的真正入口"

后面3個步驟是在編譯執(zhí)行的模式下,不過后續(xù)我們從解釋執(zhí)行開始研究,所以需要為虛擬機配置-Xint選項,有了這個選項后,Java主類的main()方法就會解釋執(zhí)行了。

在調用Java主類main()方法的過程中,我們看到了虛擬機是通過JavaCalls::call()函數(shù)來間接調用main()方法的,下一篇我們研究一下具體的調用邏輯。

到此這篇關于關于Java虛擬機HotSpot的文章就介紹到這了,更多相關Java虛擬機HotSpot內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • Java中LinkedHashSet、LinkedHashMap源碼詳解

    Java中LinkedHashSet、LinkedHashMap源碼詳解

    這篇文章主要介紹了Java中LinkedHashSet、LinkedHashMap源碼詳解,LinkedHashMap是一個以雙向鏈表的方式將Entry節(jié)點鏈接起來的HashMap子類,它在HashMap的基礎上實現(xiàn)了更多的功能,具有順序存儲和遍歷的特性,需要的朋友可以參考下
    2023-09-09
  • IDEA 2020.1 for Mac 下載安裝配置及出現(xiàn)的問題小結

    IDEA 2020.1 for Mac 下載安裝配置及出現(xiàn)的問題小結

    這篇文章主要介紹了IDEA 2020.1 for Mac 下載安裝配置及出現(xiàn)的問題小結,本文通過圖文并茂的形式給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-03-03
  • java?緩沖流的概念使用方法以及實例詳解

    java?緩沖流的概念使用方法以及實例詳解

    這篇文章主要為大家介紹了java?緩沖流的概念使用方法以及實例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-06-06
  • RocketMQ實現(xiàn)消息分發(fā)的步驟

    RocketMQ實現(xiàn)消息分發(fā)的步驟

    RocketMQ 實現(xiàn)消息分發(fā)的核心機制是通過 Topic、Queue 和 Consumer Group 的配合實現(xiàn)的,下面給大家介紹RocketMQ實現(xiàn)消息分發(fā)的步驟,感興趣的朋友一起看看吧
    2024-03-03
  • @validated注解異常返回JSON值方式

    @validated注解異常返回JSON值方式

    這篇文章主要介紹了@validated注解異常返回JSON值方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-10-10
  • 2020Mac M1安裝jdk和IDEA的詳細方法

    2020Mac M1安裝jdk和IDEA的詳細方法

    這篇文章主要介紹了2020Mac M1安裝jdk和IDEA的詳細方法,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-03-03
  • 基于java下載中getContentLength()一直為-1的一些思路

    基于java下載中getContentLength()一直為-1的一些思路

    下面小編就為大家?guī)硪黄趈ava下載中getContentLength()一直為-1的一些思路。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-06-06
  • JAVA SpringBoot統(tǒng)一日志處理原理詳解

    JAVA SpringBoot統(tǒng)一日志處理原理詳解

    這篇文章主要介紹了SpringBoot的統(tǒng)一日志處理原理,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2021-09-09
  • java實現(xiàn)gif動畫效果(java顯示動態(tài)圖片)

    java實現(xiàn)gif動畫效果(java顯示動態(tài)圖片)

    這篇文章主要介紹了java實現(xiàn)gif動畫效果示例(java顯示動態(tài)圖片),需要的朋友可以參考下
    2014-04-04
  • 詳解SpringBoot靜態(tài)方法獲取bean的三種方式

    詳解SpringBoot靜態(tài)方法獲取bean的三種方式

    本文主要介紹了詳解SpringBoot靜態(tài)方法獲取bean的三種方式,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-10-10

最新評論