目錄
本文是基於Java8的HotSpot虛擬機分析的
什麼是Java虛擬機
衆所周知,java主要特點就是平臺無關性。那麼如何實現平臺無關性呢,答案就是用虛擬機將不同操作系統的差異隔離。Java 之所以要在虛擬機中運行,是因爲它提供了可移植性。Java 代碼被編譯爲 Java 字節碼,可以在不同平臺上的 Java 虛擬機實現上運行。
JVM內存模型
JVM(Java Virtual Machine)將管理的內存劃分爲不同功能的內存區域,如下圖。
一段Java代碼的運行過程:類文件編譯後生成的class文件通過類加載器class loader加載到JVM中, 然後程序計數器記錄目前執行到了哪一條字節碼指令,然後MetaSpace存放了類的相關信息(1.8以前叫方法區),再將操作的棧幀(局部變量)存放在虛擬機棧中,創建的對象實例會存儲在堆中。
其中方法區和堆是線程共享的
程序計數器和虛擬機棧、本地方法棧是線程私有的
public class Hello {
public static void main(String[] args) {
Say say=new Say();
say.say();
}
}
class Say{
public void say(){
System.out.println("hello!");
}
}
上邊這一段代碼是舉的一個例子,後邊將以這個例子來具體說下每個內存區域都存儲了什麼信息。
程序計數器
程序計數器就是用來記錄當前執行的字節碼指令的位置的,也就是記錄目前執行到了哪一條字節碼指令。就是Hello.java文件編譯後生成的.class文件的字節碼文件。
方法區
在一個jvm實例的內部,類型信息被存儲在一個稱爲方法區的內存邏輯區中。類型信息是由類加載器在類加載時從類文件中提取出來的。類(靜態)變量也存儲在方法區中。
類型信息
對每個加載的類型,jvm必須在方法區中存儲以下類型信息:
- 這個類型的完整有效名
- 這個類型直接父類的完整有效名(除非這個類型是interface或是 java.lang.Object,兩種情況下都沒有父類)
- 這個類型的修飾符(public,abstract, final的某個子集)
- 這個類型直接接口的一個有序列表
除了以上的基本信息外,jvm還要爲每個類型保存以下信息:
類型的常量池( constant pool)
域(Field)信息
方法(Method)和返回值信息
除了常量外的所有靜態(static)變量
虛擬機棧
虛擬機棧是用來保存每個方法內的局部變量等數據,每個線程都有自己的虛擬機棧。如果線程中執行了一個方法,就會對這個方法創建對應的一個棧幀。棧幀裏就有了這個方法的局部變量、方法出口等等數據。
堆
堆是用來存放創建的各種對象。例如Say()對象實例,引用的圖例如下。堆中有分爲新生代、老年代等等,這個涉及到GC(垃圾回收)具體回頭單獨開一篇說。
本地方法棧
本地方法棧的功能和特點類似於虛擬機棧,均具有線程隔離的特點以及都能拋出StackOverflowError和OutOfMemoryError異常。不同的是,本地方法棧服務的對象是JVM執行的native方法,而虛擬機棧服務的是JVM執行的java方法。如何去服務native方法?native方法使用什麼語言實現?怎麼組織像棧幀這種爲了服務方法的數據結構?虛擬機規範並未給出強制規定,因此不同的虛擬機實可以進行自由實現,我們常用的HotSpot虛擬機選擇合併了虛擬機棧和本地方法棧。
下圖是整個jvm完整的執行流程圖