Java 運行期數據區

對於 Java 開發者來講,由虛擬機進行內存管理是把雙刃劍,一方面免去了繁雜的內存管理工做,另外一方面,一旦出現內存泄漏和溢出方面的問題,若是不瞭解虛擬機是怎樣使用內存的,排查問題將成爲一項艱難的工做。本文主要介紹 Java 運行時的內存區域。java

JVM 在執行 Java 程序的過程當中,會把它所管理的內存劃分爲若干個不一樣的數據區域。如圖:算法

程序計數器

程序計數器是一塊較小的內存空間,能夠看做是當前線程所執行的字節碼的行號指示器。數組

因爲 JVM 的多線程是經過線程輪流切換並分配處理器執行時間的方式來執行的,在任何一個肯定的時刻,一個處理器(一個核心)只會執行一個線程中的指令。所以,爲了線程切換後能恢復到正確的執行位置,每一個線程都須要有一個獨立的程序計數器,各個線程之間計數器互不影響,獨立存儲。即,「線程私有」。多線程

若是線程正在執行的是一個 Java 方法,這個計數器記錄的是正在執行的虛擬機字節碼指令的地址;若是正在執行的 Native 方法,這個計數器值則爲空(Undefined)。此內存區域是惟一一個在 JVM 規範中沒有規定任何 OutOfMemoryError 狀況的區域。線程

注意:在虛擬機的概念模型中,字節碼解釋器工做時經過改變這個計數器的值來選取下一條須要執行的字節碼指令,分支、循環、跳轉、異常處理、線程恢復等基礎功能都須要以來這個計數器來完成。指針

Java 虛擬機棧

Java 虛擬機棧也是線程私有的,生命週期跟線程的相同。虛擬機棧描述的是 Java 方法執行的內存模型:每一個方法在執行的同時都會建立一個棧幀用於存儲局部變量表、操做數棧、動態連接、方法出口等信息。每一個方法從調用到執行完成的過程,對應着一個棧幀在虛擬機中入棧道出棧的過程。code

局部變量表存放了編譯期可知的各類基本類型、對象引用、指向一個表明對象的句柄、一個指向對象起始地址的引用指針或其餘與此對象相關的位置、或者是 returnAddress 類型(指向一條字節碼指令的地址)。對象

局部變量表所需的內存空間在編譯期間完成分配,當進入一個方法時,這個方法須要在棧中分配多大的局部變量空間是徹底肯定的,在方法運行期間不會改變局部變量表的大小。blog

在 JVM 規範中,對該區域規定了兩種狀況的異常:接口

  1. 若是線程請求的棧深度大於虛擬機所容許的深度,將拋出 StackOverflowError 異常;
  2. 若是虛擬機能夠動態擴展,在擴展時若是沒法申請到足夠的內存,就會跑出 OutOfMemoryError 異常。

本地方法棧

與 Java 虛擬機棧相似,虛擬機棧爲虛擬機執行 Java 方法(字節碼)服務,本地方法棧則爲虛擬機使用到的 Native 方法服務。

本地方法棧區域也會拋出 StackOverflowErrorOutOfMemoryError異常。

Java 堆

對於大多數迎來講,Java 堆是 Java 虛擬機所管理的內存中最大的一塊,該區域被全部線程共享,在虛擬機啓用時建立。幾乎全部(JIT 技術的發展,讓這裏多了「幾乎」)的對象實例和數組都在堆上分配內存。

從內存回收的角度來看,因爲如今垃圾收集器基本都採用分帶算法,全部Java堆中還能夠細分爲:新生代和老聖代;在細一點有 Eden 空間、From Survivor 空間, To Survivor 空間。(在以後垃圾回收的文章中會有介紹)。

根據 JVM 規範的規定,Java 堆能夠處於物理上不連續的區域,只要邏輯上是連續的便可。

若是在堆中沒有完成實例分配,而且堆也沒法再擴展時,將會跑出 OutOfMemoryError 異常。

方法區(永久代)

方法區也是各線程共享的內存區域,用於存儲已被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯後的代碼等數據。

根據 JVM 規範的規定,當方法區沒法知足內存分配需求時,將拋出 OutOfMemoryError 異常。

運行時常量池

運行時常量池時方法區的一部分。Class 文件中出了有類的版本、字段、方法、接口等描述信息外,還有一項信息是常量池,用於存放編譯器生成的各類字面量和符號引用,這部份內容將在類加載後漸入方法區的運行時常量池存放。

運行時常量池相對於 Class 文件常量池的另一個重要特徵是具有動態性,Java 語言並不要求常量必定已有編譯器期才能產生,也就是並不是預置入 Class 文件中常量池的內容才能進入方法區運行時常量池,運行期間也能將新的常量放入池中。

參考:

相關文章
相關標籤/搜索