java是在java虛擬機上運行,一般地大家講到的Java內存其實就是Jvm內存
Java內存模型,往往是指Java程序在運行時內存的模型,而Java代碼是運行在Java虛擬機之上的,由Java虛擬機通過解釋執行(解釋器)或編譯執行(即時編譯器)來完成,故Java內存模型,也就是指Java虛擬機的運行時內存模型。
作為Java開發人員來說,並不需要像C/C++開發人員,需要時刻注意內存的釋放,而是全權交給虛擬機去管理,那麼有就必要了解虛擬機的運行時內存是如何構成的。運行時內存模型,分為線程私有和共享數據區兩大類,其中線程私有的數據區包含程序計數器、虛擬機棧、本地方法區,所有線程共享的數據區包含Java堆、方法區,在方法區內有一個常量池。
(1)線程私有區:
(2)線程共享區:
對於大多數的程序員來說,Java內存比較流行的說法便是堆和棧,這其實是非常粗略的一種劃分,這種劃分的”堆”對應內存模型的Java堆,”棧”是指虛擬機棧,然而Java內存模型遠比這更復雜,想深入了解Java的內存,還是有必要明白整個內存模型。
運行時內存分為五大塊區域(常量池屬於方法區,算作一塊區域),前面簡要介紹了每個區域的功能,那接下來再詳細說明每個區域的內容,Java內存總體結構圖如下:
程序計數器PC,當前線程所執行的字節碼行號指示器。每個線程都有自己計數器,是私有內存空間,該區域是整個內存中較小的一塊。
當線程正在執行一個Java方法時,PC計數器記錄的是正在執行的虛擬機字節碼的地址;當線程正在執行的一個Native方法時,PC計數器則為空(Undefined)。
虛擬機棧,生命周期與線程相同,是Java方法執行的內存模型。每個方法(不包含native方法)執行的同時都會創建一個棧幀結構,方法執行過程,對應著虛擬機棧的入棧到出棧的過程。
棧幀(Stack Frame)結構
棧幀是用於支持虛擬機進行方法執行的數據結構,是屬性運行時數據區的虛擬機站的棧元素。見上圖, 棧幀包括:
因此,一個棧幀的大小不會受到
異常(Exception)
Java虛擬機規范規定該區域有兩種異常:
本地方法棧則為虛擬機使用到的Native方法提供內存空間,而前面講的虛擬機棧式為Java方法提供內存空間。有些虛擬機的實現直接把本地方法棧和虛擬機棧合二為一,比如非常典型的Sun HotSpot虛擬機。
異常(Exception):Java虛擬機規范規定該區域可拋出StackOverFlowError和OutOfMemoryError。
Java堆,是Java虛擬機管理的最大的一塊內存,也是GC的主戰場,裡面存放的是幾乎所有的對象實例和數組數據。JIT編譯器有棧上分配、標量替換等優化技術的實現導致部分對象實例數據不存在Java堆,而是棧內存。
對象創建的過程是在堆上分配著實例對象,那麼對象實例的具體結構如下:
對於填充數據不是一定存在的,僅僅是為了字節對齊。HotSpot VM的自動內存管理要求對象起始地址必須是8字節的整數倍。對象頭本身是8的倍數,當對象的實例數據不是8的倍數,便需要填充數據來保證8字節的對齊。該功能類似於高速緩存行的對齊。
另外,關於在堆上內存分配是並發進行的,虛擬機采用CAS加失敗重試保證原子操作,或者是采用每個線程預先分配TLAB內存.
異常(Exception):Java虛擬機規范規定該區域可拋出OutOfMemoryError。
方法區主要存放的是已被虛擬機加載的類信息、常量、靜態變量、編譯器編譯後的代碼等數據。GC在該區域出現的比較少。
異常(Exception):Java虛擬機規范規定該區域可拋出OutOfMemoryError。
運行時常量池也是方法區的一部分,用於存放編譯器生成的各種字面量和符號引用。運行時常量池除了編譯期產生的Class文件的常量池,還可以在運行期間,將新的常量加入常量池,比較常見的是String類的intern()方法。
但是該區域不會拋出OutOfMemoryError異常。