垃圾收集算法是內存回收的方法論;垃圾收集器是內存回收的具體實現;java
自動內存管理是: 給對象分配內存和回收廢棄對象佔用的內存。算法
垃圾回收的範圍: java堆和方法區。數組
1) 引用計數算法(主流虛擬機並未採納): 給對象添加一個引用計數器,每當有一個地方引用到它時,計數器加1;當引用失效時,計數器值就減1;任什麼時候刻當一個對象的引用計數器爲0時,它就變爲廢棄對象。能夠被回收。安全
優勢: 實現簡單,判斷效率高;多線程
缺點: 很難解決對象之間的相互循環引用的問題。併發
2)可達性分析法: 算法思想是經過一系列的稱爲「GC Roots」的對象做爲起始點,從這些節點向下搜索,搜索所走過的路徑稱爲引用鏈,當一個對象到"GC Roots"沒有任何的引用鏈時,則說明此對象是不可用的。jvm
可做爲"GC Roots"的對象包括如下:性能
一、虛擬機棧中引用的對象;線程
二、方法區中類靜態屬性引用的對象;3d
三、方法區中常量引用的對象;
四、本地方法棧中JNI(即通常native方法)引用的對象。
3)肯定生存仍是死亡
即便是在可達性分析法中不可達的對象,也不是被當即回收的。由於要真正被看成廢棄對象進行回收,至少要通過兩次「標記」過程。
當對象在被第一次發現與一系列「Gc Roots」節點之間沒有引用鏈時,那麼它將會被標記一次,而且進行一次篩選,篩選的條件是是否有必要執行finalize()方法。當對象沒有覆蓋finalize()方法,或者finalize()方法已經被虛擬機調用過。虛擬機將視爲「沒有必要執行」。
若是該對象被斷定有必要執行finalize()方法,那該對象將會被防止在一個F-Queue隊列中,並在稍後由一個低優先級的線程中執行。finalize()方法是對象逃過被垃圾回收的最後一次機會。稍後會對F-Queue進行第二次標記。若是期間尚未與任何對象創建關係,那在第二次標記後基本上真的要被回收了。
1)標記-清除算法
標記清除算法實現思路包含兩個階段,第一個階段,根據可達性分析算法標記全部不可達的「垃圾」,第二階段,直接釋放這些對象所佔用的內存空間。
優勢:是垃圾回收的基礎思想算法,後續的回收算法都是基於這種算法進行改進。
缺點:
2)複製算法
將內存劃分爲兩份大小相等的塊,每次只使用其中的一塊,當系統發起 GC 收集動做時,將當前塊中依然存活的對象所有複製到另外一塊中,並整塊的釋放當前塊所佔內存空間。
現代商用虛擬機:都是採用這種收集算法來回收新生代區域的對象。通常是將內存分爲一塊較大的Eden和兩塊較小的Survivor空間。默認比例是8:1:1。
這樣的話,內存使用率就由50%提高至90%,可是即便是這樣,也不能保證每次回收的都只有很少於10%的對象存活。這個時候,就要由其餘內存(主要是老年代)來進行內存分配擔保。
3)標記整理算法
因爲複製算法在對象存活率較高時,就必需要進行較多的複製操做,效率就會變的很低,更關鍵的是若是不想浪費50%的內存,就須要有額外的空間進行內存分配擔保,來應對超過90&對象存活的極端狀況。因此在老年代中通常是不能直接採用這種算法。
根據老年代的特色,提出的標記-整理算法、其實就是增強版的標記-清除算法。只不過是否是直接將可回收的對象直接清除。是將存活對象向同一端移動,而後回收邊界之外的內存。
4)分代收集算法
當前主流的jvm虛擬機,都是採用「分代收集」算法,這種算法並非新的思想,只是根據對象存活週期的特色,將java堆分爲幾塊。新生代和老年代。「新生代」通常是對象短期是大量廢棄,採用複製算法效率較高。「老年代」通常對象存活率高沒有額外的空間內存進行內存分配擔保,就必須使用「標記-清理」或「標記-清除」算法了。
如圖是做用於不一樣分代的垃圾收集器,若是兩個收集器之間存在連線,就能夠搭配使用。虛擬機所在的區域,則表示它是屬於新生代收集器仍是老年代收集器
「Stop the World「會在任何一種GC算法中發生。「Stop the World「意味着 JVM 由於要執行GC而中止了應用程序的執行。
Serial收集器: 單線程,新生代收集器,使用複製算法。它只會使用一個CPU或一條收集線程去完成垃圾收集工做,在進行垃圾收集時,必須「Stop the World「,暫停替他全部的工做線程,直到它收集結束。
ParNew收集器: Serial收集器的多線程版本,控制參數、收集算法、Stop the World、對象分配規則、回收策略都與Serial收集器徹底同樣
Parallel Scavenge收集器: 生代收集器,使用複製算法,並行多線程。
Serial Old收集器: Serial收集器的老年代版本,單線程,使用標記-整理算法。
Parallel Old收集器: Parallel Scavenge收集器的老年代版本,多線程,使用標記-整理算法
CMS收集器: 一種以獲取最短回收停頓時間爲目標的收集器,基於「標記-清除」算法。運做過程分四個步驟:初始標記 、併發標記、從新標記、併發清除。
初始標記、從新標記這兩個步驟仍然須要「Stop the World」。初始標記僅僅只是標記一下GC Roots能直接關聯到的對象,速度很快。併發標記階段就是進行GC Roots Tracing 的過程,而從新標記階段則是爲了修正併發標記期間由於用戶程序繼續運做而致使標記產生變更的那一部分對象的標記記錄,這一階段的停頓時間通常會比初始標記階段稍長一些,但遠比並發標記的時間段。
整個過程當中耗時最長的併發標記和併發清除過程,收集器線程均可以與用戶線程一塊兒工做,因此,CMS收集器的內存回收過程是與用戶線程一塊兒併發執行的。
優勢: 併發收集,低停頓
缺點: 對CPU資源很是敏感、沒法處理浮動垃圾、基於標記清除算法,收集結束時有大量控件碎片產生
G1收集器: G1收集器是當今收集器技術發展最前沿成果之一,一種面向服務端應用的垃圾收集器。
G1的特色: 並行與併發、分代手機、空間整合、可預測的停頓
運做過程以下: 初始標記、併發標記、最終標記、篩選回收。
初始標記階段: 僅僅只是標記一下GC Roots能直接關聯到的對象,而且修改TAMS的值,讓下一階段用戶程序併發運行時,能在正確可用的Region中建立新對象,這階段須要停頓線程,但耗時很短。
併發標記階段: 是從GC Roots開始對堆中對象進行可達性分析,找出存活的對象,這階段耗時較長,但可與用戶程序併發執行。
而最終標記階段: 則是則是爲了修正在併發標記期間因用戶程序繼續運做而致使標記產生變更的那一部分標記記錄,虛擬機將這段時間對象變化記錄在線程Remembered Set Logs裏面。最終標記階段須要把Remembered Set Logs的數據合併到Remembered Set中,這階段須要停頓線程,可是可並行執行。
最後在篩選回收階段: 首先對各個Region的回收價值和成本進行排序。根據用戶所指望的GC停頓時間來制定回收計劃。
對象優先在Eden分配: 大多數狀況下,對象在新生代Eden區中分配。當Eden區沒有足夠空間進行分配時,虛擬機將發起一次Minor GC。
大對象直接進入老年代: 大對象是指須要大量連續內存控件的Java對象,最典型的大對象就是那種很長的字符串以及數組。
長期存活的對象將進入老年代: 虛擬機採用了分代收集的思想來管理內存,那麼內存回收時就必須能識別哪些對象應放在新生代,哪些對象應放在老年代。爲了作到這點,虛擬機給每一個對象定義了一個對象年齡計數器。若是對象在Eden出生並通過第一次Minor GC後仍然存活,而且能被Survivor容納的話,將被移動到Survivor空間中,而且對象年齡設爲1,對象在Survivor區中每「熬過」一次Minor GC,年齡就增長1歲,當它的年齡增長到必定程度(默認爲15歲),就將會被晉升到老年代。
動態對象年齡斷定: 虛擬機並非永遠要求對象的年齡必須達到了MaxTenuringThreshold才能晉升老年代,若是在Survivor空間中相同年齡全部對象大小的總和大於Survivor空間的一半,年齡大於或等於該年齡的對象就能夠直接進入老年代,無須等到MaxTenuringThreshold中要求的年齡。
空間分配擔保: 在發生Minor GC以前,虛擬機會先檢查老年代最大可用的連續空間是否大於新生代全部對象總空間,若是這個條件成立,那麼Minor GC能夠確保是安全的。若是不成立,則虛擬機會查看HandlePromotionFailure設置值是否容許擔保失敗。
內存回收與垃圾收集器在不少時候都是影響系統性能、併發能力的主要因素之一,虛擬機之因此提供多種不一樣的收集器以及提供大量的調節參數,是由於只有根據實際應用需求、實現方式選擇最優的收集方式才能獲取最高的性能。沒有固定收集器、參數組合,也沒有最優的調優方法,虛擬機也就沒有什麼必然的內存回收行爲。