jvm 中,程序計數器、虛擬機棧、本地方法棧都是隨線程而生隨線程而滅,棧幀隨着方法的進入和退出作入棧和出棧操做,實現了自動的內存清理,所以,咱們的內存垃圾回收主要集中於 堆和方法區中,在程序運行期間,這部份內存的分配和使用都是動態的。html
引用計數法:每一個對象有一個引用計數屬性,新增一個引用時計數加1,引用釋放時計數減1,計數爲0時能夠回收。
缺點是沒法釋放循環引用的對象。以下圖:算法
根搜索算法:從GC Roots開始向下搜索,搜索所走過的路徑稱爲引用鏈。當一個對象到GC Roots沒有任何引用鏈相連時,則證實此對象是不可用的。
在Java語言中,GC Roots包括:
虛擬機棧中引用的對象。
方法區中類靜態屬性實體引用的對象。
方法區中常量(final)引用的對象。
本地方法棧中JNI引用的對象。數組
能夠看到,該算法能夠釋放循環引用的對象(D和E)。jvm
標記/清除算法:當堆中的有效內存空間(available memory)被耗盡的時候,就會中止整個程序(也被成爲stop the world),而後進行兩項工做,第一項則是標記,第二項則是清除。性能
(1)標記:標記的過程其實就是,遍歷全部的GC Roots,而後將全部GC Roots可達的對象標記爲存活的對象。
(2)清除:清除的過程將遍歷堆中全部的對象,將沒有標記的對象所有清除掉。spa
缺點:一、首先,它的缺點就是效率比較低(遞歸與全堆對象遍歷),並且在進行GC的時候,須要中止應用程序,這會致使用戶體驗很是差勁
二、第二點主要的缺點,則是這種方式清理出來的空閒內存是不連續的(碎片化),JVM就不得不維持一個內存的空閒列表,這又是一種開銷。並且在分配數組對象的時候,尋找連續的內存空間會不太好找。.net
複製(copying)算法:將內存劃分爲兩個區間,全部動態分配的對象都只能分配在其中一個區間(稱爲活動區間),而另一個區間(稱爲空閒區間)則是空閒的,當有效內存空間耗盡時,JVM將暫停程序運行,開啓複製算法GC線程。將活動區間內的存活對象,所有複製到空閒區間,且嚴格按照內存地址依次排列,與此同時,GC線程將更新存活對象的內存引用地址指向新的內存地址。此時,空閒區間已經與活動區間交換,而垃圾對象如今已經所有留在了原來的活動區間。事實上,在活動區間轉換爲空間區間的同時,垃圾對象已經被一次性所有回收。線程
缺點:一、它浪費了一半的內存。
二、若是對象的存活率很高,咱們能夠極端一點,假設是100%存活,那麼咱們須要將全部對象都複製一遍,並將全部引用地址重置一遍。複製這一工做所花費的時間,在對象存活率達到必定程度時,將會變的不可忽視。htm
標記/整理算法:標記/整理算法與標記/清除算法很是類似,它也是分爲兩個階段:標記和整理。對象
(1)標記:它的第一個階段與標記/清除算法是如出一轍的,均是遍歷GC Roots,而後將存活的對象標記。
(2)整理:移動全部存活的對象,且按照內存地址次序依次排列,而後將末端內存地址之後的內存所有回收。所以,第二階段才稱爲整理階段。
優勢:標記/整理算法不只能夠彌補標記/清除算法當中,內存區域分散的缺點,也消除了複製算法當中,內存減半的高額代價。
缺點:效率也不高,不只要標記全部存活對象,還要整理全部存活對象的引用地址。從效率上來講,標記/整理算法要低於複製算法。
總結:一、三個算法都基於根搜索算法去判斷一個對象是否應該被回收,而支撐根搜索算法能夠正常工做的理論依據,就是語法中變量做用域的相關內容。所以,要想防止內存泄露,最根本的辦法就是掌握好變量做用域,
二、在GC線程開啓時,或者說GC過程開始時,它們都要暫停應用程序(stop the world)。
三、性能比較
效率:複製算法>標記/整理算法>標記/清除算法(此處的效率只是簡單的對比時間複雜度,實際狀況不必定如此)。
內存整齊度:複製算法=標記/整理算法>標記/清除算法。
內存利用率:標記/整理算法=標記/清除算法>複製算法。
GC分代的基本假設:絕大部分對象的生命週期都很是短暫,存活時間短。
GC將對象數據進行分類。主要是兩類:年輕代(Young Generation),老年代(Old Generation)。分代收集主要針對這兩類的對象進行回收。
年輕代(Young Generation):年輕代含兩種結構,伊甸園空間(1個,佔80%)和倖存空間(2個,各佔10%)。大多數對象會很快的變得不可達,所以,不少對象會在變成年輕代以後就消失,而這個過程咱們稱之爲「 minor GC」。因爲存活率低,選用複製算法,一旦發生GC,將10%的倖存區間與另外80%伊甸園空間中存活的對象轉移到10%的倖存空間,接下來,將以前90%的內存所有釋放。
年輕代遵循如下規則:
一般剛剛被建立的對象會存放在伊甸園空間。
伊甸園空間執行GC後,將Eden和From活着的對象一次性複製到另外一個名爲To的Survivor中去,而後清理Eden和From
執行GC屢次後,依然存活的對象會被轉移至老年代。
老年代(old Generation):對象來自新生代,如上所說部分對象會不可達,而剩下的從年輕代中存活下來,被拷貝至老年代。老年代所佔用的空間要比年輕代多。如上,對象在老年代也會消失,而這個過程被稱之爲「major GC」(或者是 「full GC」).老年代中由於對象存活率高、沒有額外空間對它進行分配擔保,就必須使用「標記-清除」或「標記-整理」算法來進行回收。
Permanebt Generation :持久代,或者稱爲方法區(method area),一般持久代用來保存類常量以及字符串常量。而特別須要注意這個持久代區域不是用來保存從老年代存活下來的對象的。持久代也能夠發生GC。同時這個區域的GC會被看待爲 major GC.(使用「標記-清除」或「標記-整理」算法)
永久代主要回收兩種:常量池中的常量,無用的類信息。
要知道常量的回收是相對簡單的,主要是無用的類回收比較麻煩,要注意如下幾點:
類的實例已經所有被回收了
ClassLoader已經被回收
類的對象沒有被引用
一般狀況下,如下兩種狀況發生的時候,對象會重新生代區域轉到年老帶區域。
一、在年輕代裏的每個對象,都會有一個年齡,當這些對象的年齡到達必定程度時(年齡就是熬過的GC次數,每次GC若是對象存活下來,則年齡加1),則會被轉到年老代,而這個轉入年老代的年齡值,通常在JVM中是能夠設置的。
二、在年輕代存活對象佔用的內存超過10%時,則多餘的對象會放入年老代。這種時候,年老代就是新生代的「備用倉庫」。
參考文章:
https://www.cnblogs.com/sunfi...
http://www.cnblogs.com/ityouk...
http://blog.csdn.net/u0116690...