1.概要
在《深刻理解Java虛擬機》學習小記一之自動內存管理機制(一)中,咱們羅列了如下幾個問題: linux
- 什麼操做可能致使內存溢出?
- 有哪些種類的內存溢出?
- 都是在內存的哪些區域溢出?
- 垃圾收集有哪些原則?
- 有哪些垃圾收集算法及其實現?
- 新生代和老年代的回收策略如何?
- 各類內存相關的JVM參數是什麼意思?
本文主要總結問題四、問題5和問題7.整體以下:
2.判斷對象是否存活的算法
經常使用的判斷對象是否存活的算法有兩種:一種是引用計數算法,另一種是根搜索算法。例如Python語言就是使用了引用計數算法進行內存管理的。Java、C#和古老的Lisp等語言使用了根搜索算法判斷對象是否存活。下面就算法的思想上介紹這兩種算法。
程序員
2.1引用計數算法
引用計數算法最主要的步驟以下描述: 算法
- 給對象中添加一個引用計數器,每當一個地方引用它時,計數器值就加1
- 當引用失效時,計數器值就減1
- 當計數器的值爲0時,就判斷該對象不能被使用
這個算法實現簡單,可是很難解決對象之間循環引用的問題,所以Java並無使用這種算法來管理內存。
2.2根搜索算法
根搜索算法是經過一系列的名爲「GC Roots」的對象做爲起始點,從這些節點開始向下搜索,搜索所走過的路徑稱爲引用鏈,當一個對象到GC Roots沒有任何引用鏈相連時,則證實此對象是不可用的。
3.GC算法
3.1標記-清除(Mark-Sweep)算法
算法分爲「標記」和「清除」兩個階段:首先標記出全部須要回收的對象,在標記完成後統一回收掉全部被標記的對象。算法示意圖以下:
該算法是最基礎的收集算法,後續的蒐集算法都是基於這種思路並對其缺點進行改進獲得的。
3.2複製(Copying)算法
爲了解決效率問題,複製算法將可用內存按容量劃分爲大小相等的兩塊,每次只使用其中的一快。當這塊的內存用完了,就講還存活着的對象複製到另一塊上面,而後再把已使用過的內存空間一次清理掉。算法示意圖以下:
如今的商業虛擬機都採用這種收集算法來回收新生代。
3.3標記-整理(Mark-Compact)算法
複製收集算法在對象存活率較高時就要執行較多的複製操做,效率將會很低。所以在老年代通常不能直接使用這種算法。
爲了解決這個問題,標記-整理算法爲此誕生。該算法標記過程仍然與「標記-清除」算法同樣,但後續步驟不是直接對可回收對象進行清理,而是讓全部存活的對象都向一端移動,而後直接清理掉端邊界之外的內存,算法示意圖以下:

3.4分代收集算法
當前商業虛擬機的垃圾收集都採用分代收集算法,該算法只是根據對象的存活週期不一樣將內存劃分爲幾塊。通常將Java堆劃分爲新生代和老年代。新生代通常採用複製算法,老年代通常採用標記-清除算法或標記-整理算法。
3.5幾種算法的優缺點

4.垃圾收集器
關於垃圾收集器的知識在這裏不作介紹,有興趣的能夠參考http://book.51cto.com/art/201107/278913.htm 編程
5.垃圾收集器的參數總結

6.關於GC的幾點補充
通過上述的說明,能夠發現垃圾回收有如下的幾個特色:
1.垃圾收集發生的不可預知性:因爲實現了不一樣的垃圾收集算法和採用了不一樣的收集機制,因此它有多是定時發生,有多是當出現系統空閒CPU資源時發生,也有多是和原始的垃圾收集同樣,等到內存消耗出現極限時發生,這與垃圾收集器的選擇和具體的設置都有關係。
2.垃圾收集的精確性:主要包括2 個方面:
a.垃圾收集器可以精確標記活着的對象;
b.垃圾收集器可以精確地定位對象之間的引用關係。
前者是徹底地回收全部廢棄對象的前提,不然就可能形成內存泄漏。然後者則是實現歸併和複製等算法的必要條件。全部不可達對象都可以可靠地獲得回收,全部對象都可以從新分配,容許對象的複製和對象內存的縮並,這樣就有效地防止內存的支離破碎。 多線程
3.如今有許多種不一樣的垃圾收集器,每種有其算法且其表現各異,既有當垃圾收集開始時就中止應用程序的運行,又有當垃圾收集開始時也容許應用程序的線程運行,還有在同一時間垃圾收集多線程運行。
4.垃圾收集的實現和具體的JVM 以及JVM的內存模型有很是緊密的關係。不一樣的JVM 可能採用不一樣的垃圾收集,而JVM 的內存模型決定着該JVM能夠採用哪些類型垃圾收集。如今,HotSpot 系列JVM中的內存系統都採用先進的面向對象的框架設計,這使得該系列JVM均可以採用最早進的垃圾收集。
5.隨着技術的發展,現代垃圾收集技術提供許多可選的垃圾收集器,並且在配置每種收集器的時候又能夠設置不一樣的參數,這就使得根據不一樣的應用環境得到最優的應用性能成爲可能。
針對以上特色,咱們在使用的時候要注意:
1.不要試圖去假定垃圾收集發生的時間,這一切都是未知的。好比,方法中的一個臨時對象在方法調用完畢後就變成了無用對象,這個時候它的內存就能夠被釋放。
2.Java中提供了一些和垃圾收集打交道的類,並且提供了一種強行執行垃圾收集的方法--調用System.gc(),但這一樣是個不肯定的方法。Java 中並不保證每次調用該方法就必定可以啓動垃圾收集,它只不過會向JVM發出這樣一個申請,究竟是否真正執行垃圾收集,一切都是個未知數。
3.挑選適合本身的垃圾收集器。通常來講,若是系統沒有特殊和苛刻的性能要求,能夠採用JVM的缺省選項。不然能夠考慮使用有針對性的垃圾收集器,好比增量收集器就比較適合實時性要求較高的系統之中。系統具備較高的配置,有比較多的閒置資源,能夠考慮使用並行標記/清除收集器。
4.關鍵的也是難把握的問題是內存泄漏。良好的編程習慣和嚴謹的編程態度永遠是最重要的,不要讓本身的一個小錯誤致使內存出現大漏洞。
5.儘早釋放無用對象的引用。大多數程序員在使用臨時變量的時候,都是讓引用變量在退出活動域(scope)後,自動設置爲null,暗示垃圾收集器來收集該對象,還必須注意該引用的對象是否被監聽,若是有,則要去掉監聽器,而後再賦空值。
--------------------------------------------------全文完---------------------------------------
摘至
《深刻理解Java虛擬機》