方法區(qū)(Method Area)與Java堆一樣,是各個(gè)線程共享的內(nèi)存區(qū)域,它用于存儲(chǔ)已被虛擬機(jī)加載的類信息促煮、常量、靜態(tài)變量供置、即時(shí)編譯器編譯后的代碼等數(shù)據(jù)韩玩。雖然Java虛擬機(jī)規(guī)范把方法區(qū)描述為堆的一個(gè)邏輯部分人灼,但是它卻有一個(gè)別名叫做Non-Heap(非堆),目的應(yīng)該是與Java堆區(qū)分開來绳匀。
程序計(jì)數(shù)器(Program Counter Register)是一塊較小的內(nèi)存空間,它可以看作是當(dāng)前線程所執(zhí)行的字節(jié)碼的行號(hào)指示器。在虛擬機(jī)的概念模型里(僅是概念模型疾棵,各種虛擬機(jī)可能會(huì)通過一些更高效的方式去實(shí)現(xiàn))盗飒,字節(jié)碼解釋器工作時(shí)就是通過改變這個(gè)計(jì)數(shù)器的值來選取下一條需要執(zhí)行的字節(jié)碼指令,分支陋桂、循環(huán)逆趣、跳轉(zhuǎn)、異常處理嗜历、線程恢復(fù)等基礎(chǔ)功能都需要依賴這個(gè)計(jì)數(shù)器來完成宣渗。
Java虛擬機(jī)棧(Java Virtual Machine Stacks)也是線程私有的,它的生命周期與線程相同梨州。虛擬機(jī)棧描述的是Java方法執(zhí)行的內(nèi)存模型:每個(gè)方法在執(zhí)行的同時(shí)都會(huì)創(chuàng)建一個(gè)棧幀(Stack Frame )用于存儲(chǔ)局部變量表痕囱、操作數(shù)棧、動(dòng)態(tài)鏈接暴匠、方法出口等信息鞍恢。每一個(gè)方法從調(diào)用直至執(zhí)行完成的過程,就對(duì)應(yīng)著一個(gè)棧幀在虛擬機(jī)棧中入棧到出棧的過程每窖。(局部變量表:局部變量表存放了編譯期可知的各種基本數(shù)據(jù)類型(boolean帮掉、byte、char窒典、short蟆炊、int、float瀑志、long涩搓、double)、對(duì)象引用(reference類型劈猪,它不等同于對(duì)象本身昧甘,可能是一個(gè)指向?qū)ο笃鹗嫉刂返囊弥羔槪部赡苁侵赶蛞粋€(gè)代表對(duì)象的句柄或其他與此對(duì)象相關(guān)的位置)和returnAddress類型(指向了一條字節(jié)碼指令的地址)战得。)
本地方法棧(Native Method Stack)與虛擬機(jī)棧所發(fā)揮的作用是非常相似的充边,它們之間的區(qū)別不過是虛擬機(jī)棧為虛擬機(jī)執(zhí)行Java方法(也就是字節(jié)碼)服務(wù),而本地方法棧則為虛擬機(jī)使用到的Native方法服務(wù)贡避。在虛擬機(jī)規(guī)范中對(duì)本地方法棧中方法使用的語言痛黎、使用方式與數(shù)據(jù)結(jié)構(gòu)并沒有強(qiáng)制規(guī)定,因此具體的虛擬機(jī)可以自由實(shí)現(xiàn)它刮吧。甚至有的虛擬機(jī)(譬如Sun HotSpot虛擬機(jī))直接就把本地方法棧和虛擬機(jī)棧合二為一湖饱。與虛擬機(jī)棧一樣,本地方法棧區(qū)域也會(huì)拋出StackOverflowError和OutOfMemoryError異常杀捻。(native method含義: 一個(gè)Native Method就是一個(gè)java調(diào)用非java代碼的接口井厌。一個(gè)Native Method是這樣一個(gè)java的方法:該方法的實(shí)現(xiàn)由非java語言實(shí)現(xiàn),比如C。這個(gè)特征并非java所特有仅仆,很多其它的編程語言都有這一機(jī)制器赞,比如在C++中,你可以用extern "C"告知C++編譯器去調(diào)用一個(gè)C的函數(shù)墓拜。)
Java堆(Java Heap)是Java虛擬機(jī)所管理的內(nèi)存中最大的一塊港柜。Java堆是被所有線程共享的一塊內(nèi)存區(qū)域,在虛擬機(jī)啟動(dòng)時(shí)創(chuàng)建咳榜。此內(nèi)存區(qū)域的唯一目的就是存放對(duì)象實(shí)例夏醉,幾乎所有的對(duì)象實(shí)例都在
這里分配內(nèi)存。
2.垃圾收集算法
1.標(biāo)記-清除算法
最基礎(chǔ)的收集算法是“標(biāo)記-清除”(Mark-Sweep)算法涌韩,如同它的名字一樣畔柔,算法分為“標(biāo)記”和“清
除”兩個(gè)階段:首先標(biāo)記出所有需要回收的對(duì)象臣樱,在標(biāo)記完成后統(tǒng)一回收所有被標(biāo)記的對(duì)象靶擦,它的標(biāo)記過程其實(shí)在前一節(jié)講述對(duì)象標(biāo)記判定時(shí)已經(jīng)介紹過了雇毫。之所以說它是最基礎(chǔ)的收集算法,是因?yàn)楹罄m(xù)的收集算法都是基于這種思路并對(duì)其不足進(jìn)行改進(jìn)而得到的嘴拢。它的主要不足有兩個(gè):一個(gè)是效率問題桩盲,標(biāo)記和清除兩個(gè)過程的效率都不高;另一個(gè)是空間問題,標(biāo)記清除之后會(huì)產(chǎn)生大量不連續(xù)的內(nèi)存碎片捞蛋,空間碎片太多可能會(huì)導(dǎo)致以后在程序運(yùn)行過程中需要分配較大對(duì)象時(shí),無法找到足夠的連續(xù)內(nèi)存而不得不提前觸發(fā)另一次垃圾收集動(dòng)作拟杉。標(biāo)記—清除算法的執(zhí)行過程如圖
2.復(fù)制算法
為了解決效率問題庄涡,一種稱為“復(fù)制”(Copying)的收集算法出現(xiàn)了,它將可用內(nèi)存按容量劃分為大小相等的兩塊搬设,每次只使用其中的一塊。當(dāng)這一塊的內(nèi)存用完了拿穴,就將還存活著的對(duì)象復(fù)制到另外一塊上面,然后再把已使用過的內(nèi)存空間一次清理掉默色。這樣使得每次都是對(duì)整個(gè)半?yún)^(qū)進(jìn)行內(nèi)存回收,內(nèi)存分配時(shí)也就不用考慮內(nèi)存碎片等復(fù)雜情況呕诉,只要移動(dòng)堆頂指針,按順序分配內(nèi)存即可甩挫,實(shí)現(xiàn)簡(jiǎn)單,運(yùn)行高效伊者。只是這種算法的代價(jià)是將內(nèi)存縮小為了原來的一半,未免太高了一點(diǎn)删壮。復(fù)制算法的執(zhí)行過程如圖
3.標(biāo)記-整理算法
復(fù)制收集算法在對(duì)象存活率較高時(shí)就要進(jìn)行較多的復(fù)制操作,效率將會(huì)變低税灌。更關(guān)鍵的是亿虽,如果不想浪費(fèi)50%的空間,就需要有額外的空間進(jìn)行分配擔(dān)保洛勉,以應(yīng)對(duì)被使用的內(nèi)存中所有對(duì)象都100%存活的極端情況,所以在老年代一般不能直接選用這種算法攻走。根據(jù)老年代的特點(diǎn)此再,有人提出了另外一種“標(biāo)記-整理”(Mark-Compact)算法,標(biāo)記過程仍然與“標(biāo)記-清除”算法一樣输拇,但后續(xù)步驟不是直接對(duì)可回收對(duì)象進(jìn)行清理,而是讓所有存活的對(duì)象都向一端移動(dòng)逛裤,然后直接清理掉端邊界以外的內(nèi)存猴抹,“標(biāo)記-整理”算法的示意圖如圖
4.分代收集算法
當(dāng)前商業(yè)虛擬機(jī)的垃圾收集都采用“分代收集”(Generational Collection)算法,這種算法并沒有什么新
的思想洽糟,只是根據(jù)對(duì)象存活周期的不同將內(nèi)存劃分為幾塊炉菲。一般是把Java堆分為新生代和老年代,這樣就可以根據(jù)各個(gè)年代的特點(diǎn)采用最適當(dāng)?shù)氖占惴ㄖ龆T谛律徐艚龋看卫占瘯r(shí)都發(fā)現(xiàn)有大批對(duì)象死去,只有少量存活道偷,那就選用復(fù)制算法,只需要付出少量存活對(duì)象的復(fù)制成本就可以完成收集并巍。而老年代中因?yàn)閷?duì)象存活率高换途、沒有額外空間對(duì)它進(jìn)行分配擔(dān)保,就必須使用“標(biāo)記—清理”或者“標(biāo)記—整理”算法來進(jìn)行回收军拟。
Java堆用于存儲(chǔ)對(duì)象實(shí)例,只要不斷地創(chuàng)建對(duì)象肾档,并且保證GC Roots到對(duì)象之間有可達(dá)路徑來避免垃圾回收機(jī)制清除這些對(duì)象辫继,那么在對(duì)象數(shù)量到達(dá)最大堆的容量限制后就會(huì)產(chǎn)生內(nèi)存溢出異常。
public class HeapTest {
static class OOMObject {
}
public static void main(String[] args) {
List<OOMObject> list = new ArrayList<OOMObject>();
while (true) {
list.add( new OOMObject());
}
}
}
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Unknown Source)
at java.util.Arrays.copyOf(Unknown Source)
at java.util.ArrayList.ensureCapacity(Unknown Source)
at java.util.ArrayList.add(Unknown Source)
at org.myorg.algorithm.HeapTest.main( HeapTest.java:13)
Permgen Space:permenant genaration space 永久代區(qū)域
判斷對(duì)象是否存活的算法是這樣的:給對(duì)象中添加一個(gè)引用計(jì)數(shù)器速种,每當(dāng)有一個(gè)地方引用它時(shí)低千,計(jì)數(shù)器值就加1馏颂;當(dāng)引用失效時(shí),計(jì)數(shù)器值就減1救拉;任何時(shí)刻計(jì)數(shù)器為0的對(duì)象就是不可能再被使用的。