亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Java虛擬機(jī)調(diào)用Java主類的main()方法

 更新時(shí)間:2021年11月03日 15:13:14   作者:鳩摩  
這篇文章主要介紹了Java虛擬機(jī)調(diào)用Java主類的main()方法,前一篇文章我們介紹了關(guān)于Java虛擬機(jī)HotSpot

鳩摩

在前一篇 第1篇關(guān)于Java虛擬機(jī)HotSpot,開篇說的簡(jiǎn)單些 中介紹了call_static() 、call_virtual()等函數(shù)的作用,這些函數(shù)會(huì)調(diào)用JavaCalls::call()函數(shù)。我們看Java類中main()方法的調(diào)用,

調(diào)用棧如下:

JavaCalls::call_helper() at javaCalls.cpp 
os::os_exception_wrapper() at os_linux.cpp 
JavaCalls::call() at javaCalls.cpp
jni_invoke_static() at jni.cpp 
jni_CallStaticVoidMethod() at jni.cpp 
JavaMain() at java.c
start_thread() at pthread_create.c
clone() at clone.S

這是Linux上的調(diào)用棧,通過JavaCalls::call_helper()函數(shù)來執(zhí)行main()方法。棧的起始函數(shù)為clone() ,這個(gè)函數(shù)會(huì)為每個(gè)進(jìn)程(Linux進(jìn)程對(duì)應(yīng)著Java線程)創(chuàng)建單獨(dú)的??臻g,這個(gè)??臻g如下圖所示。

在Linux操作系統(tǒng)上,棧的地址向低地址延伸,所以未使用的??臻g在已使用的棧空間之下。圖中的每個(gè)藍(lán)色小格表示對(duì)應(yīng)方法的棧幀,而棧就是由一個(gè)一個(gè)的棧幀組成。native方法的棧幀、Java解釋棧幀和Java編譯棧幀都會(huì)在***域中分配,所以說他們寄生在宿主棧中,這些不同的棧幀都緊密的挨在一起,所以并不會(huì)產(chǎn)生什么空間碎片這類的問題,而且這樣的布局非常有利于進(jìn)行棧的遍歷。上面給出的調(diào)用棧就是通過遍歷一個(gè)一個(gè)棧幀得到的,遍歷過程也是棧展開的過程。后續(xù)對(duì)于異常的處理、運(yùn)行jstack打印線程堆棧、GC查找根引用等都會(huì)對(duì)棧進(jìn)行展開操作,所以棧展開是后面必須要介紹的。

下面我們繼續(xù)看JavaCalls::call_helper()函數(shù),這個(gè)函數(shù)中有個(gè)非常重要的調(diào)用,如下:

// do call
{
    JavaCallWrapper link(method, receiver, result, CHECK);
    {
      HandleMark hm(thread);  // HandleMark used by HandleMarkCleaner
      StubRoutines::call_stub()(
         (address)&link,
         result_val_address,              
         result_type,
         method(),
         entry_point,
         args->parameters(),
         args->size_of_parameters(),
         CHECK
      );
 
      result = link.result();  
      // Preserve oop return value across possible gc points
      if (oop_result_flag) {
        thread->set_vm_result((oop) result->get_jobject());
      }
    }
}

調(diào)用StubRoutines::call_stub()函數(shù)返回一個(gè)函數(shù)指針,然后通過函數(shù)指針來調(diào)用函數(shù)指針指向的函數(shù)。通過函數(shù)指針調(diào)用和通過函數(shù)名調(diào)用的方式一樣,這里我們需要清楚的是,調(diào)用的目標(biāo)函數(shù)仍然是C/C++函數(shù),所以由C/C++函數(shù)調(diào)用另外一個(gè)C/C++函數(shù)時(shí),要遵守調(diào)用約定。這個(gè)調(diào)用約定會(huì)規(guī)定怎么給被調(diào)用函數(shù)(Callee)傳遞參數(shù),以及被調(diào)用函數(shù)的返回值將存儲(chǔ)在什么地方。

下面我們就來簡(jiǎn)單說說Linux X86架構(gòu)下的C/C++函數(shù)調(diào)用約定,在這個(gè)約定下,以下寄存器用于傳遞參數(shù):

  • 第1個(gè)參數(shù):rdi c_rarg0
  • 第2個(gè)參數(shù):rsi c_rarg1
  • 第3個(gè)參數(shù):rdx c_rarg2
  • 第4個(gè)參數(shù):rcx c_rarg3
  • 第5個(gè)參數(shù):r8 c_rarg4
  • 第6個(gè)參數(shù):r9 c_rarg5

