歡迎訪問我的博客:http://wangnan.tech
參考:
http://gityuan.com/2016/01/09/java-memory/
- 一般地大家講到的Java內存其實就是Jvm內存
- Java代碼是運行在Java虛擬機之上的,由Java虛擬機通過解釋執行(解釋器)或編譯執行(即時編譯器)來完成,故Java內存模型,也就是指Java虛擬機的運行時內存模型。
內存模型圖
詳細介紹
程序計數器PC
程序計數器PC,當前線程所執行的字節碼行號指示器。每個線程都有自己計數器,是私有內存空間,該區域是整個內存中較小的一塊。
當線程正在執行一個Java方法時,PC計數器記錄的是正在執行的虛擬機字節碼的地址;當線程正在執行的一個Native方法時,PC計數器則為空(Undefined)。
虛擬機棧
虛擬機棧,生命周期與線程相同,是Java方法執行的內存模型。每個方法(不包含native方法)執行的同時都會創建一個棧幀結構,方法執行過程,對應著虛擬機棧的入棧到出棧的過程。
棧幀(Stack Frame)結構
棧幀是用于支持虛擬機進行方法執行的數據結構,是屬性運行時數據區的虛擬機站的棧元素。見上圖, 棧幀包括:
- 局部變量表 (locals大小,編譯期確定),一組變量存儲空間, 容量以slot為最小單位。
- 操作棧(stack大小,編譯期確定),操作棧元素的數據類型必須與字節碼指令序列嚴格匹配
- 動態連接, 指向運行時常量池中該棧幀所屬方法的引用,為了 動態連接使用。
前面的解析過程其實是靜態解析;
對于運行期轉化為直接引用,稱為動態解析。 - 方法返回地址
正常退出,執行引擎遇到方法返回的字節碼,將返回值傳遞給調用者
異常退出,遇到Exception,并且方法未捕捉異常,那么不會有任何返回值。 - 額外附加信息,虛擬機規范沒有明確規定,由具體虛擬機實現。
異常(Exception)
Java虛擬機規范規定該區域有兩種異常:
- StackOverFlowError:當線程請求棧深度超出虛擬機棧所允許的深度時拋出
- OutOfMemoryError:當Java虛擬機動態擴展到無法申請足夠內存時拋出
本地方法棧
本地方法棧則為虛擬機使用到的Native方法提供內存空間,而前面講的虛擬機棧式為Java方法提供內存空間。有些虛擬機的實現直接把本地方法棧和虛擬機棧合二為一,比如非常典型的Sun HotSpot虛擬機。
異常(Exception)
Java虛擬機規范規定該區域可拋出StackOverFlowError和OutOfMemoryError。
方法區
方法區主要存放的是已被虛擬機加載的類信息、常量、靜態變量、編譯器編譯后的代碼等數據。GC在該區域出現的比較少。
異常(Exception)
Java虛擬機規范規定該區域可拋出OutOfMemoryError。
運行時常量池
運行時常量池也是方法區的一部分,用于存放編譯器生成的各種字面量和符號引用。運行時常量池除了編譯期產生的Class文件的常量池,還可以在運行期間,將新的常量加入常量池,比較常見的是String類的intern()方法。
字面量:與Java語言層面的常量概念相近,包含文本字符串、聲明為final的常量值等。
符號引用:編譯語言層面的概念,包括以下3類:
- 類和接口的全限定名
- 字段的名稱和描述符
- 方法的名稱和描述符
該區域會拋出OutOfMemoryError異常。
Java堆
Java堆,是Java虛擬機管理的最大的一塊內存,也是GC的主戰場,里面存放的是幾乎所有的對象實例和數組數據。JIT編譯器有棧上分配、標量替換等優化技術的實現導致部分對象實例數據不存在Java堆,而是棧內存。
- 從內存回收角度,Java堆被分為新生代和老年代;這樣劃分的好處是為了更快的回收內存;
- 從內存分配角度,Java堆可以劃分出線程私有的分配緩沖區(Thread Local Allocation Buffer,TLAB);這樣劃分的好處是為了更快的分配內存;
對象創建的過程是在堆上分配著實例對象,那么對象實例的具體結構如下:
對于填充數據不是一定存在的,僅僅是為了字節對齊。HotSpot VM的自動內存管理要求對象起始地址必須是8字節的整數倍。對象頭本身是8的倍數,當對象的實例數據不是8的倍數,便需要填充數據來保證8字節的對齊。該功能類似于高速緩存行的對齊。
另外,關于在堆上內存分配是并發進行的,虛擬機采用CAS加失敗重試保證原子操作,或者是采用每個線程預先分配TLAB內存.
異常(Exception)
Java虛擬機規范規定該區域可拋出OutOfMemoryError。