1. 程式人生 > >深入理解JVM-執行時資料區域

深入理解JVM-執行時資料區域

JVM執行java程式是會把它管理的記憶體劃分為若干個不同的資料區域,如圖所示

 

程式計數器

1.是一塊較小的記憶體空間,可以看做是當前執行緒所執行的位元組碼的行號指示器;

2.JVM的多執行緒是通過執行緒輪流切換並分配處理器執行時間的方式來實現的,在任何一個確定的時刻,一個處理器(對於多個處理器來說是一個核心)都只會執行一條執行緒中的命令。因此,程式計數器起到一種標記的作用,讓處理器再次執行該執行緒的時候能找到正確的執行位置;

3.執行緒如果執行的是一個Java方法,那麼計數器中記錄的是正在執行的虛擬機器位元組碼指令地址,如果執行的是Native方法,那麼計數器值為空(undefined),此記憶體區域沒有被規定任何OutOfMemoryError的情況。

 

Java虛擬機器棧

1.虛擬機器棧描述的是java方法執行的記憶體模型:每個方法在執行的同時會建立一個棧幀用於儲存區域性變量表、運算元棧、動態連結、方法出口等資訊。每個方法從呼叫到執行完成的過程就對應這個一個棧幀在虛擬機器棧幀中入棧到出棧的過程;

2.區域性變量表存放了編譯期可知的各種基本資料型別、reference型別(不等同於物件本身,他可能是一個指向物件起始地址的指標,也可能是指向一個代表物件的控制代碼或其他與此物件相關的位置)和returnAddress型別(指向了一條位元組碼命令的地址);

3.64位長度的long和double型別的資料會佔用2個區域性變數空間(Slot),其餘的資料型別只佔用1個。區域性變量表所需的記憶體空間在編譯期間完成分配,在方法執行期間不會改變區域性變量表的大小;

4.此區域中會有兩種異常情況:如果執行緒請求的棧深度大於虛擬機器所允許的深度,將丟擲StackOverflowError;如果虛擬機器棧可以動態擴充套件,無法申請到足夠記憶體時會丟擲OutOfMemoryError。

 

本地方法棧

與虛擬機器棧發揮的做用相似,區別在於虛擬機器棧為虛擬機器執行Java方法(也就是位元組碼)服務,本地方法棧則為虛擬機器使用到的Native方法服務。

 

Java堆

1.JVM所管理的記憶體中最大的一塊,被所有執行緒共享的一塊記憶體區域,在JVM啟動時建立。唯一目的就是存放物件例項,幾乎所有的例項物件都要在這裡分配記憶體;

2.Java堆是垃圾收集器管理的主要區域,因此很多時候被稱為GC堆;

3.Java堆可以處於物理上不連續的記憶體空間中,只要邏輯上是連續的即可,如果在堆中沒有完成例項分配,並且堆也無法擴充套件時,將會丟擲OutOfMemoryError。

 

方法區

1.與堆一樣,是各個執行緒共享的記憶體區域,用於儲存已被JVM載入的類資訊、常量、靜態變數、即時編譯器編譯後的程式碼資料等。他還有個別名叫做Non-Heap;

2.當無法滿足記憶體分配需求的時候將丟擲OutOfMemoryError。

 

執行時常量池

1.方法區的一部分。Class檔案中除了有類的版本、欄位、方法、介面等描述資訊外,還有一項資訊是常量池,用於存放編譯期生成的各種字面量和符號引用,這部分內容將在類載入後進入方法區的執行時常量池中存放;

2.JVM對Class檔案的每一部分的格式都有嚴格規定,每一個位元組用於儲存哪種資料都必須符合規範上的要求才能被VM認可、裝載和執行。但對於執行時常量池,JVM規範沒有做任何細節的要求。一般來說,除了儲存Class檔案中描述的符號引用外,還會把翻譯出來的直接引用也儲存到執行時常量池中;

3.執行時常量池相對於Class檔案常量池的另一個重要特徵是具備動態性,常量不一定只有編譯期才能產生,並非預置入Class檔案中常量池的內容才能進入執行時常量池,執行期間也可以將新的常量放入池中。當常量池無法再申請到記憶體時會丟擲OutOfMemoryError。

 

直接記憶體

1.並不是執行時資料區的一部分,也不是JVM規範中定義的記憶體區域。但這部分記憶體也被頻繁使用,而且也可能導致OutOfMemoryError;

2.NIO可以使用Native函式庫直接分配堆外記憶體,然後通過一個儲存在堆中的DirectByteBuffer物件作為這塊記憶體的引用進行操作,避免了在Java堆和Native堆中來回複製資料;

3.直接記憶體的分配不會收到Java堆大小的限制,但是肯定收到本機總記憶體(包括RAM以及SWAP區或分頁檔案)大小以及處理器定址空間的限制。配置JVM引數時,會根據實際記憶體設定-Xmx等引數資訊,但經常忽略直接記憶體,使得各個記憶體區域總和大於實體記憶體限制,從而導致動態擴充套件時出現OutOfMemoryError。