JVM在運行時將數據劃分爲了5個區域來存儲,這5個區域圖示以下: java
其中方法區和堆對是全部線程共享的內存區域;而java棧、本地方法棧和程序員計數器是運行時線程私有的內存區域。程序員
首先咱們熟悉一下一個 Java 程序的工做過程。一個 Java 源程序文件,會被編譯爲字節碼文件(以 .class 爲擴展名),每一個 Java 程序都須要運行在本身的 JVM 上,被 JVM 經過字節碼解釋器加載運行。那麼程序開始運行後,都是如何涉及到各內存區域的呢?算法
歸納地說,JVM 初始運行的時候都會分配好 Heap 和 方法區,而 JVM 每遇到一個線程,就爲其分配一個 Stack、本地方法棧 和 程序計數器。當線程終止時,三者所佔用的內存空間也會被釋放掉。這也是爲何要把內存區域分爲線程共享和非線程共享的緣由,非線程共享的那三個區域的生命週期與所屬線程相同,而線程共享的區域與 Java 程序運行的生命週期相同,因此這也是系統垃圾回收的場所只發生在線程共享的區域(實際上對大部分虛擬機來講是發生在 Heap 上)的緣由。多線程
下面將逐一介紹下各個區域所作的工做及其充當的功能。 spa
對於大多數應用來講,Java堆(Java Heap)是Java虛擬機所管理的內存中最大的一塊。Java堆是被全部線程共享的一塊內存區域,在虛擬機啓動時建立。此內存區域的惟一目的就是存放對象實例,幾乎全部的對象實例都在這裏分配內存。線程
Java堆是垃圾收集器管理的主要區域,所以不少時候也被稱作「GC堆」。若是從內存回收的角度看,因爲如今收集器基本都是採用的分代收集算法,因此Java堆中還能夠細分爲:新生代和老年代;再細緻一點的有Eden空間、From Survivor空間、To Survivor空間等。對象
根據Java虛擬機規範的規定,Java堆能夠處於物理上不連續的內存空間中,只要邏輯上是連續的便可,就像咱們的磁盤空間同樣。在實現時,既能夠實現成固定大小的,也能夠是可擴展的,不過當前主流的虛擬機都是按照可擴展來實現的(經過-Xmx和-Xms控制)。blog
若是在堆中沒有內存完成實例分配,而且堆也沒法再擴展時,將會拋出OutOfMemoryError異常。生命週期
方法區域存放了所加載的類的信息(名稱、修飾符等)、類中的靜態變量、類中定義爲final類型的常量、類中的Field信息、類中的方法信息,當開發人員在程序中經過Class對象中的getName、isInterface等方法來獲取信息時,這些數據都來源於方法區域,可見方法區域的重要性。一樣,方法區域也是全局共享的,它在虛擬機啓動時在必定的條件下它也會被GC,當方法區域須要使用的內存超過其容許的大小時,會拋出OutOfMemory的錯誤信息。
在Sun JDK中這塊區域對應的爲PermanetGeneration,又稱爲持久代,默認爲64M,可經過-XX:PermSize以及-XX:MaxPermSize來指定其大小。 內存
程序計數器(Program Counter Register)是一塊較小的內存空間,它的做用能夠看作是當前線程所執行的字節碼的行號指示器。在虛擬機的概念模型裏(僅是概念模型,各類虛擬機可能會經過一些更高效的方式去實現),字節碼解釋器工做時就是經過改變這個計數器的值來選取下一條須要執行的字節碼指令,分支、循環、跳轉、異常處理、線程恢復等基礎功能都須要依賴這個計數器來完成。
因爲Java虛擬機的多線程是經過線程輪流切換並分配處理器執行時間的方式來實現的,在任何一個肯定的時刻,一個處理器(對於多核處理器來講是一個內核)只會執行一條線程中的指令。所以,爲了線程切換後能恢復到正確的執行位置,每條線程都須要有一個獨立的程序計數器,各條線程之間的計數器互不影響,獨立存儲,咱們稱這類內存區域爲「線程私有」的內存。
若是線程正在執行的是一個Java方法,這個計數器記錄的是正在執行的虛擬機字節碼指令的地址;若是正在執行的是Natvie方法,這個計數器值則爲空(Undefined)。
此內存區域是惟一一個在Java虛擬機規範中沒有規定任何OutOfMemoryError狀況的區域。
JVM棧是線程私有的,每一個線程建立的同時都會建立JVM棧,JVM棧中存放的爲當前線程中局部基本類型的變量(java中定義的八種基本類型:boolean、char、byte、short、int、long、float、double)、部分的返回結果以及Stack Frame,非基本類型的對象在JVM棧上僅存放一個指向堆上的地址,所以Java中基本類型的變量是值傳遞,而非基本類型的變量是引用傳遞,Sun JDK的實現中JVM棧的空間是在物理內存上分配的,而不是從堆上分配。
因爲JVM棧是線程私有的,所以其在內存分配上很是高效,而且當線程運行完畢後,這些內存也就被自動回收。
在Java虛擬機規範中,對這個區域規定了兩種異常情況:若是線程請求的棧深度大於虛擬機所容許的深度,將拋出StackOverflowError異常;若是虛擬機棧能夠動態擴展(當前大部分的Java虛擬機均可動態擴展,只不過Java虛擬機規範中也容許固定長度的虛擬機棧),當擴展時沒法申請到足夠的內存時會拋出OutOfMemoryError異常。
本地方法棧(Native Method Stacks)與虛擬機棧所發揮的做用是很是類似的,其區別不過是虛擬機棧爲虛擬機執行Java方法(也就是字節碼)服務,而本地方法棧則是爲虛擬機使用到的Native方法服務。
與虛擬機棧同樣,本地方法棧區域也會拋出StackOverflowError和OutOfMemoryError異常。