一、運行時區域數組
Java虛擬機在執行Java程序的時候會把它管理的內厝劃分爲若干個不一樣功能的數據區域,如圖所示多線程
![](http://static.javashuo.com/static/loading.gif)
- 首先是程序計數器,程序計數器能夠理解爲當前程序執行的字節碼的行號指示器,計數器中的數據便是下一條將要執行的字節碼指令的行號。由於Java虛擬機的多線程是經過輪流切換並分配處理器執行時間的方式來實現的,在任意一個時刻,一個處理器(單核)或是一個核(多核)都只會執行一個線程中的指令,因此,每一個線程都擁有本身的程序計數器,還有就是若是執行的是一個Java方法,那麼計數器中記錄的就是字節碼的地址,若是執行的是Native方法,那麼計數器的值則是空(UnderFined)
- 虛擬機棧,虛擬機棧描述的是Java方法執行的內存模型,每次方法在執行的時候都會建立一個棧幀,棧幀中存儲了局部變量表、操做數棧、動態連接、方法出口等等信息。每個方法的從調用到執行完成的過程,就對應着一個棧幀在虛擬機棧中入棧出棧的過程。若是線程請求的棧深度大於虛擬機所容許的深度,將拋出StackOverflowError異常,若是棧能夠動態擴展,且擴展時沒法申請到足夠的內存,就會拋出OutOfMemoryError異常。局部變量表存放了編譯器可知的各類基本數據類型(boolean、byte、char、short、float、long、double)、對象引用(reference類型,它多是一個指向對象起始地址的指針,也多是一個表明對象的句柄或是其餘於此對象相關的位置)和returnAddressl類型(指向了一條字節碼指令的地址)
- 本地方法棧和虛擬機棧的做用是相似的,區別就是本地方法棧是爲Native方法服務的,一樣也會拋出StackOverflowError異常和OutOfMemoryError異常。
- Java堆,Java堆首先是全部的線程共享的一塊內存區域,這個區域的惟一目的就是用於存放對象實例,幾乎全部的對象實例都在這裏分配內存。若是堆中沒有足夠的內存完成實例分配,而且對也沒法再擴展,將會拋出OutOfMemoryError異常。
- 方法區,這也是全部的線程共享的一塊內存區域,它用於存儲已被虛擬機加載的類的信息、常量、靜態變量、即時編譯器編譯後的代碼等數據,當方法區沒法知足內存分配需求時,將會拋出OutOfMemoryError異常。
- 運行時常量池,這是方法區的一部分,這部分專門用於存放編譯期產生的各類字面量和符號引用,這部分將在類被加載後進入方法區的常量池中存放,和方法區同樣沒法知足內存分配需求時,將會拋出OutOfMemoryError異常。
- 直接內存,這部分並非虛擬機運行時數據區的一部分,但也被頻繁使用。這部分出現的是由於Native函數本身建立的,就好比NIO類讀取文件的時候,會直接分配堆外內存,這一部份內存就是直接內存,直接內存區域動態擴展時出現內存沒法知足時會拋出OutOfMemoryError異常。
二、對象的建立函數
一般建立一個對象實例是使用的new關鍵字,當虛擬機遇到new指令的時候,首先會去檢查這個指令的參數是否能在常量池中定位到一個類的符號引用,並檢查這個符號引用表明的類是否已經被加載、解析和初始化過,若是有,那麼將先執行響應的類加載過程。佈局
類加載檢查經過後,虛擬機將這個新生對象在堆中分配內存,所須要的內存大小將在類加載完成後肯定,假設Java堆中的內存是規整的,也就是說空閒的在一邊,非空閒的在一邊,當分配內存的時候只須要指向中間邊界的指針向空閒那邊移動,這種方式稱做指針碰撞,若是是非規整的,空閒的和非空閒的互相交錯着,那麼將有一個列表記錄着那部分是空閒的,這樣分配的時候就須要從這個列表中找到合適的空閒區域,並更新表中的記錄,這種方式叫做空閒列表。spa
內存分配好以後,虛擬機將對分配的內存空間都初始化爲零值,初始化完成後,虛擬機將對對象進行必要的設置,例如對象是屬於那個類的實例、怎麼才能找到類的元數據信息,對象的哈希碼、對象的GC分代年齡。等設置結束後,虛擬機就已經建立好了一個新的對象,接下來在執行初始化方法init方法,這樣一個完整的對象就算徹底生成了。線程
三、對象的內存佈局指針
對象在內存中存儲的佈局分爲3塊區域:對象頭、實例數據和對齊補充code
- 對象 頭包括兩部分,第一部分用於存儲對象自身運行時的數據,例如哈希碼、GC分代年齡、鎖狀態標誌、線程持有的鎖、偏向線程ID、偏向時間戳等,第二部分是類型指針,即對象指向它的類元數據的指針,虛擬機經過這個指針來肯定這個對象是哪一個類的實例。另外若是對象是一個Java數組,那麼在對象頭數組中還必須有存放記錄數組長度的數據。
- 實例數據部分是對象真正存儲的有效信息,就是程序中定義的各類類型的字段內容。不管是從父類繼承下來的,仍是在子類中定義的,都須要記錄下來
- 對齊補充這一部分 不必定存在也沒有什麼特殊的含義,僅僅起到佔位符的做用,由於有的虛擬機的自動內存管理系統要求對象的起始地址必須是8字節整數倍,所以須要對象的大小也必須是8字節的整數倍。
四、對象的訪問定位對象
使用對象須要找到對象在堆中的地址,一般定位一個對象的地址主流的方式有使用句柄和直接指針兩種blog
- 使用句柄,Java堆中會專門劃分出一塊內存來做爲句柄池,棧上的reference數據中存儲的就是句柄的地址,而句柄中包含了對象實例數據與類型數據各自的地址信息。
- 直接指針將直接指向Java堆中的Java實例數據的地址,Java堆中的實例數據也將有一個指向對象類型數據的指針。
![](http://static.javashuo.com/static/loading.gif)
這兩種方法各有各的好處,句柄方式中reference中存儲的句柄地址是一直不變的,當對象被移動的時候(在垃圾處理的時候對象被移動是很廣泛的行爲)時只會改變句柄中的實例數據指針。
而直接指針的好處就是速度更快,當你要操做一個對象的時候,句柄方式須要先定位到句柄在定位到對象實例,直接指節省了一次指針定位的開銷。