開局一張圖,前面已經從每一部分解析過JVM的記憶體結構了,現在按照順序來分析:
整體上來看:類檔案從類載入子系統,載入完成之後,主要存放在方法區(JRockit和H9沒有方法區,這裡指的是HotSpot)。執行時的資料主要是存放在執行時資料區,程式碼的解釋編譯優化以及垃圾收集,都是在執行引擎中。本地方法是指Native方法,也就是C/C++編寫的方法。
類載入子系統
類檔案首先需要經過類載入子系統,進行載入,進類資訊等載入到執行時資料區。
在類載入子系統中有以下三個階段操作:
- 載入
- 連結
- 初始化
其中載入的時候,有三種類載入器:
- Bootstrap ClassLoader:引導類載入器,主要載入JDK裡面的核心類
- Extension ClassLoader:擴充類載入器
- Application ClassLoader:應用載入器
而連結也分為3個階段,主要是:
- 驗證
- 連結
- 解析
執行時資料區
經過類載入子系統載入之後,進入執行時資料區,執行時區域主要分為:
- 執行緒私有:
- 程式計數器:
Program Count Register
,執行緒私有,沒有垃圾回收 - 虛擬機器棧:
VM Stack
,執行緒私有,沒有垃圾回收 - 本地方法棧:
Native Method Stack
,執行緒私有,沒有垃圾回收
- 程式計數器:
- 執行緒共享:
- 方法區:
Method Area
,以HotSpot
為例,JDK1.8
後元空間取代方法區,有垃圾回收。 - 堆:
Heap
,垃圾回收最重要的地方。
- 方法區:
虛擬機器棧,每一個執行緒有一份,每一個執行緒的虛擬機器棧裡面,存放的是一個個棧幀,每一個棧幀表示一個方法呼叫。
PC暫存器,同樣是每一個執行緒有一份,不同執行緒之間執行到何處,互不干擾。
執行引擎
執行引擎裡面可以逐行解釋執行,也可以編譯成機器指令直接執行,主要包括:
- 直譯器
- 即時編譯器:即時編譯器中包括了中間程式碼生成器,程式碼優化器,目的碼生成器等。
- 垃圾收集器
直譯器,需要逐行解釋執行,效率低下。譬如:如果迴圈兩千次,迴圈體很大,每次執行都需要解釋執行。
JIT
編譯器,除了可以直接全部即時編譯,還可以統計出那些程式碼執行頻率比較高,這部分程式碼就是熱點程式碼,這種技術叫做熱點程式碼探測技術,JIT
編譯器會將熱點程式碼,提前編譯成為機器指令,放在方法區快取起來,下次執行到的時候,不需要解釋執行,而是直接執行機器指令。
即時編譯器的執行效率很高,為什麼不將它全部提前編譯好快取起來呢?
- 全部提前編譯,首次啟動響應速度慢,會有卡頓的感覺,因為編譯需要大量時間。(主要原因)
- 快取程式碼,需要放在方法區,佔用記憶體空間,容易溢位。
- 翻譯成為機器指令,則這部分快取的
CodeCache
是不能夠直接跨平臺,因為不同環境的機器指令是不大一樣的,只能每次執行前就全部編譯。
如果需要寫一個虛擬機器,那麼需要考慮的重要兩部分是:類載入子系統和執行引擎。類載入子系統負責將類資訊按照規定,載入到執行時資料區,而執行引擎主要負責對程式碼解釋執行或者編譯成二進位制快取起來,進行執行。
【作者簡介】:
秦懷,公眾號【秦懷雜貨店】作者,技術之路不在一時,山高水長,縱使緩慢,馳而不息。個人寫作方向:Java原始碼解析,JDBC,Mybatis,Spring,redis,分散式,劍指Offer,LeetCode等,認真寫好每一篇文章,不喜歡標題黨,不喜歡花裡胡哨,大多寫系列文章,不能保證我寫的都完全正確,但是我保證所寫的均經過實踐或者查詢資料。遺漏或者錯誤之處,還望指正。
平日時間寶貴,只能使用晚上以及週末時間學習寫作,關注我,我們一起成長吧~