jvm記憶體區

lk_fuyun發表於2017-04-06

執行時的資料區域

java 虛擬機器在執行 java 程式的過程中會把它所管理的記憶體劃分為若干個不同的資料區域。這些區域都有各自的用途,以及建立銷燬時間。下面概括這些區域。

程式計數器

程式計數器是一片比較小的記憶體區域,裡面記錄著當前執行緒程式碼的執行位置。程式碼的分支、迴圈、異常處理等都是通過程式計數器來實現。
由於java的多執行緒是通過執行緒的輪流切換並分配處理器的執行時間來實現的,因此為了執行緒切換後能回到正確的執行位置每個執行緒都需要一個獨立的程式計數器。
如果一個java執行緒正在執行一個本地方法,則這個值為空。

java 虛擬機器棧

與程式計數器一樣,java 虛擬機器棧的生命週期與執行緒的生命週期相同。虛擬機器棧由一個個棧幀組成,棧幀是 java 方法執行的主要場所。每個方法在執行的同時會建立一個棧幀。用於儲存區域性變數表,動態連結,運算元棧,方法返回地址等資訊。每個 java 方法開始執行和執行結束就對應著一個棧幀的建立與銷燬。
區域性變數表儲存著方法中所有的區域性變數,這裡可以存放原始型別、物件引用等。運算元棧是指令碼執行的場所。
在 java 虛擬機器規範中,對這個區域規定了兩種異常:
1. StackOverflowError: 如果執行緒所請求的棧深度超過虛擬機器所允許的深度,則該異常會丟擲。
2. OutOfMemoryError: 如果虛擬機器棧可以動態擴充套件,而棧在擴充套件時無法申請到足夠的記憶體,則該異常會丟擲。

本地方法棧

本地方法棧是在呼叫 native 方法時使用的,這裡不做過多講解。

java 堆

java 堆是被執行緒共享的一塊區域。這塊區域存在的唯一目的就是存放物件例項,幾乎所有的物件例項都在這裡分配記憶體。
java 堆是垃圾收集管理的主要區域,因此也被叫為 “GC 堆”。從垃圾蒐集的角度講,現代的垃圾蒐集器都是分帶蒐集,所以 java 堆還可以細分為新生代和老年代。新生代又可再細分為 Eden 區、 From Survivor、 To Survivor 等。
java 堆的大小可以通過 vm 引數 -Xmx 與 -Xms 來控制。當 java 堆中沒有記憶體能完成例項的分配並且堆也無法擴充套件的時候,將會丟擲 OutOfMemoryError 異常。

方法區

方法區也是被所有執行緒共享的區域。它用於儲存被虛擬機器載入的類資訊、常量、靜態變數、即時編譯器編譯後的程式碼等資料。
對於 jdk 1.7 及以前的 HotSpot VM 來說,由於該 VM 的設計團隊把 GC 分代蒐集擴充套件至方法區,因此這裡也叫永久代。但是這並不是一個很好的叫法,只有 HotSpot VM 這麼做了,而且在 JDK 1.8 中 HotSpot VM 也已經放棄永久代,改用本地記憶體來管理方法區了。

執行時常量池

執行時常量池(Runtime Constant Pool)是方法區的一部分。 執行時常量池存放著 Class 檔案中的常量池的拷貝。
執行時常量池相對於 CLass 檔案常量池的另外一個重要特徵是具備動態性,Java語言並不要求常量一定只有編譯期才能產生,也就是並非預置入CLass 檔案中常量池的內容才能進入方法區執行時常量池,執行期間也可能將新的常量放入池中,這種特性被開發人員利用比較多的就是 String 類的 intern() 方法。

直接記憶體

直接記憶體(Direct Memory)並不是虛擬機器執行時資料區的一部分,也不是 Java 虛擬機器規範中定義的記憶體區域,它直接從作業系統中分配,因此不受 Java 堆大小的限制,但是會受到本機總記憶體的大小及處理器定址空間的限制,因此它也可能導致 OutOfMemoryError 異常出現。在 JDK1.4 中新引入了 NIO 機制,它是一種基於通道與緩衝區的新 I/O 方式,可以直接從作業系統中分配直接記憶體,即在堆外分配記憶體,這樣能在一些場景中提高效能,因為避免了在 Java 堆和 Native 堆中來回複製資料。

記憶體區簡圖

找不到圖片

相關文章