在介紹垃圾回收算法以前,須要先了解一個詞「stop the world」,stop the world會在執行某一個垃圾回收算法的時候產生,JVM爲了執行垃圾回收,會暫時java應用程序的執行,等垃圾回收完成後,再繼續運行。若是使用JMeter測試java程序,可能會發如今測試過程當中,java程序有不規則的停頓現象,其實這就是「stop the world」,停頓的時候JVM是在作垃圾回收。因此儘量減小stop the world的時間,就是優化JVM的主要目標。接下來看一下目前有哪些常見垃圾回收的算法。java
引用計數法顧名思義,就是對一個對象被引用的次數進行計數,當增長一個引用計數就加1,減小一個引用計數就減1。算法
上圖表示3個Teacher的引用指向堆中的Teacher對象,那麼Teacher對象的引用計數就是3,以此類推Student對象的引用計數就是2。併發
上圖表示Teacher對象的引用減小爲2,Student對象的引用減小爲0(減小的緣由是該引用指向了null,例如teacher3=null),按照引用計數算法,Student對象的內存空間將被回收掉。性能
引用計數算法原理很是簡單,是最原始的回收算法,可是java中沒有使用這種算法,緣由有2。1是頻繁的計數影響性能,2是它沒法處理循環引用的問題。測試
例如Teacher對象中引用了Student對象,Student對象中又引用了Teacher對象,這種狀況下,對象將永遠沒法被回收。優化
標記清除算法,它是不少垃圾回收算法的基礎,簡單來講有兩個步驟:標記、清除。spa
標記:遍歷全部的GC Roots,並將從GC Roots可達的對象設置爲存活對象;對象
清除:遍歷堆中的全部對象,將沒有被標記可達的對象清除;內存
注意上圖灰色的對象,由於從GC Root遍歷不到它們(儘管它們自己有引用關係,但從GC Root沒法遍歷到它們),所以它們沒有被標記爲存活對象,在清除過程當中將會被回收。虛擬機
這裏須要注意的是標記清除算法執行過程當中,會產生「stop the world」,讓java程序暫停等待以保證在標記清除的過程當中,不會有新的對象產生。爲何必須暫停java程序呢?舉個例子,若是在標記過程完成後,又新產生了一個對象,而該對象已經錯過了標記期,那麼在接下來的清除流程中,這個新產生的對象由於未被標記,因此將被視爲不可達對象而被清除,這樣程序就會出錯,所以標記清除算法在執行時,java程序將被暫停,產生「stop the world」。
接下來總結一下標記清除算法:
一、由於涉及大量的內存遍歷工做,因此執行性能較低,這也會致使「stop the world」時間較長,java程序吞吐量下降;
二、對象被清除以後,被清除的對象留下內存的空缺位置,形成內存不連續,空間浪費。
接下來看一下其餘算法能不能改善這些問題?
標記壓縮算法,它就是在標記清除算法的基礎上,增長了壓縮過程。
在進行完標記清除以後,對內存空間進行壓縮,節省內存空間,解決了標記清除算法內存不連續的問題。
注意標記壓縮算法也會產生「stop the world」,不能和java程序併發執行。在壓縮過程當中一些對象內存地址會發生改變,java程序只能等待壓縮完成後才能繼續。
複製算法簡單來講就是把內存一分爲二,但只使用其中一份,在垃圾回收時,將正在使用的那分內存中存活的對象複製到另外一份空白的內存中,最後將正在使用的內存空間的對象清除,完成垃圾回收。
複製算法相對標記壓縮算法來講更簡潔高效,但它的缺點也顯而易見,它不適合用於存活對象多的狀況,由於那樣須要複製的對象不少,複製性能較差,因此複製算法每每用於內存空間中新生代的垃圾回收,由於新生代中存活對象較少,複製成本較低。它另一個缺點是內存空間佔用成本高,由於它基於兩分內存空間作對象複製,在非垃圾回收的週期內只用到了一分內存空間,內存利用率較低。
以上介紹了常見的垃圾回收算法,這些算法各有各的優缺點,但在JVM中並非單純的使用特定的算法,而是使用的一種叫垃圾回收器的東西,垃圾回收器能夠看作一系列算法的不一樣組合,在不一樣的場景使用合適的垃圾回收器,才能起到事半功倍的效果。下一篇將介紹垃圾回收器。
參考資料:
《實戰Java虛擬機》 葛一鳴
《深刻理解Java虛擬機(第2版)》 周志明