全文共 1890 個字,讀完大約須要 6 分鐘。java
上一篇咱們講了垃圾標記的一些實現細節和經典算法,而本文將系統的講解一下垃圾回收的經典算法,和Hotspot虛擬機執行垃圾回收的一些實現細節,好比安全點和安全區域等。算法
由於各個平臺的虛擬機操做內存的方法各不相同,且牽扯大量的程序實現細節,因此本文不會過多的討論算法的具體實現,只會介紹幾種算法思想及發展過程。安全
標記-清除算法是最基礎的算法,像它的名字同樣算法分爲「標記」和「清除」兩個階段,首先須要標記出所須要回收的對象,標記完成後統一收集被標記的對象。數據結構
優勢: 實現簡單。 缺點: 產生不連續的內存碎片;「標記」和「清除」的執行效率都不高。性能
標記-清除算法執行過程圖:學習
(本文圖片來自《深刻理解Java虛擬機》)線程
複製算法就是將內存分爲大小相同的兩塊,當這一塊使用完了,就把當前存活的對象複製到另外一塊,而後一次性清空當前區塊。設計
優勢: 執行效率高。3d
缺點: 空間利用率低, 由於複製算法每次只能使用一半的內存。指針
也稱標記-壓縮算法,標記-整理算法採用和標記清除算法同樣的對象「標記」,但後續不會對可回收對象進行清理,而是將存活的對象往一端空閒空間移動,而後清理邊界之外的內存空間。
優勢: 解決了內存碎片問題,比複製算法空間利用率高。
缺點: 由於有局部對象移動,相對效率不高。
標記-整理算法執行過程圖:
目前商用虛擬機都採用的是分代收集的算法,這種算法按照對象存活週期把內存分爲幾塊,通常Java中分爲新生代和老年代。把存活率低的對象分到新生代使用複製算法提升垃圾回收的性能,老年代則存放存活率搞的對象,使用標記-清除和標記-整理的算法,提升內存空間使用率。
新生代和老生代的具體介紹和參數配置,後續的文章會詳細講解。
本節將詳細的介紹一下HotSpot虛擬機在執行垃圾回收時的一些細節,目的是讓讀者更好的理解Java虛擬機。
HotSpot虛擬機: 它是Sun JDK和OpenJDK自定的虛擬機,也是目前使用最普遍的虛擬機。
垃圾回收流程: Java虛擬機在內存回收以前,爲了保證內存的一致性,必須先暫停程序的執行,也就是傳說中的Stop The World(簡稱STW),在使用可達性分析算法枚舉GC Roots,標記出死亡對象,再進行垃圾回收。
垃圾回收遇到的問題: 那既然是要暫停程序的運行,就必定要保證中止的時間足夠短,而且可控,否則帶來的災難將是毀滅性的。
解決方案: 顯然HotSpot在設計的時候也考慮到了這個問題,因此在JIT編譯的時候就會使用OopMap數據結構來記錄棧和寄存器上的引用,這樣虛擬機就直接知道了那些地方存放着對象的引用,以下圖,爲我編譯String.hashCode()方法的部分本地代碼:
能夠看出,使用OopMap數據結構存儲了普通對象的指針引用。
查看彙編的方法,啓動命令窗體執行:java -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly YouTestClass
命令可能會報錯: Could not load hsdis-amd64.dll; library not loadable; PrintAssembly is disabled
報錯解決方法:使用編譯好的hsdis.dll放到:jre安裝目錄\bin\server目錄下便可,hsdis.dll地址地址:https://pan.baidu.com/s/1-D6u0gnUx291LXS3bHOorA
安全點(Safepoint)
在OopMap的協助下,HotSpot能夠快速的完成GC Roots枚舉,但致使OopMap內容變化的指令不少,並且若是給每一個對象生成對應的OopMap,會形成大量額外的空間,這會致使GC成本很高,因此HotSpot只會在「特定的位置」生成對應的OopMap,這些位置就成爲「安全點」。
HotSpot也並非任什麼時候刻都會停頓下來進行GC,只會在程序都到底安全點以後纔會GC,因此安全點的設置不能太少,讓GC等待時間太長,也不能太多增大運行時的成本。
安全點的兩種線程中斷方式
搶斷式中斷:不須要線程的執行代碼去主動配合,當發生GC時,先強制中斷全部線程,而後若是發現某些線程未處於安全點,恢復程序運行,直到進入安全點爲止。
主動式中斷:不強制中斷線程,只是簡單地設置一箇中斷標記,各個線程在執行時輪詢這個標記,一旦發現標記被改變(出現中斷標記)時,那麼將運行到安全點後本身中斷掛起。目前全部商用虛擬機所有采用主動式中斷。
安全區域(Saferegion)
安全點機制僅僅是保證了程序執行時不須要太長時間就能夠進入一個安全點進行 GC 動做,可是當特殊狀況時,好比線程休眠、線程阻塞等狀態的狀況下,顯然HotSpot不可能一直等待被阻塞或休眠的線程正常喚醒執行;此時就引入了安全區的概念。
安全區(Saferegion):安全區域是指在一段區域內,對象引用關係等不會發生變化,在此區域內任意位置開始GC都是安全的;線程運行時,首先標記本身進入了安全區,而後在這段區域內,若是線程發生了阻塞、休眠等操做,HotSpot發起GC時將忽略這些處於安全區的線程。當線程再次被喚醒時,首先他會檢查是否完成了GC Roots枚舉(或這個GC過程),若是完成了就繼續執行,不然將繼續等待直到收到能夠安全離開的Safe Region的信號爲止。
《深刻理解Java虛擬機》
《垃圾回收的算法與實現》
關注公衆號,發送「gc」關鍵字,領取《垃圾回收的算法與實現》學習資料。