JVM內存區域

Java運行時數據區(Java內存區域)

        爲更好理解Java內存區域,咱們首先看一下Java程序的執行過程:java

        Java源代碼文件(.java後綴)會被Java編譯器編譯爲字節碼文件(.class後綴)。而後由JVM中的類加載器加載各個類的字節碼文件,加載完畢以後,交由JVM執行引擎執行。算法

        Java虛擬機在執行Java程序的過程當中會把它所管理的內存劃分爲若干個不一樣的數據區域。這些區域都有各自的用途,以及建立和銷燬的時間,有的區域隨着虛擬機進程的啓動而存在,有些區域則依賴用戶線程的啓動和結束而創建和銷燬。根據《Java虛擬機規範(Java SE 7版)》的規定,Java虛擬機所管理的內存將會包括如下幾個運行時數據區域。以下圖所示:編程

 

1 程序計數器

       程序計數器(Program Counter Register)是一塊較小的內存空間,它能夠看作是當前線程所執行的字節碼的行號指示器。在虛擬機的概念模型裏(僅是概念模型,各類虛擬機可能會經過一些更高效的方式去實現),字節碼解釋器工做時就是經過改變這個計數器的值來選取下一條須要執行的字節碼指令,分支、循環、跳轉、異常處理、線程恢復等基礎功能都須要依賴這個計數器來完成。緩存

       因爲Java虛擬機的多線程是經過線程輪流切換並分配處理器執行時間的方式來實現的,在任何一個肯定的時刻,一個處理器(對於多核處理器來講是一個內核)都只會執行一條線程中的指令。所以,爲了線程切換後能回覆到正確的執行位置,每條線程都須要有一個獨立的程序計數器,各條線程之間計數器互不影響,獨立存儲,咱們稱這類內存區域爲「線程私有」的內存。數據結構

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

2 Java虛擬機棧

       與程序計數器同樣,Java虛擬機棧(Java Virtual Machine Stacks)也是線程私有的,它的生命週期與線程相同。虛擬機棧描述的是Java方法執行的內存模型:每一個方法在執行的同時都會建立一個棧幀(Stack Frame)用於存儲局部變量表、操做數棧、動態連接、方法出口等信息。每個方法從調用直至執行完成的過程,就對應着一個棧幀在虛擬機棧入棧到出棧的過程。框架

       局部變量表存放了編譯期可知的各類基本數據類型(boolean,char,byte,short,int,long,float,double)、對象引用(referee類型,它不等同於對象自己,多是一個指向對象起始地址的引用指針,也多是指向一個表明對象的句柄或者其它與此對象相關的位置)和returnAddress類型(指向一條字節碼指令的地址)。編程語言

       在Java虛擬機規範中,對這個區域規定了兩種異常狀況。若是線程請求的棧深度大於虛擬機所容許的深度,將拋出StackOverflowError異常;若是虛擬機能夠動態擴展,若是擴展時沒法申請到足夠的內存,就會拋出OutOfMemoryError異常。函數

3 本地方法棧

       本地方法棧(Native Method Stack)與虛擬機棧所發揮的做用是很是類似的,它們之間的區別不過是虛擬機棧爲虛擬機執行Java方法(也就是字節碼),而本地方法棧則爲虛擬機使用到的Native方法服務。在虛擬機規範中對本地方法棧中方法使用的語言、使用方式與數據結構並無強制規定,所以具體的虛擬機能夠自由實現它。甚至有的虛擬機(譬如Sun HotSpot虛擬機)直接就把本地方法棧和虛擬機棧合二爲一。與虛擬機棧同樣,本地方法棧也會拋出StackOverflowError和OutOfMemoryError異常。spa

       注:什麼是本地方法?簡單地講,一個Native Method就是一個java調用非java代碼的接口。一個Native Method是這樣一個java的方法:該方法的實現由非java語言實現,好比C。這個特徵並不是java所特有,不少其它的編程語言都有這一機制,好比在C++中,你能夠用extern "C"告知C++編譯器去調用一個C的函數。

4 Java堆

       Java堆(Java Heap)是 Java 虛擬機所管理的內存中最大的一塊。 Java堆是被全部線程共享的一塊內存區域,在虛擬機啓動時建立。此內存區域的惟一目的就是存放對象實例,幾乎全部的對象實例都在這裏分配內存。

       Java 堆是垃圾收集器管理的主要區域,所以不少時候也被稱作「GC 堆「(Garbage Collected Heap)。從內存回收的角度來看,因爲如今收集器基本都採用分代收集算法,因此Java堆中還能夠細分爲:新生代和老年代;在細緻一點有Eden空間、From Survivor框架等。從內存分配的角度來看,線程共享的Java堆中可能劃分出多個線程私有的分配緩存區。不過不管如何劃分,都與存放內容無關,不管哪一個區域,存儲的都仍然是對象實例,進一步劃分的目的是爲了更好的回收內存,或者更快地分配內存。

       根據Java虛擬機規範的規定,Java堆能夠處於物理上不連續的內存空間中,只要邏輯上是連續的便可,就像咱們的磁盤空間同樣。在實現時,既能夠實現成固定大小的,也能夠是擴展的,不過當前主流的虛擬機都是按照可擴展來實現的(經過=Xmx和-Xms控制)。若是在堆中沒有內存完成實例分配,而且堆也沒法再擴展時,將會拋出OutOfMemoryError異常。

5 方法區

       方法區(Method Area)與 Java 堆同樣,是各個線程共享的內存區域,它用於存儲已被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯後的代碼等數據。雖然 Java 虛擬機規範把方法區描述爲堆的一個邏輯部分,可是它卻有一個別名叫作 Non-Heap(非堆),目的應該是與 Java 堆區分開來。

       Java虛擬機規範對方法區的限制很是寬鬆,除了和Java堆同樣不須要連續的內存和能夠選擇固定大小或者可擴展外,還能夠選擇不實現垃圾收集。相對而言,垃圾收集行爲在這個區域是比較少見的,但並不是數據進入了方法區就如永久代的名字同樣」永久「存在了,這區域的內存回收目標主要是針對常量池的回收和對類型的卸載,通常來講,這個區域的回收」成績「比較難以使人滿意,尤爲是類型的卸載,條件至關苛刻,可是這部分區域的回收確實是必要的。

       根據Java虛擬機規範的規定,當方法去沒法知足內存分配需求時,將拋出OutOfMemoryError異常。

       

參考書目《深刻理解Java虛擬機》

相關文章
相關標籤/搜索