一 程序計數器java
程序計數器是一塊較小的內存空間,能夠看作當前線程所執行的字節碼的行號指示器。在虛擬機的概念模型裏(僅僅是概念模型,各類虛擬機可能經過一些更加高效的方式實現),字節碼解釋器工做時就是經過改變這個計數器的值進行下一條須要執行的字節碼指令,分支、循環、跳轉、異常處理、線程恢復等基礎功能都須要依賴這個計數器來完成。各個線程之間的計數器互不影響,獨立存儲,爲線程私有。數組
若是線程正在執行的是一個java方法,這個計數器記錄的是正在執行虛擬機字節碼指令的地址,若是是native方法,此計數器則爲空(Undefined)。此內存區域是惟一一個在java虛擬機規範中沒有規定任何OutOfMemoryError狀況的區域。函數
二 虛擬機棧性能
虛擬機棧也是線程私有的,每一個方法執行的同時會建立一個棧幀用於存儲局部變量表、操做數棧(保存計算過程的中間結果)、動態連接、方法出口等信息。局部變量表存放着編譯器可知的各類基本數據類型(boolean、byte、char、short、int、float、long、double)、對象引用類型(因此取對象也是經過此引用獲取)。進入一個方法中時,須要在幀中分配多大的局部變量空間是徹底肯定的,在方法運行期間不會改變局部變量表大小。優化
若是線程請求的棧深度大於虛擬機容許的深度,將會拋出StackOverflowError異常;若是虛擬機棧能夠動態擴展,當擴展時沒法申請到足夠的內存,將會拋出OutOfMemoryError異常。我的感受這兩個異常只是表述的角度不同,其實是指代同一個東西。spa
三 本地方法棧.net
本地方法棧(Native Method Stack)與虛擬機棧所發揮的做用是十分類似的。惟一的區別是虛擬機棧爲虛擬機執行java方法(也就是字節碼)服務,而本地方法棧則爲虛擬機使用到的native方法服務。線程
四 堆對象
全部的對象以及數組都要在堆上分配,可是隨着JIT編譯器的發展和逃逸分析技術逐漸成熟,棧上分配、標量替換優化技術將會致使一些微妙的變化發生。blog
java堆還能夠細分爲:新生代和老年代,再細緻一點的有Eden空間、From Survivor空間、To Survivor空間等,同時線程共享的堆還能夠劃分多個線程私有的分配緩衝區。進一步劃分的目的是更好的進行內存分配和回收內存。
五 方法區
方法區主要用來存儲已被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯後的代碼等數據。須要注意的是"永久區"和方法區並不等價,僅僅是由於HotSpot虛擬機團隊把gc分代收集擴展至方法區,或者說用永久代來實現方法區而已,在目前的jdk1.7的HotSpot,已經把本來在永久代的字符串常量池移出。jdk1.8已經把永久代區移出,取而代之的是元數據區。可使用參數-XX:MaxMetaspaceSize指定。
六 運行時常量池
運行時常量池是方法區的一部分,用於存放編譯器生成的各類字面量和符號引用,這部份內容將在類加載後進入方法區的運行時常量池中存放。運行期間也能夠把新的常量放入池中,像String類的intern()方法即是其中一個應用。常量池的內存受到方法區內存的限制,沒法申請到內存時會拋出OutOfMemoryError異常
七 直接內存
直接內存(Direct Memory)並非虛擬機運行時的數據區的一部分,可是這部份內存也被頻繁使用,也可能致使OutOfMemoryError異常出現。好比jdk1.4引進的NIO類,引入了一種基於通道(Channel)與緩衝區(Buffer)的I/O方式,它可使用Native函數庫直接分配堆外內存,而後經過一個儲存在java堆中的DirectByteBuffer對象做爲這塊內存的引用進行操做。這樣能在一些場景中顯著提升性能,由於避免了java堆和native堆中來回複製數據。
配置虛擬機參數時,每每是根據內存配置-Xmx等參數而忘了直接內存,使得各個區域內存總和大於物理內存限制,從而致使動態擴展時出現OutOfMemoryError異常。