Java虛擬機器札記-Java記憶體區域劃分
眾所周知,Java程式設計師不需要為物件手動分配和釋放記憶體。這要歸功於虛擬機器。但凡事有利就有弊,一旦出現記憶體溢位或洩露方面的問題,程式設計師如果不瞭解虛擬機器是如何管理記憶體的,排查問題不是件容易的事情。本文將介紹Java虛擬機器管理的各個記憶體區域,為以後學習虛擬機器是如何管理記憶體打好基礎。
執行時資料區域 根據《Java虛擬機器規範》,Java虛擬機器管理的記憶體可以分為以下幾個執行時資料區域。
程式計數器 Java虛擬機器棧 本地方法棧 Java堆 方法區 程式計數器、Java虛擬機器棧和本地方法棧是“執行緒私有”的,這類記憶體區域會隨著使用者執行緒的啟動和結束而建立和銷燬。 Java堆和方法區是被所有執行緒共享的,會隨著虛擬機器的啟動而存在。
程式計數器 程式計數器(Program Counter Register)是一塊較小的記憶體空間,可以看作是當前執行緒所執行位元組碼的行號指示器。分支、迴圈、跳轉、異常處理、執行緒恢復等基礎功能都需要依賴這個計數器完成。
由於 Java 虛擬機器的多執行緒是通過執行緒輪流切換並分配處理器執行時間的方式實現的。為了執行緒切換後能恢復到正確的執行位置,每條執行緒都需要一個獨立的程式計數器,各執行緒之間的計數器互不影響,獨立儲存,我們稱程式計數器為“執行緒私有”的記憶體。
如果執行緒正在執行的是一個 Java 方法,計數器記錄的是正在執行的虛擬機器位元組碼指令的地址。如果正在執行的是 Native 方法,這個計數器的值為空。
程式計數器是唯一一個沒有規定任何 OutOfMemoryError 的區域。
Java 虛擬機器棧 Java 虛擬機器棧(Java Virtual Machine Stacks)也是執行緒私有的,生命週期與執行緒相同。 虛擬機器棧描述的是 Java 方法執行的記憶體模型:每個方法被執行的時候都會建立一個棧幀(Stack Frame)用於儲存
區域性變量表 操作棧 動態連結 方法出口 等資訊。每一個方法被呼叫到執行完成的過程,就對應著一個棧幀在虛擬機器棧中從入棧到出棧的過程。我們常說的“堆記憶體”指的就是Java虛擬機器棧。 區域性變量表儲存了編譯期可知的各種基本資料型別、引用型別、returnAddress型別。區域性變量表的記憶體空間會在編譯期間分配完成。 這個區域有兩種異常情況:
StackOverflowError:執行緒請求的棧深度大於虛擬機器所允許的深度 OutOfMemoryError:虛擬機器棧擴充套件到無法申請足夠的記憶體時 本地方法棧 本地方法棧(Native Method Stacks)和Java虛擬機器棧很相似,區別在於Java虛擬機器棧為虛擬機器執行 Java 方法(位元組碼)服務,而本地方法棧為虛擬機器使用到的 Native 方法服務。
與Java虛擬機器棧一樣,本地方法棧也會丟擲StackOverflowError和OutOfMemoryError兩種異常。
Java堆 對大多數應用來說,Java堆(Java Heap)是Java虛擬機器中記憶體最大的一塊。Java 堆在虛擬機器啟動時建立,被所有執行緒共享。作用是存放物件例項。
Java 堆是垃圾收集器管理的主要區域。在介紹GC時會詳細講解這一部分。
這個區域有一種異常情況:OutOfMemoryError,如果在堆中沒有完成記憶體分配,並且堆再也無法擴充套件時,將丟擲此異常。
方法區 方法區(Method Area)被所有執行緒共享,用於儲存已被虛擬機器載入的類資訊、常量、靜態變數、即時編譯器編譯後的程式碼等資料。
和 Java 堆一樣,不需要連續的記憶體,可以選擇固定的大小,更可以選擇不實現垃圾收集。
這個區域有一種異常情況:OutOfMemoryError,當方法區無法滿足記憶體分配需求時,將丟擲此異常。 執行時常量池 執行時常量池(Runtime Constant Pool)是方法區的一部分。用於存放編譯器中生成的各種字面量和符號引用。執行時常量池可以在執行期間將新的常量放入池中。
除了以上的集中虛擬機器執行時資料區域,還有一種頻繁被使用的記憶體需要注意:直接記憶體(Direct Memory)。
直接記憶體 在JDK1.4中加入了NIO,引入了一種基於通道和緩衝區的I/O方式,它可以使用Native函式庫直接分配堆外記憶體。顯然,這部分記憶體不會受到Java堆大小的限制,而在配置虛擬機器引數時如果忽略了直接記憶體,可能會導致各個記憶體區域總和大於實體記憶體,出現OutOfMemoryError。
總結