話很少說,幹就完了。算法
在以前咱們說到堆空間是對象實例存放的地方。程序會一致運行,對象也可能一直建立,可是堆的內存空間是有限的,那麼如何保證在程序運行過程當中,堆空間一直有足夠的內存來建立新的對象呢?垃圾回收,垃圾回收將已經不使用的對象進行回收,釋放內存空間,以便在分配新的對象時有足夠的內存空間來進行分配。性能
在對堆空間進行垃圾回收以前,首先就是要肯定哪些對象還"存活",哪些對象已經"死去"(也就是不回再被任何途徑所使用)。垃圾回收只會針對死去的對象。this
引用計數器算法就是在對象中添加一個引用計數器,每當有一個地方使用到該對象時,計數器值就加1,而當引用失效的時候,該計數器值就減1,只要當計數器的值爲0的時候,就表示該對象已經再也不被使用。spa
優勢線程
引用計數器算法實現簡單,並且效率高。3d
缺點指針
很難解決對象之間的相互循環引用問題。code
因爲其缺點,因此目前主流的虛擬機中都沒有選用引用計數器算法來管理內存。那麼什麼是對象的相互循環引用呢?經過下面代碼實例來進行說明cdn
public class Dog {
private Cat cat;
// 省略get/set方法
}
public class Cat {
private Dog dog;
// 省略get/set方法
}
public static void main(String[] args){
Dog dog = new Dog();
Cat cat = new Cat();
// 對象的相互循環引用
cat.dog = dog;
dog.cat = cat;
dog = null;
cat = null;
}
複製代碼
在上述代碼中,雖然dog,cat被置爲null,也就是再也不使用了,可是dog和cat之間存在相互引用,因此虛擬機並不會回收這兩個對象。對象
可達性算法的基本思想就是經過一系列被稱爲"GC Roots"的對象做爲起始點,由這些節點開始向下搜索,搜索所走過的路徑稱爲引用鏈(Reference Chain),當一個GC Roots對象沒有任何引用鏈相連時,則證實該對象是不可用的。
在Java中,可做爲GC Roots的對象主要包括瞭如下幾種:
須要說明的是,即便是在可達性算法中,不可達的對象,並不是就會被標記爲"非死不可"的對象。對於一個對象的死亡宣告,至少要經歷兩次標記的過程。
第一次標記
l對象通過可達性算法分析後,發現沒有與GC Roots項鍊的引用鏈時,它會被標記一次,同時會進行篩選,篩選的條件就是此對象有沒有必要執行finalize方法。若是該對象沒有覆蓋finalize方法或者說finalize方法已經被虛擬機執行過。那麼虛擬機會認爲這兩種狀況沒有必要執行finalize方法。
第二次標記
若是對象被斷定爲有必要執行finalize方法,那麼虛擬機會將對象防止在一個F-Queue隊列中。同時虛擬機會自動創建一個低優先級的Finalizer線程去執行它。須要注意的是,執行僅僅表示虛擬機會觸發這個對象的finalize方法,但並不會保證等待這個對象的運行結束。
finalize方法是對象逃脫死亡命運的最後一次機會,GC會對F-Queue隊列中的對象作第二次標記。對象在finalize方法中,若是從新創建與引用鏈上的任何一個對象關聯,例如將本身(this關鍵字)賦值給某個類變量或者對象的成員變量。那麼在第二次標記時,會被移出"即將回收"的集合。
最終流程如上圖所示。須要注意的是,對於任何一個對象的finalize方法,都只會被系統自動調用一次,若是對象已經調用過finalize方法以後,那麼它的finalize方法就不會被再次執行。在實際開發過程當中,應當避免調用對象的finalize方法。
常見的垃圾收集算法有四類:標記-清除算法、標記-整理算法、複製算法、分代算法。下面分別依次介紹每一種算法的思想。
標記-清除算法是垃圾收集算法中最基礎的算法。在以前說如何斷定一個對象是否存活的算法中,GC Roots會對對象進行標記,而標記清除算法,則是根據GC Roots的標記判斷該對象可回收,以下圖:
經過途中能夠很明顯的看到,標記清除算法會帶來一個問題,那就是內存碎片化。在說到堆空間的新生代時,也提到過內存的碎片化,其後果就是會影響到程序的性能。
標記整理算法的標記過程同標記-清除算法一致,可是後續過程存在差別,標記-整理算法在標記後不是立馬對可回收的對象進行回收,而是讓存活的對象都向一端移動,而後清除可用邊界之外的對象,釋放內存空間。
在上圖中能夠明顯的看到,與標記-清除算法不通的是,它並不會產生內存碎片,而是經過整理,使當前可用對象都會保存在一段連續的內存上。
在說到堆空間的新生代時,有說到新生對象從Eden區到Survivor區的流轉過程,而這個過程正好就是複製算法的實際體現。複製算法會在內存中劃分出兩塊大小相等的區域(假設爲A、B),每次只使用其中一塊,當A區域滿了的時候,會將存活的對象複製一份到B區域,同時清空A區域。只須要移動堆頂的指針,按照順序分配,也就不用考慮內存碎片的問題了。
在目前主流的商業虛擬機中,基本上採用的都是分代算法,分代算法實質上就是根據對象的存活週期不一樣劃分不一樣的區域。通常是把Java堆劃分紅新生代和老年代。而針對不一樣的代採起不一樣的收集算法。例如在新生代中,一般會採用複製算法,而在老年代中,由於對象的存活率較高,通常使用標記-整理或標記-清除算法。
本章中主要講解了垃圾回收的相關的算法。兩個方面,一是斷定對象是否存活的算法,二是垃圾回收算法,總結以下圖:
不怕路歹行不怕大雨淋,心上一字敢 面對個人夢,甘願來做憨人。 --<憨人>