開局一張圖,前面已經從每一部分解析過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等,認真寫好每一篇文章,不喜歡標題黨,不喜歡花里胡哨,大多寫系列文章,不能保證我寫的都完全正確,但是我保證所寫的均經過實踐或者查找資料。遺漏或者錯誤之處,還望指正。
平日時間寶貴,只能使用晚上以及周末時間學習寫作,關注我,我們一起成長吧~