在函數(shù)調(diào)用時(shí),6個(gè)及小于6個(gè)用如下寄存器來傳遞,在HotSpot中通過更易理解的別名c_rarg*來使用對(duì)應(yīng)的寄存器。如果參數(shù)超過六個(gè),那么程序?qū)?huì)用調(diào)用棧來傳遞那些額外的參數(shù)。

數(shù)一下我們通過函數(shù)指針調(diào)用時(shí)傳遞了幾個(gè)參數(shù)?8個(gè),那么后面的2個(gè)就需要通過調(diào)用函數(shù)(Caller)的棧來傳遞,這兩個(gè)參數(shù)就是args->size_of_parameters()CHECK(這是個(gè)宏,擴(kuò)展后就是傳遞線程對(duì)象)。

所以我們的調(diào)用棧在調(diào)用函數(shù)指針指向的函數(shù)時(shí),變?yōu)榱巳缦聽顟B(tài):

右邊是具體的call_helper()棧幀中的內(nèi)容,我們把threadparameter size壓入了調(diào)用棧中,其實(shí)在調(diào)目標(biāo)函數(shù)的過程還會(huì)開辟新的棧幀并在parameter size后壓入返回地址和調(diào)用棧的棧底,下一篇我們?cè)僭敿?xì)介紹。先來介紹下JavaCalls::call_helper()函數(shù)的實(shí)現(xiàn),我們分3部分依次介紹。

1、檢查目標(biāo)方法是否"首次執(zhí)行前就必須被編譯”,是的話調(diào)用JIT編譯器去編譯目標(biāo)方法;

代碼實(shí)現(xiàn)如下:

void JavaCalls::call_helper(
 JavaValue* result, 
 methodHandle* m, 
 JavaCallArguments* args, 
 TRAPS
) {
  methodHandle method = *m;
  JavaThread* thread = (JavaThread*)THREAD;
  ...
 
  assert(!thread->is_Compiler_thread(), "cannot compile from the compiler");
  if (CompilationPolicy::must_be_compiled(method)) {
    CompileBroker::compile_method(method, InvocationEntryBci,
                                  CompilationPolicy::policy()->initial_compile_level(),
                                  methodHandle(), 0, "must_be_compiled", CHECK);
  }
  ...
}

對(duì)于main()方法來說,如果配置了-Xint選項(xiàng),則是以解釋模式執(zhí)行的,所以并不會(huì)走上面的compile_method()函數(shù)的邏輯。后續(xù)我們要研究編譯執(zhí)行時(shí),可以強(qiáng)制要求進(jìn)行編譯執(zhí)行,然后查看執(zhí)行過程。

2、獲取目標(biāo)方法的解釋模式入口from_interpreted_entry,也就是entry_point的值。獲取的entry_point就是為Java方法調(diào)用準(zhǔn)備棧楨,并把代碼調(diào)用指針指向method的第一個(gè)字節(jié)碼的內(nèi)存地址。entry_point相當(dāng)于是method的封裝,不同的method類型有不同的entry_point。

接著看call_helper()函數(shù)的代碼實(shí)現(xiàn),如下:

address entry_point = method->from_interpreted_entry();

調(diào)用methodfrom_interpreted_entry()函數(shù)獲取Method實(shí)例中_from_interpreted_entry屬性的值,這個(gè)值到底在哪里設(shè)置的呢?我們后面會(huì)詳細(xì)介紹。

