日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

沒想到進(jìn)入main函數(shù)前,發(fā)生了這么多事!

 長(zhǎng)沙7喜 2021-03-18

題外話

最近這段時(shí)間,軒轅有些迷茫了,工作生活中一堆事兒,忙得我兩頭摸黑,很難找到時(shí)間靜下心來寫文章,就連你現(xiàn)在看到的這一篇還是我點(diǎn)燈熬油到1點(diǎn)鐘才寫完的。

估計(jì)朋友們也有發(fā)現(xiàn)了,這段時(shí)間故事文章少了很多,確實(shí)是這樣,不像水文幾下完事兒,也不像普通技術(shù)文章按照標(biāo)準(zhǔn)流程走就行。故事文需要放空自己,有好的靈感才能一氣呵成。

這段時(shí)間有好幾個(gè)選題,都開了個(gè)頭,占了個(gè)坑,然后因?yàn)闆]時(shí)間思考,所以一直留著···

圖片

所以今天,先寫了一篇?jiǎng)e的頂一頂,故事文章大家再給我點(diǎn)時(shí)間吧,感謝各位老鐵的支持!圖片

圖片

在之前的一篇文章中,聊過從創(chuàng)建進(jìn)程到進(jìn)入main函數(shù),發(fā)生了什么?

但當(dāng)時(shí)只是針對(duì)C/C++這樣的native語言,從操作系統(tǒng)(Linux & Windows)的層面去探討了程序的啟動(dòng)過程,而對(duì)Java、Python這樣的基于虛擬機(jī)/解釋器的語言并未提及。

今天,咱們就一起來探索下在Java語言中,你寫的main方法又是怎么被執(zhí)行到的?

圖片

對(duì)于Java而言,其底層是Java虛擬機(jī)在跑著,也就是JVM,這篇文章如無特殊說明,默認(rèn)以Hotspot為研究對(duì)象。

先來回顧一下那篇文章,對(duì)于C/C++程序而言,從創(chuàng)建進(jìn)程到進(jìn)入main函數(shù),主要就是經(jīng)歷了四個(gè)階段:

  • 進(jìn)程 & 主線程創(chuàng)建階段
  • 主線程啟動(dòng)執(zhí)行并進(jìn)行進(jìn)程級(jí)初始化操作(如加載系統(tǒng)動(dòng)態(tài)鏈接庫)
  • 主線程進(jìn)入可執(zhí)行文件的入口(OEP)并進(jìn)行C/C++運(yùn)行時(shí)庫初始化
  • 從C/C++運(yùn)行時(shí)庫調(diào)入main函數(shù)

你知道的,Java的虛擬機(jī)JVM主要是C++編寫的,所以JVM本質(zhì)上也算是一個(gè)C++程序。

因此,上面的四個(gè)階段,對(duì)于JVM而言,同樣適用。

只不過呢,對(duì)于C/C++程序而言,到這里就已經(jīng)進(jìn)入main函數(shù)了,話題就可以結(jié)束了,而對(duì)于Java程序,執(zhí)行到JVM的main,一切才剛剛開始。

JVM的main

故事,要從JVM的main函數(shù)開始講起···

你應(yīng)該知道的,不管你是普通Java程序,還是用的Spring或者其他什么框架,最終的程序都是在一個(gè)Java進(jìn)程中運(yùn)行的,這個(gè)進(jìn)程的可執(zhí)行文件就是一個(gè)exe(windows上)或者elf(linux上)。

咱們就從這個(gè)可執(zhí)行文件入手,以Linux系統(tǒng)上的Java8版本為例,用反匯編神器IDA打開可以看到,這個(gè)可執(zhí)行文件的入口:

圖片

和咱們?cè)谏弦黄治龅牧鞒谭?,進(jìn)入這個(gè)程序啟動(dòng)入口后,會(huì)經(jīng)過一系列的調(diào)用,最后來到main函數(shù):

圖片

反匯編看著好頭大,好在,HotSpot虛擬機(jī)有開源版本,咱們可以去OpenJDK中找來這個(gè)main函數(shù)的源碼瞧瞧。

不同版本差異還是挺大,這里以Java8為例:

代碼路徑:https://github.com/openjdk/jdk/blob/jdk8-b20/jdk/src/share/bin/main.c

圖片

在這個(gè)代碼中除了main函數(shù),還可以看到如果定義了JAVAW宏定義,則入口從main變成了WinMain函數(shù),做過Windows應(yīng)用程序開發(fā)的朋友這個(gè)時(shí)候應(yīng)該露出了滿意的微笑。

如果定義了JAVAW,則是一個(gè)Win32 GUI的程序,當(dāng)然在Linux上是肯定沒有這個(gè)宏定義的,不過這不是本文的主題。

