重溫深入理解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

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空間的一般,年齡大于或等于該年齡的對象直接進入老年代。