一直對JVM的基本結構很困惑,今天抽空整理下。
Java Virtual Machine的簡稱即Java虛擬機
虛擬機,是指通過軟件模擬具有完整硬件功能的完整計算機系統.比如VMWare/Visual Box/JVM.VMWare和Visual Box模擬的是物理CPU的指令集,我們看的見摸的著,而JVM所模擬的是JAVA字節碼的指令集.
JVM定義了JVM規范,如Class文件格式,運行時的數據的存儲、幀棧、虛擬機的指令集等。比如Scala可以在JVM上運行,很顯然Scala不符合Java語言規范,但是卻是符合JVM規范的。
JVM基本結構
有一個經典的圖,畫了JVM的內部結構(話說,已經在好多地方看到這個圖了,但是完全不知道這個圖的出處)
類加載器負責把class文件加載到jvm的內存空間里去,JVM的內存空間是分區域的,分為方法區,java堆,Java棧,本地方法區.然后執行引擎負責執行方法區里的內容,還有一個pc寄存器指向下一條需要運行的指令的地址,同時還有GC,來負責內存空間的垃圾回收.
類加載器
使用java編譯器可以把java代碼編譯為存儲字節碼的Class文件,使用其他語言的編譯器一樣可以把程序代碼翻譯成Class文件,java虛擬機不關心Class的來源是何種語言。
類從被加載到虛擬機內存中開始,到卸載出內存為止,它的生命周期包括了:加載(Loading)、驗證(Verification)、準備(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)、卸載(Unloading)七個階段,其中驗證、準備、解析三個部分統稱鏈接。具體每一步做了什么,見類的加載和初始化過程.
PC寄存器
每一個線程都會分配一個PC寄存器,PC寄存器總是會指向下一條指令的地址
方法區
用來保存類的元信息,如常量池,方法的信息,方法的字節碼,方法區通常我們叫永久區.
Java堆
New出來的對象都在Java堆里,且堆是全局共享的,所有的線程都可以訪問到。
從GC的角度來看,堆是分代的,一般可以分為Eden、s0、s1、tenured。
Java棧
棧跟堆不同,棧是線程私有的。
棧是由幀組成,所以java的棧也叫幀棧。
幀里主要包含以下幾部分內容:局部變量表、操作棧、動態鏈接、返回地址。
局部變量表
局部變量表分為兩種,一種是靜態方法,還有一種是實例方法.實例方法的分配基本和靜態方法一致,區別是,第一個槽位,實例方法傳的是當前對象的引用(this).
局部變量表的槽位長度是32位.
//todo分析局部變量表
//todo分析操作棧
棧上分配
Java棧還有一個比較重要的概念:棧上分配。棧上分配顧名思義,也就是變量和對象在棧上分配,不在堆里分配空間。
我們來看一段代碼。
首先我們通過參數
-server -Xmx10m-Xms10m -XX:-DoEscapeAnalysis -XX:+PrintGC
來運行代碼
得到的結果如下:
然后,我們通過下面的參數再運行一次:
-server -Xmx10m-Xms10m -XX:+DoEscapeAnalysis-XX:+PrintGC
得到的結果如下:
對比之下,我們可以看到我們加上了+DoEscapeAnalysis之后,GC也沒有了,運行時間也少了幾百倍,由此可見棧上分配優化的好處。棧上分配一般都是分配的小對象,因為每個線程都有一個棧,棧本來就很小,基本上是幾百K到1M。