可以看到main函數(shù)只是一個(gè)包裝,直接就進(jìn)入了JLI_Launch中。

這個(gè)函數(shù)位于同目錄下的隔壁java.c文件中,是JVM非常重要的初始化函數(shù),主要完成了下面幾件事情:

  • 參數(shù)解析,環(huán)境配置
  • 檢查Java運(yùn)行環(huán)境
  • 加載JVM核心動(dòng)態(tài)庫libjvm.so
  • 創(chuàng)建并初始化Java虛擬機(jī)對(duì)象

這些過程都不是我們這篇文章探究的目標(biāo),咱們繼續(xù)把目光聚焦在Java中的main函數(shù)是怎么得到調(diào)用的。

在JLI_Launch的結(jié)尾,調(diào)用了ContinueInNewThread,從這個(gè)函數(shù)的名字我們也能窺探它的作用。

圖片

這個(gè)函數(shù)還是一層封裝,內(nèi)部調(diào)用了真正干活的函數(shù)ContinueInNewThread0

圖片

接下來就是創(chuàng)建線程來繼續(xù)后面的事情了,不過創(chuàng)建線程涉及到操作系統(tǒng)API的調(diào)用,所以這個(gè)函數(shù)在不同版本的系統(tǒng)中都有對(duì)應(yīng)的實(shí)現(xiàn)。來看傳給它的第一個(gè)參數(shù),這是新線程啟動(dòng)后將要執(zhí)行的入口函數(shù):JavaMain。

JavaMain

這個(gè)函數(shù)的名字就有點(diǎn)意思了,看起來,快要進(jìn)入Java的地界兒了,加油繼續(xù)看下去:

int JNICALL  JavaMain(void * _args) {
 
    // ...
    // 尋找啟動(dòng)類
    mainClass = LoadMainClass(env, mode, what);
    // ...
    // 尋找啟動(dòng)類中的main函數(shù)
    mainID = (*env)->GetStaticMethodID(env, mainClass, 'main',
                                       '([Ljava/lang/String;)V');
    // ...
    // 調(diào)用它
    (*env)->CallStaticVoidMethod(env, mainClass, mainID, mainArgs);
    // ...
}

JavaMain中的細(xì)節(jié)挺多的,咱們抽出需要關(guān)心的,要調(diào)用咱們寫的main方法就像把大象關(guān)進(jìn)冰箱一共三步:

  • 找到啟動(dòng)類
  • 找到啟動(dòng)類中的main方法
  • 調(diào)用它

具體尋找的過程這里就不展開了,有些繁瑣,但你應(yīng)該能猜到,Java代碼編譯后都是以class文件的形式存儲(chǔ)的,所以這個(gè)尋找的背后少不了要涉及到class類加載等一系列的工作。

總之,一頓操作猛如虎,嘿,JVM把咱們寫的main方法找到了!接下來就是調(diào)用它了。

進(jìn)入Java世界

調(diào)用main方法的是CallStaticVoidMethod,從名字可以看到,這是在調(diào)用一個(gè)靜態(tài)的、返回值為空的方法。注意了,C++的地盤快到邊境了,咱們即將通過它來到美麗的Java新世界!

這個(gè)函數(shù)內(nèi)部后面會(huì)來到:

JavaCalls::call(result, method, &java_args, CHECK);

最終,會(huì)創(chuàng)建Java方法棧幀,準(zhǔn)備好模板解釋器,隨后轉(zhuǎn)向解釋器入口開始執(zhí)行字節(jié)碼,正式進(jìn)入Java世界!

進(jìn)入Java世界第一站,就是前面找到的啟動(dòng)類的main方法,在這里開啟程序在Java世界的征程。

總結(jié)

現(xiàn)在可以來回答這個(gè)問題了:從創(chuàng)建進(jìn)程到Java的main方法,經(jīng)歷了什么?

咱們來劃分三個(gè)大的階段:

第一階段:操作系統(tǒng)層面進(jìn)程和主線程的創(chuàng)建

第二階段:主線程啟動(dòng)執(zhí)行并進(jìn)入到Java可執(zhí)行文件(exe/elf)中的main函數(shù)(C++層面)

第三階段:創(chuàng)建JVM,尋找啟動(dòng)類中的main方法,啟動(dòng)解釋器執(zhí)行對(duì)應(yīng)字節(jié)碼進(jìn)入Java世界

前兩個(gè)階段是上一篇文章《從創(chuàng)建進(jìn)程到進(jìn)入main函數(shù),發(fā)生了什么?》討論的內(nèi)容,第三階段則是本文的內(nèi)容。

現(xiàn)在你明白你寫的main方法是怎么被調(diào)用的了嗎?

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多