java相較於c、c++語言的優點之一是自帶垃圾回收器,程序開發人員不用手動管理內存,內存的分配和釋放徹底由gc(Garbage Collector)來作,極大地提升了軟件開發效率及程序健壯性(手動管理內存容易形成內存泄漏)。凡事皆有兩面性,java gc在給咱們帶來內存管理便捷性的同時,也面臨STW(Stop The World)影響程序吞吐的缺陷。做爲java開發人員,只有深刻理解jvm垃圾回收的機制,才能在程序性能出現瓶頸時,更好的對程序進行優化。java
歡迎學Java和大數據的朋友們加入java架構交流: 855835163
羣內提供免費的架構資料還有:Java工程化、高性能及分佈式、高性能、深刻淺出。高架構。性能調優、Spring,MyBatis,Netty源碼分析和大數據等多個知識點高級進階乾貨的免費直播講解 能夠進來一塊兒學習交流哦c++
在垃圾回收以前,jvm須要肯定哪些對象已死,即須要當作垃圾被回收。垃圾確認的方法傳統的有引用計數法:用一個引用計數器來標記對象當前的引用次數,當引用計數爲0時,對象可回收。這種方法有個弊端是沒法解決循環引用的問題,如兩個對象相互引用則它們永遠不會釋放。另一種方法是可達性分析算法,目前主流的語言(java、c#、golang等)都是採用這種方法來斷定一個對象是否存活。可達性分析算法的思路是:將一系列根對象做爲起點,沿着這些起點向下搜索,搜索通過的全部路徑成爲引用鏈,沒有在引用鏈中的對象則爲不可達對象,這類對象會被判斷爲可回收對象。可達性分析圖以下:golang
可達性分析算法
標記清除算法是最基礎的收集算法,顧名思義,算法分爲兩個階段:首先標記須要回收的對象,而後統一回收全部被標記的對象。這種垃圾回收算法比較簡單,可是存在兩個不足之處:一個是效率問題,標記和清除都須要掃描整個內存空間的對象,效率較低;另外一個是標記清除後會產生大量的內存碎片。標記-清除算法流程以下所示:c#
標記-清除算法架構
爲了解決上面的效率和內存碎片問題,有人提出了「複製」算法,它的思路是將內存一分爲二,每次分配對象時只使用其中一半內存空間,當一塊內存空間用完時,就將該塊內存空間上存活的對象複製到另一塊內存上,而後將已使用過的內存空間一次清理掉,這樣每次只對一半的內存進行回收,同時也解決了內存碎片的問題。一樣,複製算法也有很明顯的缺點:1. 犧牲了一半的內存空間,內存利用率最大也就50%;2. 當內存中存活的對象較多時,會進行大量的複製操做,效率會較低。複製算法的執行過程以下所示:jvm
複製算法分佈式
複製算法在存在大量存活對象的狀況下效率低下,更關鍵的是50%的內存空間得不到利用,若是存活對象過多,內存空間可能不夠,則須要額外的空間做爲擔保,所以,對於老年代不適合使用這種算法(新生代有老年代作擔保)。源碼分析
所以,根據老年代的特色,有人提出了「標記-整理」算法,標記-整理算法和標記-清除算法的第一步相同,即對全部須要回收的對象進行標記,不一樣的是,標記-整理算法在第二階段並非直接對可回收的對象進行清理,而是將全部存活的對象向一端移動,而後一把清理掉存活邊界以外的內存。這種算法結合了標記-清除和複製算法的思想,即未浪費內存也避免了內存碎片的產生。「標記-整理」算法以下所示:性能
標記-整理算法
分代收集並無什麼創新之處,這種算法只是將jvm堆內存分爲不一樣的區域,再根據區域的特色採用不一樣的垃圾回收算法。通常分爲新生代和老年代,新生代在再細分爲Eden和Survivor區(8:1),在新生代中,對象的存活率低,所以採用「複製」算法,將Edian區存活的對象複製到Survivor區,而老年代中對象存活率高且沒有額外的中間作擔保,所以採用「標記-清除」或者「標記-整理」算法來進行垃圾回收。
歡迎學Java和大數據的朋友們加入java架構交流: 855835163 羣內提供免費的架構資料還有:Java工程化、高性能及分佈式、高性能、深刻淺出。高架構。性能調優、Spring,MyBatis,Netty源碼分析和大數據等多個知識點高級進階乾貨的免費直播講解 能夠進來一塊兒學習交流哦