垃圾回收算法_垃圾回收算法有哪些
垃圾檢測通常通過建立一個根對象的集合以及建立一個從這些根對象開始能夠觸及的對象集合來實現(xiàn)。如果正在執(zhí)行的程序可以訪問到根對象和某個對象之間存在引用路徑,這個對象就是可觸及的。對于程序來說,根對象總是可以訪問的。從這些根對象開始,任何可以被觸及的對象都被認為是“活動”的對象。無法被觸及的對象被認為是垃圾。
虛擬機的根對象集合根據(jù)實現(xiàn)不同而不同,包含局部變量中的對象引用和棧幀的操作數(shù)棧(以及類變量中的對象引用)、被加載的類的常量池中的對象引用(比如字符串)、傳遞到本地方法中的沒有被本地方法釋放的對象引用。任何被根對象引用的對象都是可觸及的,從而是活動的,任何被活動的對象引用的對象都是可觸及的,程序可以訪問任何可觸及的對象,所以這些對象應(yīng)用留在堆中,而對于那些不可觸及的對象,程序沒有辦法訪問它們,應(yīng)該被收集和釋放。
區(qū)分活動對象和垃圾的兩個基本方法是引用計數(shù)和跟蹤。引用計數(shù)垃圾收集器通過為堆中的每個對象保存一個計數(shù)來區(qū)分活動對象和垃圾對象。這個計數(shù)記錄下這個對象的引用次數(shù)。跟蹤垃圾收集器實際上追蹤從根節(jié)點開始的引用圖。在追蹤中遇上的對象以某種方式打上標記,當(dāng)追蹤結(jié)束時,沒有被打上標記的對象就被判定是不可觸及的,可以被當(dāng)作垃圾收集。==引用計數(shù)算法==
給對象添加一個引用計數(shù)器,每當(dāng)一個地方引用它時,數(shù)據(jù)器加1;當(dāng)引用失效時,計數(shù)器減1;計數(shù)器為0的即可被回收。
- 優(yōu)點:實現(xiàn)簡單,判斷效率高
- 缺點:很難解決對象之間的相互循環(huán)引用(objA.instance = objB; objB.instance = objA)的問題,所以java語言并沒有選用引用計數(shù)法管理內(nèi)存
==根搜索算法==
Java和C#都是使用根搜索算法來判斷對象是否存活。通過一系列的名為“GC Root”的對象作為起始點,從這些節(jié)點開始向下搜索,搜索所有走過的路徑稱為引用鏈(Reference Chain),當(dāng)一個對象到GC Root沒有任何引用鏈相連時(用圖論來說就是GC Root到這個對象不可達時),證明該對象是可以被回收的。
在Java中這些對象可以成為GC Root:
- 虛擬機棧(棧幀中的本地變量表)中的引用對象
- 方法區(qū)中的類靜態(tài)屬性引用的對象
- 方法區(qū)中的常量引用對象
- 本地方法棧中JNI(即Native方法)的引用對象
==標記-清除算法==
標記-清除算法是一種常見的基礎(chǔ)垃圾收集算法,它將垃圾收集分為兩個階段
- 標記階段:標記出可以回收的對象。
- 清除階段:回收被標記的對象所占用的空間。
63707281.jpeg
標記-清除算法主要有兩個缺點,一個是標記和清除的效率不高,另一個從圖中就可以看出,就是容易產(chǎn)生大量不連續(xù)的內(nèi)存碎片,碎片太多可能會導(dǎo)致后續(xù)沒有足夠的連續(xù)內(nèi)存分配給較大的對象,從而提前觸發(fā)新的一次垃圾收集動作。
==復(fù)制算法==
為了解決標記-清除算法的效率不高的問題,產(chǎn)生了復(fù)制算法。它把內(nèi)存空間劃分為兩個相等的區(qū)域,每次只使用其中一個區(qū)域。在垃圾收集時,遍歷當(dāng)前使用的區(qū)域,把存活對象復(fù)制到另一個區(qū)域中,最后將當(dāng)前使用的區(qū)域的可回收的對象進行回收。
90984624.jpeg
這種算法每次都對整個半?yún)^(qū)進行內(nèi)存回收,不需要考慮內(nèi)存碎片的問題,代價就是使用內(nèi)存為原來的一半。復(fù)制算法的效率與存活對象的數(shù)目多少有很大的關(guān)系,如果存活對象很少,復(fù)制算法的效率就會很高。由于絕大多數(shù)對象的生命周期很短,并且這些生命周期很短的對象都存于新生代中,所以復(fù)制算法被廣泛應(yīng)用于新生代中。
==標記-壓縮算法==
在新生代中可以使用復(fù)制算法,但是在老年代就不能選擇復(fù)制算法,因為老年代對象存活率會較高,這樣會有較多的復(fù)制操作,導(dǎo)致效率變低。標記-清除算法可以應(yīng)用在老年代中,但是效率不高,在內(nèi)存回收后容易產(chǎn)生大量內(nèi)存碎片。因此就出現(xiàn)了一種標記-壓縮算法,與標記-清除算法不同的是,在標記可回收的對象后將所有存活的對象壓縮到內(nèi)存的一端,使它們緊湊地排列在一起,然后對邊界以外的內(nèi)存進行回收,回收后,已用和未用的內(nèi)存都各自一邊。
94057049.jpeg
標記-壓縮算法解決了標記-清除算法效率低和容易產(chǎn)生大量內(nèi)存碎片的問題,它被廣泛應(yīng)用于老年代中。
==分代收集算法==
分代收集算法會結(jié)合不同的收集算法來處理不同的空間,因此在學(xué)習(xí)分代收集算法之前我們首先要了解Java堆區(qū)的空間劃分。Java堆區(qū)的空間劃分在Java虛擬機中,各種對象的生命周期會有著較大的差別,大部分對象生命周期很短暫,少部分對象生命周期很長,有的甚至與應(yīng)用程序以及Java虛擬機的運行周期一樣長。因此,應(yīng)該對不同生命周期的對象采取不同的收集策略,根據(jù)生命周期長短將它們放到不同的區(qū)域,并在不同的區(qū)域采用不同的收集算法,這就是分代的概念。現(xiàn)在主流的Java虛擬機的垃圾收集器都采用分代收集算法。Java堆區(qū)基于分代的概念,分為新生代和老年代,其中新生代再細分為Eden空間、From Survivor空間和To Survivor空間。因為Eden空間中的大多數(shù)生命周期很短,所以新生代的空間劃分并不是均分的,HotSpot虛擬機默認Eden空間和兩個Survivor空間的所占的比例為8:1。
根據(jù)Java堆區(qū)的空間劃分,垃圾收集的類型分為兩種,它們分別如下:
- Minor Collection:新生代垃圾收集。
- Full Collection:對老年代進行收集,又可以稱作Major Collection,Full Collection通常情況下會伴隨至少一次的Minor Collection,它的收集頻率較低,耗時較長。
當(dāng)執(zhí)行一次Minor Collection時,Eden空間的存活對象會被復(fù)制到To Survivor空間,并且之前經(jīng)過一次Minor Collection 并在From Survivor空間存活的仍年輕的對象也會復(fù)制到To Survivor空間。有兩種情況Eden空間和From Survivor空間存活的對象不會復(fù)制到To Survivor空間, 而是晉升到老年代。一種是存活的對象的分代年齡超過-XX:MaxTenuringThreshold(用于控制對象經(jīng)歷多少次Minor GC 才晉升到老年代)所指定的閾值。另一種是To Survivor空間容量達到閾值。當(dāng)所有存活的對象被復(fù)制到To Survivor空間,或者晉升到老年代,也就意味著Eden空間和From Survivor空間剩下的都是可回收對象。
這個時候GC執(zhí)行Minor Collection,Eden空間和From Survivor空間都會被清空,新生代存活的對象都存放在To Survivor空間。接下來將From Survivor空間和To Survivor空間互換位置,也就是此前的From Survivor空間成為了現(xiàn)在的To Survivor空間,每次Survivor空間互換都要保證To Survivor空間是空的,這就是復(fù)制算法在新生代中的應(yīng)用。在老年代則會采用標記-壓縮算法或標記-清除算法。
總結(jié)
以上是生活随笔為你收集整理的垃圾回收算法_垃圾回收算法有哪些的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: std::deque的使用
- 下一篇: PPT演讲的技巧