JVM(四)垃圾回收的實現算法和執行細節

上一篇我們講了垃圾標記的一些實現細節和經典算法,而本文將系統的講解一下垃圾回收的經典算法,和Hotspot虛擬機執行垃圾回收的一些實現細節,比如安全點和安全區域等。

因爲各個平臺的虛擬機操作內存的方法各不相同,且牽扯大量的程序實現細節,所以本文不會過多的討論算法的具體實現,只會介紹幾種算法思想及發展過程。

垃圾回收算法

1、標記-清除算法

標記-清除算法是最基礎的算法,像它的名字一樣算法分爲“標記”和“清除”兩個階段,首先需要標記出所需要回收的對象,標記完成後統一收集被標記的對象。

優點: 實現簡單。
缺點: 產生不連續的內存碎片;“標記”和“清除”的執行效率都不高。

標記-清除算法執行過程圖:

標記-清除算法

(本文圖片來自《深入理解Java虛擬機》)

2、複製算法

複製算法就是將內存分爲大小相同的兩塊,當這一塊使用完了,就把當前存活的對象複製到另一塊,然後一次性清空當前區塊。

優點: 執行效率高。

缺點: 空間利用率低, 因爲複製算法每次只能使用一半的內存。

複製算法

3、標記-整理算法

也稱標記-壓縮算法,標記-整理算法採用和標記清除算法一樣的對象“標記”,但後續不會對可回收對象進行清理,而是將存活的對象往一端空閒空間移動,然後清理邊界以外的內存空間。

優點: 解決了內存碎片問題,比複製算法空間利用率高。

缺點: 因爲有局部對象移動,相對效率不高。

標記-整理算法執行過程圖:

標記-整理算法

4、分代收集算法

目前商用虛擬機都採用的是分代收集的算法,這種算法按照對象存活週期把內存分爲幾塊,一般Java中分爲新生代和老年代。把存活率低的對象分到新生代使用複製算法提高垃圾回收的性能,老年代則存放存活率搞的對象,使用標記-清除和標記-整理的算法,提高內存空間使用率。

新生代和老生代的具體介紹和參數配置,後續的文章會詳細講解。

垃圾回收執行細節

本節將詳細的介紹一下HotSpot虛擬機在執行垃圾回收時的一些細節,目的是讓讀者更好的理解Java虛擬機。

HotSpot虛擬機: 它是Sun JDK和OpenJDK自定的虛擬機,也是目前使用最廣泛的虛擬機。

垃圾回收流程: Java虛擬機在內存回收之前,爲了保證內存的一致性,必須先暫停程序的執行,也就是傳說中的Stop The World(簡稱STW),在使用可達性分析算法枚舉GC Roots,標記出死亡對象,再進行垃圾回收。

垃圾回收遇到的問題: 那既然是要暫停程序的運行,就一定要保證停止的時間足夠短,並且可控,不然帶來的災難將是毀滅性的。

解決方案: 顯然HotSpot在設計的時候也考慮到了這個問題,所以在JIT編譯的時候就會使用OopMap數據結構來記錄棧和寄存器上的引用,這樣虛擬機就直接知道了那些地方存放着對象的引用,如下圖,爲我編譯String.hashCode()方法的部分本地代碼:

String.hashCode本地代碼

可以看出,使用OopMap數據結構存儲了普通對象的指針引用。

查看彙編的方法,啓動命令窗體執行:java -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly YouTestClass

命令可能會報錯: Could not load hsdis-amd64.dll; library not loadable; PrintAssembly is disabled

報錯解決方法:使用編譯好的hsdis.dll放到:jre安裝目錄binserver目錄下即可,hsdis.dll地址地址:https://pan.baidu.com/s/1-D6u...

安全點(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”關鍵字,領取《垃圾回收的算法與實現》學習資料。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章