Java虛擬機(jī)在執(zhí)行Java程序的過程中會把他所管理的的內(nèi)存劃分為若干個不同的數(shù)據(jù)區(qū)域。
這些區(qū)域都有各自的用途,以及創(chuàng)建和銷毀時間,有的區(qū)域隨著虛擬機(jī)進(jìn)程的啟動而存在。
有些區(qū)域則依賴用戶線程的啟動和結(jié)束而建立和銷毀。
1、程序計數(shù)器
程序計數(shù)器是一塊較小的內(nèi)存空間,他可以看作是當(dāng)前線程的所執(zhí)行的字節(jié)碼的行號指示器。
由于Java虛擬機(jī)的多線程是通過線程輪流切換并分配處理器執(zhí)行時間的方式來實現(xiàn)的,在任何一個確定的時刻,一個處理器(對于多核處理器來說是一個內(nèi)核)都只會執(zhí)行一條線程中的指令。因此為了線程切換后能夠恢復(fù)到正確的執(zhí)行位置,每條線程都需要有一個獨立的程序計數(shù)器。
如果線程正在執(zhí)行的是一個Java方法,這個計數(shù)器記錄的是正在執(zhí)行的虛擬機(jī)字節(jié)碼指令地址;如果正在執(zhí)行的是一個Native方法,這個計數(shù)器的值為空。
此內(nèi)存區(qū)域是唯一 一個在Java虛擬機(jī)規(guī)范中沒有規(guī)定任何OutOfMemoryError情況的區(qū)域。
2、Java虛擬機(jī)棧
和程序計數(shù)器一樣,Java虛擬機(jī)棧也是線程私有的,它的生命周期與線程相同。虛擬機(jī)棧描述的是Java方法執(zhí)行的內(nèi)存模:每個方法在執(zhí)行的同時都會創(chuàng)建一個棧幀用于存儲局部表量表、操作數(shù)棧、動態(tài)鏈接、方法出口等信息。每一個方法從調(diào)用到執(zhí)行完成的過程,就對應(yīng)著一個棧幀在虛擬機(jī)棧中入棧到出棧的過程。
局部變量表存放了編譯期可知的各種基本數(shù)據(jù)類型(boolean、byte、char、short、int、float、long、double)、對象引用和returnAddres類型(指向了一條字節(jié)碼指令地址)。
其中64位長度的long和double類型的數(shù)據(jù)會占用2個局部變量空間(Slot),其余的數(shù)據(jù)類型只占用1個。局部變量表所需的內(nèi)存空間在編譯期間完成分配,當(dāng)進(jìn)入一個方法時,這個方法需要在棧幀中分配多大的局部變量空間是完全確定的,在方法運行期間不會改變局部變量表的大小。
在Java虛擬機(jī)規(guī)范中對這個區(qū)域規(guī)定了兩種異常狀況:如果線程請求的棧深度大于虛擬機(jī)所允許的深度,將拋出StackOverflowError異常;如果虛擬機(jī)棧可以動態(tài)擴(kuò)展,如果擴(kuò)展時無法申請到足夠的內(nèi)存,就會拋出OutOfMemoryEooro異常。
3、本地方法棧
本地方法棧與虛擬機(jī)棧所發(fā)揮的作用非常相似,他們之間的區(qū)別不過是虛擬機(jī)棧為虛擬機(jī)執(zhí)行的Java方法(也就是字節(jié)碼)服務(wù),為本地 方法棧則為虛擬機(jī)使用到的Native方法服務(wù)。與虛擬機(jī)棧一樣,本地方法棧區(qū)域也會拋出StackOverflowErrow和OutOfMemoryError一樣。
4、Java堆
Java堆是Java虛擬機(jī)所管理的內(nèi)存中最大的一塊Java堆是被所有線程共享的一塊內(nèi)存區(qū)域,在虛擬機(jī)啟動時創(chuàng)建。此內(nèi)存區(qū)域的唯一目的就是存放對象實例,幾乎所有的對象實例都在這里分配。這一點在Java虛擬機(jī)規(guī)范中的描述是:所有的對象實例以及數(shù)組都要在堆上分配。
Java堆是垃圾收集器管理的主要區(qū)域,因此很多時候也被稱作"GC堆"。從內(nèi)存回收的角度來看,由于現(xiàn)在收集器基本都采用分代手機(jī)算法所以Java堆中還可以細(xì)分為:新生代和老年代;在細(xì)致一點的有哦Eden空間、Form Survivor空間、To Survivor空間等。
根據(jù)Java虛擬機(jī)規(guī)范的規(guī)定,Java堆可以處于物理上不連續(xù)的內(nèi)存空間中,只要邏輯上是連續(xù)即。如果在堆中沒有內(nèi)存完成實例分配,并且堆也無法在擴(kuò)展時,將會拋出OutOfMemoryError異常。
5、方法區(qū)
方法區(qū)和Java堆一樣,是各個線程共享的內(nèi)存區(qū)域,它用于存儲以被虛擬機(jī)加載的類信息、常量、靜態(tài)變量、及時編譯期編譯后的代碼等數(shù)據(jù)。
根據(jù)Java虛擬機(jī)規(guī)范的規(guī)定,當(dāng)方法區(qū)無法滿足內(nèi)存反配需求是,將拋出OutOfMemoryError異常。
6、運行時常量池
運行時常量池是方法區(qū)的一部分。Class文件中除了有類的版本、字段、方法、接口等描述信息外,還有一項信息就是常量池,用于存放編譯期生成的各種字面量和符號引用,這部分內(nèi)容將在類加載后進(jìn)入方法區(qū)的運行時常量池中存放。
量池的好處
常量池是為了避免頻繁的創(chuàng)建和銷毀對象而影響系統(tǒng)性能,其實現(xiàn)了對象的共享。
例如字符串常量池,在編譯階段就把所有的字符串文字放到一個常量池中。
(1)節(jié)省內(nèi)存空間:常量池中所有相同的字符串常量被合并,只占用一個空間。
(2)節(jié)省運行時間:比較字符串時,==比equals()快。對于兩個引用變量,只用==判斷引用是否相等,也就可以判斷實際值是否相等。
既然運行時常量池是方法區(qū)的一部分,自然受到方法區(qū)內(nèi)存的限制當(dāng)常量池?zé)o法在申請到內(nèi)存是會拋出OutOfMemoryError異常。