一、JVM是什麼
JVM全名叫“Java Virtual Machine”,中文名叫“爪哇虛擬機”,是java和java系(如Scala、Kotlin)語言實現平臺無關性的關鍵角色。牛逼但也不玄乎,歸根結底它只是一個軟件而已,也就是運行於操作系統上的一個應用程序,與即時通訊軟件、遊戲這些應用程序沒有本質區別。但還有一點要說,JVM是一個概念,或者說是一類軟件。比如“即時通訊軟件”包括QQ、微信、網易泡泡、飛信等等,“遊戲軟件”包括陰陽師、王者榮耀、和平精英等等。“JVM類軟件”包括Oracle Hotspot(Oracle公司持有的Hotspot虛擬機)、Azul Systems Zing(Azul Systems公司基於Hotspot開發的主打低延遲的虛擬機)、Alibaba AJVM(阿里巴巴公司基於OpenJDK開發了自己的JDK,我們姑且稱其中的JVM爲“Alibaba JVM”,簡稱“AJVM”)等等。即:
即時通訊軟件:
- QQ、微信、網易泡泡、飛信、......
遊戲軟件:
- 陰陽師、王者榮耀、和平精英、......
JVM類軟件:
- Hotspot、Zing、AJVM、......
它們宏觀上所處的位置大概是這樣的,我順便標明瞭Hotspot的作用。
二、JVM的結構是什麼樣的?
既然是個應用軟件,那自然應該有很多內部模塊和處理邏輯了,下面我們先看一下它大概長什麼樣?
這是Hotspot 在jdk1.8中結構的示意圖,雖然配色有點野,可是結構該是對的(哪裏不對可以評論教教我)。
我們可以看到,它內部大概分爲了五部分:類裝載器、運行時數據區、本地方法庫、本地方法接口、執行引擎。下面逐一說明他們的作用及結構。
1.類裝載器
功能
類裝載器是用來裝載Class文件的,具體的方式有
- 從本地系統中直接加載
- 通過網絡下載.class文件
- 從zip,jar等歸檔文件中加載.class文件
- 從專有數據庫中提取.class文件
- 將Java源文件動態編譯爲.class文件
加載流程
類加載的過程包括了加載、驗證、準備、解析、初始化五個階段。其中的驗證、準備、解析三個階段也被稱爲連接階段。
加載、驗證、準備和初始化這四個階段發生的順序是確定的,而解析階段則不一定,它在某些情況下可以在初始化階段之後開始,這是爲了支持Java語言的運行時綁定(也稱爲動態綁定或晚期綁定)
2.運行時數據區
程序運行需要一定的內存空間用來存儲諸如程序的代碼、類名、方法名、變量名、對象、對象的引用等,這個內存空間就被稱爲運行時方法區。根據存儲的對象的不同,運行時方法區被分爲了五個區域。分別是方法區、Java棧、堆、本地方法棧、程序計數器。
2.1 方法區(元空間):
方法區主要存放已被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯後的代碼等數據(比如spring 使用IOC或者AOP創建bean時,或者使用cglib,反射的形式動態生成class信息等)。它是線程共享的,需要考慮線程安全問題。
方法區在jdk1.8中的實現,叫做“元空間”。它被設計放在操作系統的內存之中。
ClassLoader
用來存放類加載器信息。
Class
用來存放類信息。
常量池
用來存放程序運行中的常量。
而在jdk1.8之前的jdk,如jdk1.6中,方法區的實現叫做“永久代”。
2.2 堆
英文名叫heap,作用是存儲對象實體,即各種類的實例化對象。它是線程共享的,需要考慮線程安全問題。它是GC的主戰場。
新生代
包含伊甸園區,倖存區。倖存區有兩塊,他們交替作爲from區和to區。
老年代
存放從新生代倖存區晉升過來的大齡對象。當新生代連續空間不多的時候,也會有體積很大的對象在此處誕生。
StringTable
串池,當創建一個字符串變量時,會先從串池中搜索是否存在該字符串,若有,則直接引用,若沒有,則創建該字符串並將其添加到串池,並引用。它的本質是一個HashTable,大小是固定的不可擴容。
- 常量池中的字符串僅是符號,第一次用到時才變爲對象。
- 利用串池的機制,來避免重複創建字符串對象。
- 字符串變量的拼接原理是StringBuilder(1.8)。
- 字符串常量的拼接原理是編譯期優化。
- 可以使用intern方法,主動將串池中還沒有的字符串對象放入串池
- 1.8 將這個字符串對象嘗試放入串池,如果有則並不放入,若沒有則放入,會把串池中的對象返回。
- 1.6 將這個字符串對象嘗試放入串池,如果有則並不放入,若沒有則複製一份放入,會把串池中的對象返回。
動態拼接的字符串,不會放在串池,只會存在於堆中。除非主動調用intern方法。
(StringTable是從jdk1.7開始,從原來的方法區常量池挪到堆中的)
2.3 本地方法庫
本地方法:在java中,存在一些由其他語言實現的方法,比如c++/c,它們通常是用來控制底層資源的方法。它們通常由native修飾。
本地方法庫就是用來存放這些所需的本地方法的地方。
2.4 本地方法接口
程序在調用本地方法時,是通過其接口來調用的。這些端口就存放在這裏。
2.5 執行引擎
即基於代碼邏輯和數據進行運算的地方。是程序的實際執行者。
說明:
jdk1.7的更新:StringTable的移動
jdk1.8的更新:方法區的重新實現
GC:內容過多,需要單獨開一篇來講