如果你對JVM一知半解,如果你想了解JVM的工作流程,如果你知道一些JVM面試題卻無法將知識點串聯起來,那么這篇文章非常適合你。
這些面試題Javaer們應該都很熟悉,但是你知道這些面試題的背后嗎?
圖片
如果你總是背了又忘,忘了又背,歸根結底,還是對JVM沒有一個系統的認識。
那么希望通過這篇文章,可以為你構建一個連貫的JVM框架。
眾所周知,高級編程語言編寫的程序,最終要轉化為機器碼,才可以在計算機上運行。
圖片
我們在編寫完一段Java代碼后,如果想要運行它,需要通過Java編譯器,將其編譯為JVM認識的字節碼文件。
圖片
然后執行Java命令,這段代碼就會通過JVM運行。
圖片
在這個過程中,JVM就充當了轉換的角色,負責將字節碼,翻譯成對應平臺上的機器指令。這樣的話,Java程序就可以在任何安裝了JVM的平臺上運行。這就是Java語言一次編寫到處運行的跨平臺特性。
圖片
翻譯字節碼的工作,是由JVM的執行引擎完成。
在將字節碼翻譯為機器指令之前,JVM還有一個非常重要的工作,那就是將字節碼文件中的二進制數據準確的加載到JVM中。這個工作是由JVM的類加載系統完成,
另外,為了在運行時方便管理內存,JVM定義了一個專門的區域,也就是大名鼎鼎的運行時數據區。
圖片
所以,類加載系統、運行時數據區、執行引擎,就構成了JVM平臺。
接下來,看一下它們是如何工作的。
在這之前,要對字節碼現有一個認識,畢竟它貫穿了Java代碼運行的整個流程。
Java虛擬機對Java編程語言一無所知,只知道一種特定的二進制格式,即類文件格式。類文件包含Java虛擬機指令(或字節碼)和符號表,以及其他輔助信息。
類加載系統目的很明確,就是將字節碼文件中的二進制數據準確地加載到JVM,從Class文件加載到內存 & 對數據進行校驗、轉換解析和初始化,最終形成可被虛擬機直接使用的Java使用類型
執行Java命令后,Java虛擬機啟動,類加載系統就開始工作了。
圖片
類加載系統首先會讀取指定的類文件,并遵循雙親委派機制進行加載。
圖片
然后將文件中的常量池、字段、方法和指令等數據加載到JVM內存的共享區域方法區中。
圖片
然后對其進行驗證,目的是為了確保類的正確性。比如版本號為52或更高時,不應該存在這個版本不支持的指令。
圖片
或者標識類文件的魔術數字是不是cafebabe,這些完整性的檢查和約束都是非常有必要,就像我們自己開發的應用,也不可能隨便讓別人訪問一樣。
圖片
驗證完成后,在方法區為類的靜態變量分配內存并設置默認值。
圖片
緊接著,將常量池中表示對象的符號引用,指向到實際的內存地址,也就是直接引用。
圖片
什么是符號引用呢?
符號引用是常量池中的類、方法、字段等指向的目標在字節碼文件中的靜態表示,當JVM運行時,需要將目標的靜態表示轉換成實際的內存指針,也就是直接引用。在這個例子中,如果JVM需要加載Object這個類,它會查找常量池中的#3(Class類型,指向#27),然后解析#27中的字符串java/lang/Object/為實際的類文件路徑,并加載這個類。
最后執行靜態代碼塊,為靜態變量設置初始值,類加載工作就算完成了。
整個加載過程就是面試被經常問到的類加載機制。
圖片
那么問題來了:靜態變量為什么要先設置默認值,再設置初始值,知道的評論區留言。
靜態代碼塊被執行時,執行引擎就會處理這些指令。執行引擎有兩種工作模式:
解釋執行就是每次執行都會逐行解釋字節碼指令
圖片
即時編譯是將熱點代碼,編譯成當前平臺的機器碼,并緩存下次就可以直接執行機器碼,這樣就可以提高執行效率。
圖片
JVM通常采用解釋器與即時編譯器并存的混合模式。在程序啟動時,解釋器可以立即發揮作用,省去編譯時間;隨著程序運行時間的推移,JIT編譯器逐漸發揮作用,將越來越多的熱點代碼編譯為本地機器碼,以提高執行效率。
靜態代碼塊執行完成后,JVM會繼續調用main方法。如果執行Java命令的字節碼文件中沒有main方法,JVM就會報錯,這個是JVM規范。
圖片
執行引擎工作期間,會和運行時數據區域有大量的交互。
調用main方法時,會創建一個線程并在運行時數據區中分配線程私有的空間:棧幀以及程序計數器。
圖片
程序計數器初始時會指向第一條指令, 然后隨著指令的執行而遞增。
圖片
執行靜態變量賦值的指令時,會把整數推送到棧幀中的操作數棧,隨后賦值給靜態變量。
圖片
在執行創建一個Object實例的指令時,如果Object Class未被加載,類加載器會啟動加載過程。然后在堆中分配一塊內存并初始化實例。
圖片
分配內存這個過程,就涉及到“堆內存分代設計”、“對象內存分配過程”、“內存分配方式”等知識點了。
圖片
如果對象過多導致空間不足,JVM就會通過垃圾回收來釋放一些空間。“如何確定對象是垃圾”、“使用哪個垃圾回收器”、“用了什么回收算法”就需要我們去了解。
圖片
實例初始化后,會將對象的引用存儲到局部變量表中。這樣的話,線程就可以通過引用訪問到該對象。
圖片
后續的代碼會延續這個流程,該加載類的加載類、該翻譯指令的翻譯、該分配內存的分配、該回收垃圾的回收,直到Java虛擬機停止工作。
本文鏈接:http://www.tebozhan.com/showinfo-26-112701-0.html我們一起聊聊 JVM 是如何執行Java程序的
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com