JVM虛擬機在判斷哪些對象須要回收以後,接下來就是如何回收這些對象的內存。能夠把系統內存想象成一個個小方格,jvm使用到的內存和須要收集到內存都散亂分佈。如何將須要回收的內存回收,並儘量提升效率和剩餘內存的規整?
算法
這個是最基礎的算法,顧名思義,這個算法分爲兩步:安全
複製算法之因此說它是最基礎的收集算法,是由於後續的收集算法都是基於這種思路並對其不足進行改進而獲得的。數據結構
爲了解決效率問題,一種稱爲「複製」(Copying)的收集算法出現了
實現步驟:jvm
在實際應用中,大部分新生代都使用這個算法,實際並不須要縮小一半這麼多。IBM公司的專門研究代表,新生代中的對象98%是「朝生夕死」的,因此並不須要按照1∶1的比例來劃份內存空間,而是將內存分爲一塊較大的Eden空間和兩塊較小的Survivor空間。ide
標記-整理算法與新生代不一樣,老年代的垃圾回收,通常大部分對象都會存活,若是使用標記-複製操做,不只效率會下降,而且也必須按照1:1的比例來分配內存空間,形成50%的空間浪費。根據老年代的特色,有人提出了另一種「標記-整理」(Mark-Compact)算法,標記過程仍然與「標記-清除」算法同樣,但後續步驟不是直接對可回收對象進行清理,而是讓全部存活的對象都向一端移動,而後直接清理掉端邊界之外的內存
線程
這種算法並無什麼新的思想,只是根據對象存活週期的不一樣將內存劃分爲幾塊。
通常是把Java堆分爲新生代和老年代,這樣就能夠根據各個年代的特色採用最適當的收集算法。對象
垃圾收集的第一步,是肯定哪些對象已死,這裏使用的是可達性分析算法。第一步:枚舉根節點,即肯定可達性分析的GC ROOTS節點。如何根節點可能出現的地方很大,所有都須要掃描一遍,所以可達性分析對執行時間的敏感還體如今GC停頓上,由於這項分析工做必須在一個能確保一致性的快照中進行,——這裏「一致性」的意思是指在整個分析期間整個執行系統看起來就像被凍結在某個時間點上,不能夠出現分析過程當中對象引用關係還在不斷變化的狀況,該點不知足的話分析結果準確性就沒法獲得保證。blog
這點是致使GC進行時必須停頓全部Java執行線程(Sun將這件事情稱爲「Stop TheWorld」)的其中一個重要緣由,即便是在號稱(幾乎)不會發生停頓的CMS收集器中,枚舉根節點時也是必需要停頓的。進程
HoptSpot虛擬機中採用的是準確式GC,因此當執行系統停頓下來後,並不須要一個不漏地檢查完全部執行上下文和全局的引用位置。HoptSpot虛擬機中使用一組稱爲OopMap的數據結構來達到這個目的的,在類加載完成的時候,HotSpot就把對象內什麼偏移量上是什麼類型的數據計算出來,在JIT編譯過程當中,也會在特定的位置記錄下棧和寄存器中哪些位置是引用。這樣,在系統停頓時,就不須要在一一遍歷全部對象,只須要從OopMap直接獲取根節點就能夠了。內存
在OopMap的協助下,HotSpot能夠快速且準確地完成GC Roots枚舉,但一個很現實的問題隨之而來:可能致使引用關係變化,或者說OopMap內容變化的指令很是多,若是爲每一條指令都生成對應的OopMap,那將會須要大量的額外空間,這樣GC的空間成本將會變得很高。
因此,爲了解決這個問題,引入安全點的概念,即:不是全部操做都記錄OopMap,只在一些關鍵點記錄。這就意味着,當jvm想要中止運行時,不是馬上中止運行,而是等全部線程所有跑到安全點纔會真的中止。
這就引入了下一個問題:如何讓全部線程都跑到安全點?如今主要有兩種方案:搶先式中斷(Preemptive Suspension)和主動式中斷(VoluntarySuspension)
安全點彷佛完美解決了進程如何進入GC的問題,可是有個前提,全部的線程都要「跑」到安全點才能夠,那麼若是線程處於Sleep狀態或者Blocked狀態,這時候線程沒法響應JVM的中斷請求,「走」到安全的地方去中斷掛起,JVM也顯然不太可能等待線程從新被分配CPU時間。對於這種狀況,就須要安全區域(Safe Region)來解決。
安全區域是指在一段代碼片斷之中,引用關係不會發生變化。在這個區域中的任意地方開始GC都是安全的。咱們也能夠把Safe Region看作是被擴展了的Safepoint。
簡單來講,就是一段代碼唄標識爲安全區域,線程執行到這裏時,會標記線程處於安全區域。那麼程序GC時,就無論這個線程,同時這個線程離開安全區域時,若是系統正在GC,也要等待GC完成以後再離開。 簡要地介紹了HotSpot虛擬機如何去發起內存回收的問題,可是虛擬機如何具體地進行內存回收動做仍然未涉及,由於內存回收如何進行是由虛擬機所採用的GC收集器決定的,而一般虛擬機中每每不止有一種GC收集器,這個問題再下一篇博客中探討