深入理解Java虛擬機器 JVM基本框架
JVM基本框架
首先是JVM的整圖結構圖:
圍繞JVM記憶體空間有三個重要的組成部分(綠色框):
另外的內容還包括:
- Javac編譯器(.java源程式如何如何編譯成類檔案?java的語法糖?)
- 程式的併發執行
JVM記憶體空間
三個重要組成部分都是圍繞JVM執行時的記憶體模型展開,因此先簡單介紹這部分。
虛擬機器棧:虛擬機器棧描述的是方法Method執行的記憶體模型:每個方法被呼叫時都會建立一個棧幀,存放方法區域性變量表、運算元棧和動態連結,方法出口地址。棧幀結構如下:
- 每個執行緒都有私有的棧幀空間,相互獨立。
- 當前執行方法擁有頂部當前棧幀,方法的呼叫和返回過程就是棧幀的入棧和出棧過程。
- 區域性變量表:基本資料型別和物件引用區域性變數、方法引數和隱含的this指標。
- 運算元棧:JVM位元組碼指令集是基於運算元棧的(就像x86硬體指令集基於通用暫存器一樣)。
- 虛擬機器棧可通過VM引數-Xss(每個執行緒棧尺寸)設定棧空間大小,如果當前執行緒申請的棧深度大於虛擬機器允許的深度,StackOverFlowError異常。
本地方法棧:與虛擬機器棧類似,只是服務物件是Native方法。
- 程式計數器:當前執行緒程式碼執行指示器,每個執行緒獨立。
- 堆:幾乎所有物件例項在堆中分配空間(棧上只是堆中例項的引用),為所有執行緒共享。
- 堆是垃圾收集器GC的主要管理區域(在講到GC時會有堆上空間的細分)
- JIT編輯器的逃逸技術等,使某些物件也能夠在棧上分配空間。
方法區:存放被載入的類資訊、常量(final型別常量,常量池)、靜態變數、即時編譯器編譯後的原生代碼。
- HotSpot JVM用持久代(Permanet Generation)來存放方法區。
- 方法去垃圾回收主要是常量池回收和型別解除安裝。
[執行時常量池](Runtime Constant Pool)
- 是方法區一部分,與Class檔案中[常量池](Constant Pool Table)相對應,當類載入後,Class檔案的[常量池]將存放在[執行時常量池]中。
- [常量池]存放編譯期生成的字面量(字串”abc”,宣告為final常量等)和符號引用
物件的記憶體佈局和建立
1)堆是物件儲存區域,那麼一個物件記憶體佈局是怎樣的?
2)虛擬機器如何能夠解讀物件例項資料?即虛擬機器如何知道物件中各欄位具體偏移?
將類資訊指標置於物件頭內,如上圖。klass pointer指向方法區內物件型別元資料。這也是HotSpot的做法。
通過控制代碼。
3)物件如何建立?
Point originOne = new Point(0,0);
在類被裝載、連線和初始化,這個類就隨時都可能使用了。物件例項化和初始化是就是物件生命的起始階段的活動,在這裡我們主要討論物件的初始化工作的相關特點。
Java 編譯器在編譯每個類時都會為該類至少生成一個例項初始化方法–即 “<init>()
” 方法。此方法與原始碼中的每個構造方法相對應,如果類沒有明確地宣告任何構造方法,編譯器則為該類生成一個預設的無參構造方法,這個預設的構造器僅僅呼叫父類的無參構造器,與此同時也會生成一個與預設構造方法對應的 “<init>()
” 方法.
通常來說,<init>()
方法內包括的程式碼內容大概為:呼叫另一個<init>()
方法;對例項變數初始化;與其對應的構造方法內的程式碼。
如果構造方法是明確地從呼叫同一個類中的另一個構造方法開始,那它對應的<init>()
方法體內包括的內容為:一個對本類的<init>()
方法的呼叫;對應用構造方法內的所有位元組碼。
如果構造方法不是通過呼叫自身類的其它構造方法開始,並且該物件不是 Object 物件,那<init>()
法內則包括的內容為:一個對父類<init>()
方法的呼叫;對例項變數初始化方法的位元組碼;最後是對應構造子的方法體位元組碼。
如果這個類是 Object,那麼它的<init>()
方法則不包括對父類<init>()
方法的呼叫。