Java虛擬機在執行Java程序的過程當中會把它所管理的內存劃分爲若干個不一樣的數據區域。 算法
程序計數器是一塊較小的內存空間,它能夠看作是當前線程所執行的字節碼的行號指示器。在虛擬機的概念模型裏,字節碼解釋器工做時就是經過改變這個計數器的值來選取下一條須要執行的字節碼指令。多線程
因爲Java虛擬機的多線程是經過線程輪流切換並分配處理器執行時間的方式來實現的,在任何一個肯定的時刻,一個處理器都只會執行一條線程中的指令。所以爲了線程切換後能恢復到正確的執行未知,每條線程都須要有一個獨立的程序計數器,各條線程之間計數器互不影響,獨立存儲,咱們稱這類內存區域爲「線程私有」的內存。函數
執行Java方法的時候,計數器記錄的是正在執行的虛擬機字節碼指令的地址;執行Native方法的時候,計數器值爲空。此內存區域是惟一一個在Java虛擬機規範中沒有規定任何OOM狀況的區域。優化
虛擬機棧是線程私有的,它的生命週期與線程相同。虛擬機棧描述的是Java方法執行的內存模型:每一個方法在執行的同時都會建立一個棧幀用於存儲局部變量表、操做數棧、動態連接、方法出口等信息。每個方法從調用直至執行完成的過程,就對應着一個棧幀在虛擬機棧中入棧到出棧的過程。線程
局部變量表存放了編譯器可知的各類基本數據類型(boolean、byte、char、short、int、float、long、double)、對象引用(reference類型,它不等同與對象自己,多是一個對象對象起始位置的引用指針,也多是指向一個表明對象的句柄或其餘與此對象相關的位置)和returnAddress類型(指向了一條字節碼指令的地址)。設計
其中64位長度的long和double類型的數據會佔用兩個局部變量空間(Slot),其他的數據類型只佔用1個。局部變量表所需的內存空間在編譯期間完成分配,當進入一個方法時,這個方法須要在幀中分配多大的局部變量空間是徹底肯定的,在方法運行期間不會改變局部變量表的大小。指針
若是線程請求的棧深度大於虛擬機所容許的深度,將會拋出StackOverFlow異常;若是虛擬機棧能夠動態擴展,若是擴展時沒法申請到足夠的內存,就會拋出OOM異常。cdn
本地方法棧與虛擬機棧所發揮的做用是很是類似的,它們之間的區別不過是虛擬機棧爲虛擬機執行Java方法服務,而本地方法棧則爲虛擬機使用到的Native方法服務。與虛擬機棧區域同樣,本地方法也會拋出StackOverFlow異常和OOM異常。對象
Java堆是Java虛擬機所管理內存中最大的一塊,Java堆是被全部線程共享的一塊內存區域,在虛擬機啓動時建立。幾乎全部的對象實例都在這裏分配內存,可是隨着JIT編譯器的發展與逃逸分析技術逐漸成熟,棧上分配、標量替換優化技術將會有些改變。blog
Java堆是垃圾收集器管理的主要區域。從內存回收的角度來看,因爲如今收集器基本都採用分帶收集算法,因此Java堆中還能夠細分爲:新生代和老年代;新生代又能夠細分爲Eden空間、S0空間、S1空間。從內存分配的角度來看,線程共享的Java堆中可能劃分出多個線程私有的分配緩衝區(Thread Local Allocation Buffer,TLAB)。
Java堆經過-Xms和—Xms來控制大小。若是堆中沒有內存完成實例分配,而且堆也沒法在擴展時,將會拋出OOM異常。
方法區與Java堆同樣,是各個線程共享的內存區域,它用於存儲已被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯後的代碼等數據。
對於習慣在HotSpot虛擬機上開發、部署程序的開發者來講,不少人都更願意把方法區成爲永久代,本質上二者並不相等,僅僅是由於HotSpot虛擬機的設計團隊把GC分代收集擴展至方法區,或者說使用永久代來實現方法區而已,這樣HotSpot的垃圾收集器能夠像管理Java堆同樣管理這部份內存。JDK1.8中已經取消了永久代,改成元空間。
相對而言,垃圾收集行爲在這個區域是比較少出現的,但數據並不是用久存在了,這個區域的內存回收目標主要是針對常量池的回收和類型的卸載。
當方法區沒法知足內存分配需求時,將拋出OOM異常。
運行時常量池是方法區的一部分。Class文件中除了類的版本、字段、方法、接口等描述信息外,還有一項信息是常量池,用於存放編譯期間生成的各類字面量和符號引用,這部份內容將在類加載後進入方法區的運行時常量池中存放。
直接內存並非虛擬機運行時數據區的一部分,也不是Java虛擬機規範中定義的內存區域。但這部份內存也被頻繁的使用,也可能致使OOM異常。
在JDK1.4中新加入了NIO類,引入了一種基於通道與緩衝區的I/O方法,它可使用Native函數庫直接分配堆外內存,而後經過一個存儲在Java堆中的DirectByteBuffer對象做爲這塊內存的引用進行操做。