死磕深入理解java虛擬機2--第三章筆記

重溫深入理解java虛擬機這本書,溫故而知新。

書是基于java虛擬機規(guī)范而來,文章中會摻雜我的個人理解的描述,如有誤,請指正。

?1、對象收集相關(guān)算法

(1)引用計數(shù)算法(虛擬機沒有采用此種算法,無法解決循環(huán)依賴的問題)

?(2)可達性分析算法

當(dāng)一個對象到GC Roots沒有任何引用鏈,則為可回收的對象

GC Roots的對象包括:虛擬機棧中引用的對象、方法區(qū)中靜態(tài)屬性應(yīng)用的對象、方法去中常量引用的對象、本地方法棧中的JNI引用的對象。

(3)四種引用:強引用 軟引用 弱引用 虛引用

(4)方法區(qū)的回收

2、垃圾收集算法

(1)標(biāo)記-清除算法

標(biāo)記可回收對象,清理被標(biāo)記的對象,這兩步效率不高,會產(chǎn)生內(nèi)存碎片。

?老年代可以采用此種方式

(2)復(fù)制算法

將房子一分為二,一半放雜物,過一段時間后你發(fā)現(xiàn)放東西的一半滿了,于是你就把不用的雜物扔掉了,然后剩下的雜物搬到屋子的另一邊,搬的過程順手整理一下,這樣開始放雜物的一半就空出來,可以放更多的東西了。

因為1:1這種方式浪費空間,所以現(xiàn)在的虛擬機采用Eden空間和兩塊較小的Survivor空間,HotSpot默認(rèn)Eden和Survivor比例為8:1,當(dāng)三塊地都不夠用的時候,老年代就可以用上了。

新生代采用此種方式

(3)標(biāo)記-整理算法

當(dāng)對象存活率較高時候,會出現(xiàn)較多的復(fù)制操作,效率變低,而且會浪費額外的空間,基于以上問題,老年代可以采用標(biāo)記可回收對象,移動存活的對象向一邊移動,清理邊界之外的內(nèi)存的方式。與(1)方式不同點在于不直接清理,而是移動存活對象再進行清理。

基于以上算法介紹,虛擬機采用了分代收集的算法應(yīng)對不同代的回收,原因是新生代中,每次收集會有大批量的對象死去,少量存活,復(fù)制算法效率高一些。老年代相反,沒有額外的空間進行分配擔(dān)保,必須使用(1)和(3)兩種算法進行。

3、HotSpot的算法實現(xiàn)

(1)虛擬機發(fā)起內(nèi)存回收的過程

A、枚舉根節(jié)點

可達性分析中從GCRoots節(jié)點找引用鏈,如果逐個檢查會消耗非常多的時間,并且在進行分析的時間段中引用關(guān)系不能變化,所以會導(dǎo)致stop the world?;谝陨显?,hotspot采用了一組OopMap的數(shù)據(jù)結(jié)構(gòu)存放引用。

B、安全點(Safepoint)

關(guān)于安全點,我也沒有完全理解透徹,附上一篇文章說明下:http://www.itdecent.cn/p/c79c5e02ebe6

為了解決OopMap內(nèi)容變化的指令非常多,會產(chǎn)生大量的額外空間,所以只在特定的位置記錄信息

C、安全區(qū)域

當(dāng)線程處于Sleep階段或者Blocked狀態(tài),這樣就不能進入安全點,這時可以通過標(biāo)定一塊安全區(qū)域,安全區(qū)域內(nèi)的代碼不會改變引用關(guān)系,可以放心的進行GC,離開安全區(qū)域時,要進行Safepoint的檢查

(2)垃圾收集器(即具體怎么回收的)

http://www.cnblogs.com/redcreen/archive/2011/05/04/2037057.html

HotSpot虛擬機的垃圾收集器關(guān)系圖

A、Serial收集器(新生代收集器,復(fù)制算法)

垃圾收集時必須暫停其他所有的工作線程,直到收集結(jié)束,沒有線程切換的消耗,在client模式下仍然是默認(rèn)的垃圾收集器的實現(xiàn)??梢耘cCMS配合使用。

設(shè)置參數(shù):?

? ? ? ? "-XX:+UseSerialGC":添加該參數(shù)來顯式的使用串行垃圾收集器;

B、ParNew收集器(新生代,多線程版本)

可以與CMS配合使用。

設(shè)置參數(shù):

??????"-XX:+UseConcMarkSweepGC":指定使用CMS后,會默認(rèn)使用ParNew作為新生代收集器;

??????"-XX:+UseParNewGC":強制指定使用ParNew;????

??????"-XX:ParallelGCThreads":指定垃圾收集的線程數(shù)量,ParNew默認(rèn)開啟的收集線程與CPU的數(shù)量相同;

C、Parallel Scavenge 收集器(新生代收集器,關(guān)注可控的吞吐量)

吞吐量 = 運行用戶代碼時間/(運行用戶代碼時間+垃圾收集時間),例如總共運行100分鐘,垃圾收集花掉1分鐘,吞吐量為99%。

設(shè)置參數(shù):

"-XX:MaxGCPauseMillis":控制最大垃圾收集停頓時間

"-XX:GCTimeRatio":直接設(shè)置吞吐量

"-XX:+UseAdaptiveSizePolicy":開關(guān)參數(shù),打開之后可以自動調(diào)節(jié)堆內(nèi)存的代分配比例達到最優(yōu),稱為GC的自適應(yīng)的調(diào)節(jié)策略,也是與ParNew收集器的一個重要區(qū)別。

D、Serial Old收集器(老年代收集器,標(biāo)記-整理算法)

單線程的,在1.5之前跟Parallel?Scavenge配合使用,在CMS發(fā)生Concurrent Mode failure時使用。

E、Parallel Old收集器(老年代收集器,標(biāo)記-整理算法)

與Parallel?Scavenge配合的老年代收集器,1.6版本之后可用。

注重吞吐量和cpu資源敏感的場合使用。

F、CMS收集器(老年代收集器,標(biāo)記-清除算法)

G、G1收集器

4、內(nèi)存分配與回收策略

(1)對象優(yōu)先在Eden區(qū)分配??

例子:-Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8

public static void main(String[] args){ byte[] a1,a2,a3,a4; int _1MB = 1048576; a1 = new byte[2 * _1MB]; a2 = new byte[2 * _1MB]; a3 = new byte[2 * _1MB]; a4 = new byte[5 * _1MB];}

發(fā)生一次MinorGC

(2)大對象直接進入老年代

例子:-XX:PretenureSizeThreshold=3145728? (不能直接寫3MB)

(3)長期存活的對象將進入老年代

虛擬機給每個對象定義一個年齡(age)計數(shù)器,如果在Eden出生并經(jīng)過MinorGC仍然存活且能被Survivor容納的話,將被移動到Survivor中,年齡設(shè)為1,在Survivor每熬過一次MinorGC,年齡增加一歲,當(dāng)?shù)揭欢挲g(-XX:MaxTenuringThreshold? 設(shè)置此年齡),就可以晉升老年代。

(4)動態(tài)對象年齡判定

提前進入老年代的情況:如果Survivor中相同年齡的所有對象大小總和大于Survivor空間的一般,年齡大于或等于該年齡的對象直接進入老年代。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容