1. 程式人生 > >JVM-Java記憶體區域與記憶體溢位

JVM-Java記憶體區域與記憶體溢位

JVM虛擬機器執行時資料區結構分為:

    

其中方法區和堆是所有執行緒共享的記憶體區域,而Java棧、本地方法棧、程式計數器是執行緒私有的。

我們詳細介紹執行時資料區的各個區域及其作用。


 程式計數器:

  一塊較小的記憶體空間,位元組碼指示器工作時通過改變計數器的值來選取下一條需要執行的位元組碼指令。分支、迴圈、跳轉、異常處理、執行緒恢復等基礎功能都需要依賴這個計數器來完成。

  JVM的多執行緒通過執行緒輪流切換並分配處理器執行時間的方式來實現,在任何一個確定的時刻,一個處理器都只會執行一條執行緒中的指令,為了保證執行緒切換後能恢復到正確的執行位置,每條執行緒都需要有一個獨立的程式計數器,各條執行緒之間的計數器互不影響,獨立儲存,我們稱這類記憶體區域為“執行緒私有”的記憶體。

  儲存內容:

    當執行緒執行一個Java方法時,計數器記錄的是正在執行的JVM位元組碼指令的地址。如果執行的是Native方法,計數器則為空(Undefined)。

  該記憶體區域是唯一一個在jvm規範中沒有規定任何OutOfMemoryError情況的區域。


 Java虛擬機器棧:

  執行緒私有,生命週期與執行緒相同。虛擬機器棧描述的是Java方法執行的記憶體模型:每個方法被執行的時候都會同時建立一個棧幀(Stack Frame)用於儲存區域性變量表、操作棧、動態連結、方法出口等資訊。每一個方法被呼叫直至執行完成的過程,就對應著一個棧幀在虛擬機器棧中從入棧到出棧的過程。

  儲存內容:

    棧的儲存可以說是區域性變量表部分,存放了編譯期可知的基本資料型別(boolean、byte、char、short、int、float、long、double),物件引用(reference型別,它不等同於物件本身,根據不同的虛擬機器實現,它可能是一個指向物件起始地址的引用指標,也可能指向一個代表物件的控制代碼或者其他與此物件相關的位置)和returnAddress型別(指向了一條位元組碼指令的地址)。區域性變量表所需的記憶體空間在編譯期間完成分配,當進入一個方法時,這個方法需要在幀中分配多大的區域性變數空間是完全確定的,在方法執行期間不會改變區域性變量表的大小。

  異常狀況:

    1、執行緒請求的棧深度大於JVM所允許的深度,丟擲StackOverflowError異常。

    2、虛擬機器棧可以動態擴充套件時,當無法申請到足夠的記憶體,就會丟擲OutOfMemoryError異常。


 本地方法棧:

  作用與虛擬機器棧非常相似,區別在於虛擬機器棧服務於執行Java方法,而本地方法棧服務於執行Native方法。它也會丟擲StackOverflowErrorOutOfMemoryError異常


 Java堆:

  Java虛擬機器管理的記憶體中最大的一塊,被所有執行緒共享的記憶體區域。虛擬機器啟動時建立,該區域的目的就是存放物件例項,幾乎所有物件例項都在這裡分配記憶體。Java堆也是垃圾收集器管理的主要區域,又被稱為GC堆。

  從收集器的分代收集法來看,Java堆還可以分為:新生代和老年代。Java堆可以處於物理上不連續的記憶體空間,只要邏輯上是連續的即可。

  儲存內容:

    幾乎所有物件例項

  異常情況:

    如果在堆中沒有記憶體完成例項分配,並且堆無法再擴充套件時,將丟擲OutOfMemoryError異常。


 方法區:

  與Java堆一樣,是各個執行緒共享的記憶體區域。別名非堆(Non-Heap),目的是與Java對區分開

  儲存記憶體:

    儲存已被虛擬機器載入的類資訊、常量、靜態變數、即時編譯器編譯後的程式碼等資料

  Java虛擬機器規範對這塊區域限制非常寬鬆,除了和Java堆一樣不需要連續的記憶體和可以選擇固定大小或者可擴充套件外,還可以選擇不實現垃圾收集。因此又有很多人將其趁為永久代。這個區域記憶體回收目標主要是針對常量池的回收和對型別的解除安裝,一般來說這個區域的回收“成績”比較難以令人滿意,尤其是型別的解除安裝,條件相當苛刻,但是這部分割槽域的回收確實是有必要的。

  異常情況:

    當方法區無法滿足記憶體分配需求時,將丟擲OutOfMemoryError異常。

  方法區有時候也被稱為持久代(PermGen),所有物件在例項化後的整個執行週期內,都被存放在堆記憶體中。堆記憶體又被劃分成不同的部分:伊甸區(Eden),倖存者區域(Survivor Sapce),老年代(Old Generation Space)。

  方法的執行都是伴隨著執行緒的。原始型別的本地變數以及引用都存放線上程棧中。而引用關聯的物件比如String,都存在在堆中。


 執行時常量池(Runtime Constant Pool):

  方法區的一部分,存放編譯期生成的各種字面量和符號引用,這部分內容將在類載入後進入方法區的執行時常量池中釋放。

  當常量池無法再申請到記憶體時會丟擲OutOfMemoryError異常。 


  直接記憶體:

  該區域並不是虛擬機器執行時資料區的一部分,也不是Java虛擬機器規範中定義的記憶體區域,但這部分記憶體被頻繁使用也可能導致OutOfMemoryError異常。

  本機直接記憶體分配不會受到Java堆大小的限制,但會受到本機總記憶體及處理器定址空間的限制。配置虛擬機器引數時,忽略直接記憶體,會使得各個記憶體區域總和大於實體記憶體限制,導致動態擴充套件時出現OutOfMemoryError異常。