Java虛擬機在執(zhí)行Java程序的過程中會把他所管理的的內(nèi)存劃分為若干個不同的數(shù)據(jù)區(qū)域拌消。
這些區(qū)域都有各自的用途置蜀,以及創(chuàng)建和銷毀時間镣煮,有的區(qū)域隨著虛擬機進程的啟動而存在姐霍。
有些區(qū)域則依賴用戶線程的啟動和結(jié)束而建立和銷毀。
1典唇、程序計數(shù)器
程序計數(shù)器是一塊較小的內(nèi)存空間镊折,他可以看作是當(dāng)前線程的所執(zhí)行的字節(jié)碼的行號指示器。
由于Java虛擬機的多線程是通過線程輪流切換并分配處理器執(zhí)行時間的方式來實現(xiàn)的介衔,在任何一個確定的時刻恨胚,一個處理器(對于多核處理器來說是一個內(nèi)核)都只會執(zhí)行一條線程中的指令。因此為了線程切換后能夠恢復(fù)到正確的執(zhí)行位置炎咖,每條線程都需要有一個獨立的程序計數(shù)器赃泡。
如果線程正在執(zhí)行的是一個Java方法,這個計數(shù)器記錄的是正在執(zhí)行的虛擬機字節(jié)碼指令地址乘盼;如果正在執(zhí)行的是一個Native方法升熊,這個計數(shù)器的值為空。
此內(nèi)存區(qū)域是唯一 一個在Java虛擬機規(guī)范中沒有規(guī)定任何OutOfMemoryError情況的區(qū)域绸栅。
2级野、Java虛擬機棧
和程序計數(shù)器一樣,Java虛擬機棧也是線程私有的粹胯,它的生命周期與線程相同蓖柔。虛擬機棧描述的是Java方法執(zhí)行的內(nèi)存模:每個方法在執(zhí)行的同時都會創(chuàng)建一個棧幀用于存儲局部表量表辰企、操作數(shù)棧、動態(tài)鏈接况鸣、方法出口等信息蟆豫。每一個方法從調(diào)用到執(zhí)行完成的過程,就對應(yīng)著一個棧幀在虛擬機棧中入棧到出棧的過程懒闷。
局部變量表存放了編譯期可知的各種基本數(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)進入一個方法時甥郑,這個方法需要在棧幀中分配多大的局部變量空間是完全確定的,在方法運行期間不會改變局部變量表的大小荤西。
在Java虛擬機規(guī)范中對這個區(qū)域規(guī)定了兩種異常狀況:如果線程請求的棧深度大于虛擬機所允許的深度澜搅,將拋出StackOverflowError異常;如果虛擬機椥靶浚可以動態(tài)擴展勉躺,如果擴展時無法申請到足夠的內(nèi)存,就會拋出OutOfMemoryEooro異常觅丰。
3饵溅、本地方法棧
本地方法棧與虛擬機棧所發(fā)揮的作用非常相似,他們之間的區(qū)別不過是虛擬機棧為虛擬機執(zhí)行的Java方法(也就是字節(jié)碼)服務(wù)妇萄,為本地 方法棧則為虛擬機使用到的Native方法服務(wù)蜕企。與虛擬機棧一樣,本地方法棧區(qū)域也會拋出StackOverflowErrow和OutOfMemoryError一樣嚣伐。
4糖赔、Java堆
Java堆是Java虛擬機所管理的內(nèi)存中最大的一塊Java堆是被所有線程共享的一塊內(nèi)存區(qū)域,在虛擬機啟動時創(chuàng)建轩端。此內(nèi)存區(qū)域的唯一目的就是存放對象實例放典,幾乎所有的對象實例都在這里分配。這一點在Java虛擬機規(guī)范中的描述是:所有的對象實例以及數(shù)組都要在堆上分配。
Java堆是垃圾收集器管理的主要區(qū)域奋构,因此很多時候也被稱作"GC堆"壳影。從內(nèi)存回收的角度來看,由于現(xiàn)在收集器基本都采用分代手機算法所以Java堆中還可以細分為:新生代和老年代弥臼;在細致一點的有哦Eden空間宴咧、Form Survivor空間、To Survivor空間等径缅。
根據(jù)Java虛擬機規(guī)范的規(guī)定掺栅,Java堆可以處于物理上不連續(xù)的內(nèi)存空間中,只要邏輯上是連續(xù)即纳猪。如果在堆中沒有內(nèi)存完成實例分配氧卧,并且堆也無法在擴展時,將會拋出OutOfMemoryError異常氏堤。
5沙绝、方法區(qū)
方法區(qū)和Java堆一樣,是各個線程共享的內(nèi)存區(qū)域鼠锈,它用于存儲以被虛擬機加載的類信息闪檬、常量、靜態(tài)變量购笆、及時編譯期編譯后的代碼等數(shù)據(jù)粗悯。
根據(jù)Java虛擬機規(guī)范的規(guī)定,當(dāng)方法區(qū)無法滿足內(nèi)存反配需求是由桌,將拋出OutOfMemoryError異常为黎。
6、運行時常量池
運行時常量池是方法區(qū)的一部分行您。Class文件中除了有類的版本、字段剪廉、方法娃循、接口等描述信息外,還有一項信息就是常量池斗蒋,用于存放編譯期生成的各種字面量和符號引用捌斧,這部分內(nèi)容將在類加載后進入方法區(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異常。