1.程序計數器java
爲了線程切換後能恢復到正確的執行位置,每條線程都須要有一個獨立的程序計數器。若是線程正在執行的是一個java方法,這個計數器記錄的是正在執行的虛擬機字節碼指令的地址;若是正在執行的是Native方法,這個計數器值則爲空(Undenifined)。函數
此內存區域是惟一一個在java虛擬機規範中沒有規定任何OutOfMemoryError狀況的區域。性能
2.java虛擬機棧線程
也是線程私有的,生命週期與線程相同。描述的是java方法執行的內存模型:無法方法在執行的同時都會建立一個棧幀(Stack Frame)用於存儲局部變量、操做數棧、動態連接、方法出口等信息。每一個方法從調用直至執行完成的過程,就對應着一個棧幀在虛擬機棧中入站到出站的過程。設計
java虛擬機規範中,對這個區域規定了兩種異常情況:若是線程請求的棧深度大於虛擬機所容許的深度,將拋出StackOverflowError異常;若是虛擬機展能夠動態擴展(當前大部分的java虛擬機均可動態擴展,只不過java虛擬機規範中也容許固定長度的虛擬機棧),若是擴展是沒法申請到足夠的內存,就會拋出OutOfMemoryError異常。對象
3.本地方法棧接口
爲虛擬機使用到的Native方法服務。有的虛擬機(譬如Sun HotSpot虛擬機)直接就把本地方法棧和虛擬機棧合二爲一。生命週期
與虛擬機棧同樣,本地方法棧也會拋出StackOverflowError和OutOfMemoryError異常。進程
4.java堆內存
根據java虛擬機規範的規定,java堆能夠處於物理上不連續的內存空間中,只要邏輯上是連續的便可。首先是,既能夠固定大小也能夠是可擴展的,當前主流虛擬機都是可擴展的。
若是堆中沒有內存完成實例分配,而且堆也沒法在擴展時,將會拋出OutOfMemoryError異常。
①方法區
用於存儲已被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯後的代碼等數據。雖然java虛擬機規範把方法區描述爲對的一個邏輯部分,可是它卻有一個別名叫作Non-Heap(非堆),目的應該是與java堆區區分開來。
對於習慣在HotSpot虛擬機上開發、部署程序的開發者來講,不少人都更願意把方法區稱爲「永久代」(Permanent Generation),本質上二者並不等價,僅僅是由於HotSpot虛擬機的設計團隊選擇吧GC分代收集擴展至方法區,或者說使用永久代來實現方法區而已,這樣HotSpot的垃圾收集器能夠像管理Java對同樣管理這部份內存,省去專門爲方法區編寫內存管理的代碼的工做。其餘虛擬機(BEA JRockit、IBM J9)來講是不存在永久代的概念的。
原則上, 如何實現方法區屬於虛擬機實現細節, 不受虛擬機規範約束, 但使用永久代來實現方法區, 如今看來並非一個好主意, 由於這樣更容易遇到內存溢出問題( 永久代有-XX: MaxPermSize的上限, J9和JRockit只要沒有觸碰到進程可用內存的上限, 例如32位系統中的4GB, 就不會出現問題) , 並且有極少數方法( 例如String.intern( ) ) 會因這個緣由致使不一樣虛擬機下有不一樣的表現。 所以, 對於HotSpot虛擬機, 根據官方發佈的路線圖信息, 如今也有放棄永久代並逐步改成採用Native Memory來實現方法區的規劃了[1], 在目前已經發布的JDK 1.7的HotSpot中, 已經把本來放在永久代的字符串常量池移出。
Java虛擬機規範對方法區的限制很是寬鬆, 除了和Java堆同樣不須要連續的內存和能夠選擇固定大小或者可擴展外, 還能夠選擇不實現垃圾收集。 相對而言, 垃圾收集行爲在這個區域是比較少出現的, 但並不是數據進入了方法區就如永久代的名字同樣「 永久」 存在了。 這區域的內存回收目標主要是針對常量池的回收和對類型的卸載, 通常來講, 這個區域的回收「 成績」 比較難以使人滿意, 尤爲是類型的卸載, 條件至關苛刻, 可是這部分區域的回收確實是必要的。 在Sun公司的BUG列表中, 曾出現過的若干個嚴重的BUG就是因爲低版本的HotSpot虛擬機對此區域未徹底回收而致使內存泄漏。根據Java虛擬機規範的規定, 當方法區沒法知足內存分配需求時, 將拋出OutOfMemoryError異常。
②運行時常量池
是方法區的一部分。Class文件中除了有類的版本、字段、方法、接口等描述信息外,還有一項信息是常量池(Constant Pool Table),用於存放編譯期生成的各類字面量和符號引用,這部份內容將在類加載後進入方法去的運行時常量池中存放。
運行時常量池相對於CLass文件常量池的另一個重要特徵是具有動態性,Java語言並不要求常量必定只有編譯期才能產生,也就是並不是預置入CLass文件中常量池的內容才能進入方法區運行時常量池,運行期間也可能將新的常量放入池中,這種特性被開發人員利用比較多的就是String類的intern()方法。
既然運行時常量池是方法區的一部分,天然受到方法區內存的限制,當常量池沒法再申請到內存是會拋出OutOfMemoryError異常。
③直接內存
不是虛擬機運行時數據區的一部分,也不是java虛擬機規範中定義的內存區域。
jdk1.4中新加入了NIO(New Input/Output)類,引入了一種基於通道(Channel)與緩衝區(Buffer)的I/O方式,它可使用Native函數庫直接分配堆外內存,而後經過一個存儲在java堆中的DirectByteBuffer對象做爲這塊內存的引用進行操做。因避免了在java堆和native堆來回複製數據,顯著提升了性能。
動態擴展時,各內存區域總和大於物理內存限制會出現OutOfMemoryError異常。