3、調(diào)用call_stub()函數(shù),需要傳遞8個(gè)參數(shù)。這個(gè)代碼在前面給出過,這里不再給出。下面我們?cè)敿?xì)介紹一下這幾個(gè)參數(shù),如下:

  • (1)link 此變量的類型為JavaCallWrapper,這個(gè)變量對(duì)于棧展開過程非常重要,后面會(huì)詳細(xì)介紹;
  • (2)result_val_address 函數(shù)返回值地址;
  • (3)result_type 函數(shù)返回類型;
  • (4)method() 當(dāng)前要執(zhí)行的方法。通過此參數(shù)可以獲取到Java方法所有的元數(shù)據(jù)信息,包括最重要的字節(jié)碼信息,這樣就可以根據(jù)字節(jié)碼信息解釋執(zhí)行這個(gè)方法了;
  • (5)entry_point HotSpot每次在調(diào)用Java函數(shù)時(shí),必然會(huì)調(diào)用CallStub函數(shù)指針,這個(gè)函數(shù)指針的值取自_call_stub_entry,HotSpot通過_call_stub_entry指向被調(diào)用函數(shù)地址。在調(diào)用函數(shù)之前,必須要先經(jīng)過entry_point,HotSpot實(shí)際是通過entry_point從method()對(duì)象上拿到Java方法對(duì)應(yīng)的第1個(gè)字節(jié)碼命令,這也是整個(gè)函數(shù)的調(diào)用入口;
  • (6)args->parameters() 描述Java函數(shù)的入?yún)⑿畔ⅲ?/li>
  • (7)args->size_of_parameters() 參數(shù)需要占用的,以字為單位的內(nèi)存大小
  • (8)CHECK 當(dāng)前線程對(duì)象。

到此這篇關(guān)于Java虛擬機(jī)調(diào)用Java主類的main()方法的文章就介紹到這了,更多相關(guān)Java虛擬機(jī)調(diào)用main()方法內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java源碼解析之HashMap的put、resize方法詳解

    Java源碼解析之HashMap的put、resize方法詳解

    這篇文章主要介紹了Java源碼解析之HashMap的put、resize方法詳解,文中有非常詳細(xì)的代碼示例,對(duì)正在學(xué)習(xí)java的小伙伴們有很大的幫助,需要的朋友可以參考下
    2021-04-04
  • Java中Json字符串直接轉(zhuǎn)換為對(duì)象的方法(包括多層List集合)

    Java中Json字符串直接轉(zhuǎn)換為對(duì)象的方法(包括多層List集合)

    下面小編就為大家?guī)硪黄狫ava中Json字符串直接轉(zhuǎn)換為對(duì)象的方法(包括多層List集合)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2016-08-08
  • 詳解springmvc攔截器攔截靜態(tài)資源

    詳解springmvc攔截器攔截靜態(tài)資源

    本篇文章主要介紹了詳解springmvc攔截器攔截靜態(tài)資源,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。
    2017-01-01
  • 淺談SpringBoot集成Redis實(shí)現(xiàn)緩存處理(Spring AOP實(shí)現(xiàn))

    淺談SpringBoot集成Redis實(shí)現(xiàn)緩存處理(Spring AOP實(shí)現(xiàn))

    這篇文章主要介紹了淺談SpringBoot集成Redis實(shí)現(xiàn)緩存處理(Spring AOP實(shí)現(xiàn)),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-12-12
  • Maven中dependency和plugins的繼承與約束

    Maven中dependency和plugins的繼承與約束

    這篇文章主要介紹了Maven中dependency和plugins的繼承與約束,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-12-12
  • Java?SpringBoot操作Redis

    Java?SpringBoot操作Redis

    這篇文章主要介紹了SpringBoot如何操作Redis,文章中有詳細(xì)的代碼示例,有一定的參考價(jià)值,感興趣的同學(xué)可以參考閱讀
    2023-04-04
  • Spring boot怎么整合Mybatis

    Spring boot怎么整合Mybatis

    spring boot的簡(jiǎn)配置方便的開發(fā),下面通過本文給大家分享Spring boot整合Mybatis的方法,需要的朋友參考下
    2017-07-07
  • SpringBoot如何讀取配置文件中的數(shù)據(jù)到map和list

    SpringBoot如何讀取配置文件中的數(shù)據(jù)到map和list

    這篇文章主要介紹了SpringBoot如何讀取配置文件中的數(shù)據(jù)到map和list,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-02-02
  • Java?HashSet的Removals()方法注意事項(xiàng)

    Java?HashSet的Removals()方法注意事項(xiàng)

    這篇文章主要介紹了Java?HashSet的Removals()方法注意事項(xiàng),文章圍繞制主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下
    2022-06-06
  • SpringBoot整合LDAP的流程分析

    SpringBoot整合LDAP的流程分析

    這篇文章主要介紹了SpringBoot整合LDAP的流程分析,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-05-05

最新評(píng)論