GC的思想早在java誕生以前就已經出現,垃圾回收是內存「自動化」的一部分。java
判斷對象是否能夠回收有兩種策略。第一種是引用計數法,方法顧名思義,但它的問題是不能解決循環引用的狀況,循環引用以下所示:算法
public class Test{ public Onject reference; public static void main(String[] args) { Test test1 = new Test(); Test test2 = new Test(); test1.reference = test2; test2.reference = test1; test1 = null; test2 = null; } }
主流jvm使用可達性分析算法,其核心思路是從一系列GC Root對象出發向下搜索,搜索的路徑稱爲引用鏈。GC Root對象包括:安全
GC的區域是堆和方法區(永久代),這兩個區域都是線程共享的。首先先說明清理方法區。方法區中主要存放的是類加載後的信息、常量、靜態屬性等。GC會清理沒用的類和常量。咱們知道java中String是存放在常量池中的,那麼若是沒有引用指向常量池中的某個字符串,gc就會將其清理。清理類條件相對苛刻,須要知足才能夠被回收,並非必定會回收,還跟jvm配置的相關參數有關:數據結構
Gc的主要區域是java堆,本文以及後面博文中會對堆的回收作詳細說明。jvm
這裏會記錄GC算法的思想,算法實現超出能力範圍編碼
1.標記-清除算法spa
顧名思義,算法分爲「標記」和「清除」兩個階段,它是GC中最基礎的算法。但有明顯缺陷:第一效率低;第二產生內存碎片,當程序運行須要分配大對象時,沒法找到足夠的連續內存,進而觸發另外一次GC操做。線程
以前腦子故障一直想不通爲何不邊標記邊刪除,- -想出答案時我簡直「佩服」個人智商,,由於只能按照引用鏈標記可達對象。。。另外看了不少概念性的東西,我在想什麼樣的對象是不可達的。不少例子中直接把引用指向null,這種我以爲並無什麼意義。後來我注意到做用域在是否可達中起很大做用。不一樣大小的做用域,好比類、方法、甚至是某個代碼塊。當離開某個做用域時,做用域中對象變爲不可達。那麼gc就會回收這些內存。指針
2.複製算法code
複製算法產生的緣由是爲了解決標記-清除算法效率低的問題。將可用內存分爲兩塊(原理並不是實際),每次只使用其中一塊,當這塊用完,將存活對象複製到另外一塊中,並清除原來的半區。內存分配再也不考慮碎片問題,僅僅是指針移動便可。不過算法的代價是內存利用率低。
Sun的HotSpot在新生代採用這種回收算法。新生代中大部分,有書上寫98%的對象都是「朝生夕亡」的,多以並不須要按照1:1來劃份內存。將較大的分給Eden,S0、S1則較小。默認是8:1:1的比例,不過也能夠配置相關的jvm參數調整。使用時對象存放在Eden+某個Survivor中,並將存活的複製到另外一個Survivor上。這樣僅僅浪費了10%的新生代空間。但也會出現存活對象多於10%的狀況。此時須要依賴老年代進行分配擔保,若是老年代提供擔保,這些對象直接進入老年代。
3.標記-整理算法
新生代採用複製算法的一個重要緣由是新生代中對象存活率低。若是是老年代,對象存活率高,就須要大量的複製,內存利用率也不多是90%。因此老年代不採用複製算法。採用標記-整理算法。
標記-整理算法,在標記對象後,讓存活對象向一端移動,最後清除邊界之外的內存。
新生代使用複製算法、老年代使用標記-整理算法,這種方式稱爲「分代收集算法」。
爲了快速完成標記過程,減小gc的停頓時間(目前的虛擬機是沒法避免的,任何一種垃圾回收器都會產生停頓,STW Stop The World。產生停頓的緣由是垃圾回收須要確保一致性的快照中進行,保證標記清理時引用關係不變。其實CMS能夠達到很短的停頓,但犧牲了一部分吞吐量,這個下一篇博文會說明),HotSpot中使用一組OopMap的數據結構來達到目的。ps:這裏書上寫的也不徹底明確。類加載完成的時候HotSpot就把對象內什麼偏移量上是什麼類型的數據計算出來,在JIT編譯過程當中,也會在特定的位置記錄下棧和寄存器中哪些位置是引用。這樣GC掃描時就直接得知這些信息。這裏我也不是徹底理解,之後慢慢理解。
安全點。安全點有兩個做用,1.程序運行到安全點才生成OopMap 2.程序執行時只有全部線程都停頓在安全點纔開始GC。方法調用、循環跳轉、異常跳轉這些指令會產生安全點。目前全部的虛擬機都採用主動中斷的方式讓全部線程停在最近的安全點。每一個線程在通過安全點時會檢查一個終端標誌,若是爲真就停下來(掛起)。其實總結一下,安全點就是在編譯的過程當中在特定的地方加入特定的指令,功能分別是生成OopMap和輪詢中斷標誌、中斷線程。若是在GC的過程當中有線程從sleep或掛起的狀態變爲運行狀態,由於他是活動的,會對GC形成影響。虛擬機中還有成爲安全區的機制。安全區域是不會改變引用關係的代碼片斷,若是醒來的線程在安全區中它能夠繼續運行至退出安全區,若是此時沒有完成標記工做,該線程會停下來,等待能夠離開安全區的信號。
jvm基礎總歸會有不少概念性的,很理論的東西。我認爲這些算法思路是能夠應用到平時編碼中的。一些看似顯而易見的結論或算法,在得出結論和應用時也許是不簡單的。下一篇博文會說明各類垃圾回收器。