Java虛擬機器記憶體區域劃分
Java程式執行時的資料區域
Java虛擬機器在執行Java程式的過程中,會把它所管理的記憶體劃分為若干個不同的資料區域,這些區域都有各自的用途,以及建立和銷燬的時間,有的區域隨著虛擬機器程序的啟動而存在,有些區域則依賴使用者執行緒的啟動和結束而建立和銷燬。Java虛擬機器所管理的記憶體主要包含下圖所示的幾個執行時資料區域。
程式計數器(PC Registers)
程式計數器是一塊比較小的記憶體空間,它可以看作是當前執行緒所執行的位元組碼的行號指示器。在虛擬機器的概念模型中,位元組碼直譯器工作時就是通過改變這個計數器的值來選取下一條需要執行的位元組碼指令,分支、迴圈、跳轉、異常處理、執行緒恢復等基礎功能都需要依賴這個計數器來完成。
如果執行緒正在執行的是一個Java方法,這個計數器記錄的是正在執行的虛擬機器位元組碼指令的地址;
如果正在執行的是一個Native方法,這個計數器值則為空(Undefined)。
此記憶體區域是唯一一個在Java虛擬機器規範中沒有規定任何OutOfMemoryError情況的區域。
Java虛擬機器棧(Java Language Stacks)
與程式計數器一樣,Java虛擬機器棧也是執行緒似有的,它的生命週期與執行緒相同。
虛擬機器棧描述的是Java方法執行的記憶體模型:每個方法在執行的同時都會建立一個棧幀(Stack Frame)用於儲存區域性變量表、運算元棧、動態連結、方法出口等資訊。每一個方法從呼叫直至執行完成的過程,就對應著一個棧幀在虛擬機器棧的入棧到出棧的過程。
經常有人把Java記憶體分為堆記憶體(Heap)和棧記憶體(Stack),這種分法實際上是非常粗糙的,但是也突出表現出程式設計師實際上最為關注的兩塊記憶體。這裡的“棧”則指的是虛擬機器棧,或者說是虛擬機器棧中區域性變量表部分。
區域性變量表存放了編譯期可知的各種基本資料型別(boolean, byte, char, short, int, float, long, double)、物件引用(reference型別 它不等同於物件本身,可能是一個指向物件起始地址的引用指標,也可能是指向了一個位元組碼指令的地址。)
在Java虛擬機器規範中,對這個區域規定了兩種異常狀況:StackOverflowError
OutofMemoryError
本地方法棧(Native Method Stacks)
本地方法棧與虛擬機器棧所發揮的作用是非常類似的,它們的區別不過是虛擬機器棧為虛擬機器執行Java方法(也就是位元組碼)服務,而本地方法棧則為虛擬機器使用到的Native方法服務。在Sun HotSpot虛擬機器中,直接將本地方法棧和虛擬棧合二為一。
與虛擬機器棧一樣,本地方法棧區域也會丟擲StackOverflowError
和 OutofMemoryError
異常。
Java堆(Java Heap)
對於大多數應用來說,Java堆(Java Heap)是Java虛擬機器所管理的記憶體中最大的一塊。Java堆是被所有執行緒共享的一塊記憶體區域,在虛擬機器啟動時建立。此記憶體區域的唯一目的就是存放物件的例項,幾乎所有的物件例項都會在這裡分配記憶體。
所有物件例項以及陣列都要在堆上分配,但是隨著JIT編譯器的發展與逃逸分析技術逐漸成熟,棧上分配、標量替換優化技術導致一些微妙的變化發生,所有的物件都分配在堆上也漸漸不是那麼“絕對“了。
——深入理解Java虛擬機器:JVM高階特性與最佳實踐
Java堆是垃圾收集器管理的主要區域,因此很多時候也稱為GC堆(Garbage Collected Heap)。
根據Java虛擬機器規範的規定,Java堆可以處於物理上不連續的記憶體空間中,只要邏輯上是連續的即可。因此在實現時,既可以實現成固定大小的,也可以是可擴充套件的,不過當前主流的虛擬機器都是按照可擴充套件來實現的(通過-Xmx和-Xms控制)。如果在堆中沒有記憶體完成例項分配,且堆也無法再擴充套件時,將會丟擲OutOfMemoryError
異常。
方法區(Method Area)
方法區與Java堆一樣,是各個執行緒共享的記憶體區域,它用於儲存已被虛擬機器載入的類資訊、常量、靜態變數、即時編譯器編譯後的程式碼等資料。雖然Java虛擬機器規範把方法區描述為堆的一個邏輯部分,但是它卻有一個別名叫做Non-Heap,目的是與Java堆區分開來。
根據Java虛擬機器規範的規定,當方法區無法滿足記憶體分配需求時,將丟擲OutOfMemoryError
異常。
執行時常量池(Runtime Constant Pool)
執行時常量池是方法區的一部分。Class檔案中除了有類的版本、欄位、方法、介面等描述資訊外,還有一項資訊就是常量池,用於存放編譯期生成的各種字面量和符號引用,這部分內容將在類載入後進入方法區的執行時常量池中存放。
直接記憶體(Direct Memory)
直接記憶體並不是虛擬機器執行時資料區的一部分,也不是Java虛擬機器規範中定義的記憶體區域。但是這部分記憶體也被頻繁的使用,而且也可能導致OutOfMemoryError異常出現。