摘要:今日最佳對于程序員而言,所謂的二八定律指的是花百分之八十的時間去學習日常研發中不常見的那百分之二十的原理。
【今日最佳】對于程序員而言,所謂的二八定律指的是 花百分之八十的時間去學習日常研發中不常見的那百分之二十的原理。
據說阿里某程序員對書法十分感興趣,退休后決定在這方面有所建樹。于是花重金購買了上等的文房四寶。
一日,飯后突生雅興,一番磨墨擬紙,并點上了上好的檀香,頗有王羲之風范,又具顏真卿氣勢,定神片刻,潑墨揮毫,鄭重地寫下一行字:hello world。
當然了,這是個專屬程序員的段子哈哈哈。
那么問題來了,寫了這么久的Hello World,大家確定自己了解自己寫的東西背后是什么原理嗎?(o???)
【給出2分鐘,該知識點涉及到了Java程序執行流程,包括編譯、加載和執行,你是否能夠理清呢?】
接下來進入嚴肅時間 (@ ̄ー ̄@)
與眾不同的Hello Worldpublic class Main { private static String word = "Hello World!"; public static void main(String[] args) { new Main().say(); } private void say() { System.out.println(word); } }
整個代碼的執行過程可以分為三個階段:
代碼編譯
類加載
類執行
代碼編譯代碼編譯的作用就是將我們編寫的 Main.java文件轉化為Main.class文件,.class在這里又被稱為字節碼文件,打開就是一堆的火星文【反正就是看不懂】,在這里我們可以將編譯的過程看作生產JVM原料的過程,使用的工具就是jdk提供的工具javac。
大致流程如下:
詞法分析,即將源代碼的字符流轉變為Token集的過程。白話文描述下,就是在我們實際編程中,單個字符是最小單位,而實際上在編程過程中,標記才是最小單位,如關鍵字、變量名、字面量、運算符等都可以成為Token,貌似還是有點蒙蔽,舉個例子(>﹏<),比如整型int在我們編程中它就是三個字符組成的,就是i、n、t 三個字符,而在編譯過程中它就是一個Token,不可拆分。
這個過程對我們來說其實是完全屏蔽的,但是實際上它是現代經典編譯原理的
套路,詞法分析也是為了給后面編譯做準備的】
語法分析,通過詞法分析拿到Token集后,下一步就是構建抽象語法樹了,所謂的抽象語法樹其實就是一種用來描述程序代碼語法結構的樹形表示方式,其中語法樹的每一個節點都代表著程序代碼中的一個語法結構,如包、類型、修飾符、運算符等。
在我們眼中,Main.java已經可以清晰理解到底寫的是什么東西了,但是對于JVM來說還是一臉懵逼的,所以才需要構建成語法樹,在這一步后就不會再對源碼文件進行操作了,后續的操作都建立在抽象語法樹上
填充符號表,符號表是由一組符號地址和符號信息構成的表格,這個表格在編譯的不同階段都會被用到,如在目標代碼生成階段,會對符號名進行地址分配,而符號表就是地址分配的依據。
語義分析,語義分析階段也可以說是語義檢測階段,上面說到語法分析會構建一棵語法樹,那么這棵語法樹是否是正確合理的,就由語義分析來做了,語義分析會通過標注檢查和數據及控制流分析檢查兩步入手,在生成字節碼的最后一步信息把關。
字節碼生成,這是javac編譯過程的最后一個階段了,字節碼生成階段并不只是簡簡單單的將前面各個步驟生成的信息轉化成字節碼然后放入磁盤中,還進行了少量的代碼添加和轉換工作,如我們自己重載的構造函數。
編譯期到這里就結束了,那么由誰來將這些原料傳輸給JVM虛擬機呢?這個時候就要看看類加載的過程了。
類加載類加載簡單來說就是將由類加載器將編譯后的字節碼文件【Main.class】加載到虛擬機中
,那么自然而然的,要先介紹下四種類加載器
說說四種類加載器
可以從上圖中看出,類加載器可以分為四種,而第四種是由我們自己實現的,那么其他三種由JVM提供的類加載在我們啟動該Main程序的過程中起到了什么作用呢?
首先說說啟動類加載器 Bootstrap ClassLoader ,啟動類加載器的作用主要是加載 %JAVA_HOME%jrelibrt.jar 類庫,將其加載到虛擬機內存中,那么rt.jar類庫到底有什么作用呢?rt.jar下包含了Java的基礎類庫,也就是Java doc里面看到的所有的類的class文件,感興趣的朋友可以自己打開目錄看下。
其次是擴展類加載器 Extension ClassLoader ,擴展類加載器的作用主要是負責加載JAVA_HOMEjrelibext目錄下的所有類庫,主要是載入擴展包。
再者是系統類加載器 Application ClassLoader, 也稱之為應用程序類加載器,負責加載用戶類路徑(也就是我們配置的CLASSPATH)上所指定的類庫,是應用程序中默認的類加載。
看完以上三個類加載器的簡單描述過程,是不是有種終于知道了我們配置的jdk環境的最終作用了吧,是的,就是讓類加載器識別到后加載各種類庫。
那么問題來了?是哪個類加載器加載了我們的Hello World程序呢?是的,就是應用程序中默認的類加載器 Application ClassLoader。
知道了類加載器后,那接下來總要了解下類加載器怎么加載的吧?
說說類加載的過程
網上找了張圖片,簡單明了。雖說是簡單明了,不過確實異常重要的,因為是面試熱點(????)
加載其實就是上文說到的系統類加載器 Application ClassLoader將編譯后的Main.class文件加載到內存中。
【思考】拋出個問題,所謂的加載到內存中,我們都知道JVM把內存分成了幾大模塊,那么請問是加載到哪個模塊中?熱點面試題,答案見文末!
鏈接鏈接中包含了三部曲,總的作用就是負責將Main.class的二進制數據合并到JRE中。
關于三部曲,其實很好理解;
首先是驗證階段,類加載器將二進制字節流加載到虛擬機中,肯定是需要進行驗證的,避免危害虛擬機自身安全,而這也是驗證階段存在的價值;
接下來是準備階段,準備階段是正式為類變量分配內存并且設置類變量默認值的地方,比如上面HelloWorld程序中的
private static String word = "Hello World!";
注意我描述的第一個是類變量,也就是static所描述的變量,其次是默認值,也就是上面的word的默認值null,如果是數字則為0。
最后是解析階段,解析階段的作用主要是將常量池內的符號引用替換為直接引用的過程,解析階段其實有點難理解,至少是比上面的兩個階段要難理解的,我這里盡量直白點;
所謂的符號引用指的是包含了類的信息、方法名、方法參數等信息的字符串,而當第一次運行時,JVM會根據這行字符串去檢索到對應的方法入口,而為了下次不用再做同樣的檢索,在第一次運行的時候就會將符號引用替換成直接引用,這樣后面就可以省去一定的消耗了;這里的直接引用其實就是指偏移量,虛擬機可以通過偏移量直接找到方法入口,不再需要做檢索了。
初始化
終于來到初始化階段了,上面我們有說到word默認值是null,是系統賦的默認值,而在初始化階段,則是根據我們人為的初始化類變量和其他資源,比如上面的word則被我初始化成了"Hello World!"。
上面說到Main.class被加載到了Java虛擬機內存中,那么接下來便是執行的過程了。那么由誰來執行這一過程呢?
如圖
實際上,一個Java虛擬機在運行的時候可以劃分為三個子系統:
類加載子系統
執行引擎子系統
垃圾收集子系統
很明顯、很清晰,圖中的類加載子系統在上面已經談了,執行引擎子系統就是負責執行這一部分的,那么過程是怎么樣的呢?
其實很簡單,執行引擎會把字節碼轉換為機器碼【what?竟然還要轉換。拜托<(ˉ^ˉ)>,字節碼是被JVM識別的語言,字節碼才是最終被操作系統識別的語言】
然后操作系統才可以真正調用,很多學或者做Java的人都聽過JIT,但是都不知道具體是干嘛的,沒錯說的就是你。
這里終于可以解釋下了,字節碼轉換成機器碼的翻譯工作使用的就是JIT(Just In Time)即時編譯器(對熱代碼整段編譯)和Java字節碼解釋器(一行一行解釋字節碼)來完成的。
這里給下JIT編譯的工作流程:
JVM字節碼 -> 機器無關優化 -> 中間代碼 -> 機器相關優化 -> 中間代碼 -> 寄存器分配器 -> 中間代碼 -> 目標機器碼生成器 -> 目標機器碼
最后執行引擎會找到main()這個入口方法,并且執行其中的字節碼指令。
最后,關于HelloWorld執行過程,基本上闡述完畢了,關于執行程序期間,JVM內存分配問題,是一個比較大的模塊,欲知詳情,請關注公眾號,我們下次再聊!!!
【思考解惑】加載階段完成后,虛擬機會將Main.class的二進制字節流按照虛擬機所需的格式存儲在方法區之中,然后在內存中實例化一個java.lang.Class類的對象,作為程序訪問方法區中的這些類型數據的外部接口,實例化后的java.lang.Class類的對象也是存放在方法區中的。
公眾號主營:服務端編程相關技術解說,具體可以看歷史文章。
公眾號副業:各種陪聊吹水,包括技術、就業、人生經歷、大學生活、內推等等。
歡迎關注,一起侃大山
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/75097.html
摘要:總結動態代理的相關原理已經講解完畢,接下來讓我們回答以下幾個思考題。 【干貨點】 此處是【好好面試】系列文的第12篇文章。文章目標主要是通過原理剖析的方式解答Aop動態代理的面試熱點問題,通過一步步提出問題和了解原理的方式,我們可以記得更深更牢,進而解決被面試官卡住喉嚨的情況。問題如下 SpringBoot默認代理類型是什么 為什么不用靜態代理 JDK動態代理原理 CGLIB動態代理...
前言 未來的公司形態會不斷地演化,去中心化,分布式,強化合作,適應變化,直到徹底地被網絡化。終極公司的形式將會變得與生物體相同,無縫地集成到生態圈中,成為其中的一個環節。—— 凱文·凱利《失控》 小劇場 小二: 糖糖,我愛你哦~ 糖糖: 你騙人!男人的話能信母豬能上樹。 小二: 我可以向全世界證明,我說的是真的~ 糖糖: 那你怎么證明啊~ 小二: 我可以用 區塊鏈 寫下 糖糖我愛你哦~...
摘要:給百度百科給的環比定義為環比,統計學術語,是表示連續個統計周期比如連續兩月內的量的變化比。二你所不知道的同比環比兩種方式的核心區別判斷兩個數據到底是同比還是環比。 ...
你所不知道的 URL 0.說明 第一幕 產品:大叔有用戶反映賬戶不能綁定公眾號。大叔:啊咧咧?怎么可能,我看看?大叔:恩?這也沒問題啊,魏蝦米。大叔:還是沒問題啊,挖叉類。大叔:T T,話說產品姐姐是不是Java提供接口的時候,沒有對URL進行encodeURI。產品:啊咧咧?我問問看? 第二幕 大叔:小二你給我過來!小二:啊咧咧?怎么了大叔?大叔:知道在URL中的+有時候會變成什么嗎?小二:啊咧...
摘要:請注意是創建一個全局對象的屬性,而不是聲明了一個全局變量。由于變量聲明自帶不可刪除屬性,比較跟,前者是變量聲明,帶不可刪除屬性,因此無法被刪除后者為全局變量的一個屬性,因此可以從全局變量中刪除。下期預告前端面試你所不知道系列偽類和偽元素 寫在開始 又到了一年的伊始,很多人可能因為各種原因想換一份工作,而找工作難免遇到各種各樣頭痛的面試題,于是我打算寫一個系列,關于面試中最常見或者前端一...
閱讀 818·2021-10-25 09:48
閱讀 610·2021-08-23 09:45
閱讀 2496·2019-08-30 15:53
閱讀 1758·2019-08-30 12:45
閱讀 585·2019-08-29 17:21
閱讀 3406·2019-08-27 10:56
閱讀 2546·2019-08-26 13:48
閱讀 690·2019-08-26 12:24