Java
虛擬機在執行Java
程序的過程當中會把它所管理的內存劃分爲若干個不一樣的區域,它們有的隨着虛擬機進程的啓動而存在,有些區域則依賴用戶線程的啓動和結束而創建而銷燬。 下面,咱們就分兩個部分討論:程序員
Java
方法,那麼計數器記錄的是正在執行的虛擬機字節碼指令的地址;若是正在執行的是Native
方法,那這個計數器則爲空。 此內存區域是惟一一個在Java
虛擬機規範中沒有規定任何OOM
狀況的區域。Java
虛擬機的多線程是經過線程輪流切換並分配處理器執行時間的方式來實現的,所以,爲了線程切換後能恢復到正確的執行位置,每條線程都須要有一個獨立的程序計數器。Java
虛擬機棧Java
方法執行的內存模型,每一個方法在執行的同時會建立一個棧幀,用於存儲局部變量表、操做數棧、動態連接、方法出口等信息。 局部變量表:存放了編譯期可知的各類基本數據類型(boolean/byte/..
),對象引用(指向對象起始地址的引用指針,或者是指向一個表明對象的句柄,或者是其它與此對象相關的位置)和returnAddress
地址。 局部變量表所需的內存空間在編譯期間完成分配,當進入一個方法時,這個方法須要在棧中分配多大局部變量空間是徹底肯定的,在方法運行期間不會改變局部變量表的大小。 每個方法從調用到執行完成的過程,就對應一個棧幀在虛擬機棧中出棧到入棧的過程。StackOverflowError
異常;若是虛擬機棧能夠動態擴展,當擴展時沒法申請到足夠的內存,就會拋出OOM
。Java
虛擬機方法棧相似,不過本地方法棧爲虛擬機使用到的Native
方法,有些虛擬機(譬如HotSpot
)直接將本地方法棧和虛擬棧合二爲一。Java
堆Java
堆在虛擬機啓動時建立,它的目的是存放對象實例,它也是垃圾收集器管理的主要區域。 Java
堆能夠處於物理上不連續的內存空間中,只要邏輯上連續便可,在實現上,既能夠實現成固定大小的,也能夠是可擴展的。OOM
異常。下面咱們討論一下堆中的對象分配、佈局和訪問過程:數組
對象的建立分爲如下幾步:多線程
new
指令時,首先將去檢查這個指令的參數是否能在常量池中定位到一個類的符號引用,而且檢查這個符號引用表明的類是否已經被加載、解析和初始化過,若是沒有,那麼必須執行相應的類加載過程,多線程的問題下的解決方案:佈局
對分配內存空間的動做進行同步,虛擬機上採用CAS
配上失敗重試的方式保證更新操做的原子性。線程
每一個線程在Java
堆中預先分配一小塊內存,成爲本地線程分配緩衝TLAB
,哪一個線程須要分配內存,就在哪一個線程的TLAB
上分配,只有TLAB
用完須要分配新的TLAB
才須要同步。指針
第三步:在內存分配完成,把除了對象頭以外的分配到的內存空間都初始化爲零值,接下來就是對對象進行必要的設置,這些信息存放在對象頭中。code
第四步:當對象頭設置完畢以後,從虛擬機的視角來看,新的對象就產生了,接着就執行<init>
方法,把對象按照程序員的意願進行初始化。對象
對象在內存中存儲的佈局能夠分爲三個區域:對象頭、實例數據、對其填充。接口
對象頭進程
存儲對象自身的運行時數據:HashCode
、Gc
分代年齡、鎖狀態標誌、線程持有的鎖、偏向線程ID
、偏向時間戳等,所佔位數和虛擬機位數相同。
類型指針:對象指向它的類元數據的指針,虛擬機經過這個指針來肯定這個對象是哪一個類的實例
若是對象是一個Java
數組,那麼在對象頭中還必須有一塊記錄數組長度的數據。
實例數據 包括在父類和子類中所定義的各類類型的字段內容,存儲順序收到虛擬機分配策略參數的影響,相同寬度的字段老是被分配到一塊兒,在知足這個前提條件下,父類中定義的變量會出如今子類以前。
對齊填充 HotSpot
要求對象的大小必須是8
字節的整數倍,而對象頭部分正好是8
字節的整數倍,當對象實例數據部分沒有對齊時,就須要經過對齊填充來不全。
對象的訪問有兩種方式:
Java
堆中劃分出一塊內存做爲句柄池,reference
中存儲的就是對象的句柄地址,而句柄中包含了對象實例數據與類型數據各自的地址信息。 優勢:reference
中存儲的是穩定的句柄地址,在對象被移動時,只會改變句柄中的實例數據指針,而reference
自己不須要修改。Java
堆對象的佈局中放置訪問類型數據的相關信息,reference
中存儲的直接就是對象地址。 優勢:速度快,HotSpot
採用的就是這種方式。HotSpot
選擇把GC
分代收集器擴展至方法區,或者說用永久代來實現方法區,這樣HotSpot
的垃圾收集器能夠像管理Java
堆同樣管理這部份內存,可以省去專門爲這個方法區編寫內存管理的代碼。 對這區域的內存回收主要是針對常量池的回收和對類型的卸載。OOM
。Class
文件中除了有類的版本、字段、方法、接口等描述信息外,還有一項信息是常量池,用於存放編譯期生成的各類字面量和符號引用,這部份內容將在類加載後進入方法區的運行時常量池中存放。 並不是預置入Class
文件中常量池的內容才能進入方法區運行時常量池,運行期間也可能將新的常量放入池中,例如String
類的intern
方法。 當常量池中沒法再申請內存,就會拋出OOM
異常。