【JVM】垃圾回收算法總結

垃圾回收算法有不少種,目前商業虛擬機經常使用的是分代回收算法,但最初並非用這個算法的
咱們來看一下垃圾收集算法的背景知識算法

標記-清除算法
最基礎的垃圾回收算法,顧名思義,整個回收過程分兩步:
1.逐個標記
2.統一回收
該算法能夠算是後來全部垃圾回收算法的基石(後續全部算法都有標記和清除這兩步,只不過策略上有了一些優化)
這裏值得一說的是這個標記 虛擬機是如何判斷一個對象是「活」仍是「死」?
所以又引出兩種標記算法:
1.引用計數算法
引用計數算法很是簡單且高效,當一個對象被引用一次則+1再也不被引用則-1,當計數爲0就是不可能在被使用的對象了,可是這種算法存在一個致命的缺陷:兩個對象相互引用對方呢?因此,這種算法確定不能用,pass掉
2.可達性分析算法
目前的標記算法主流實現都是用的可達性分析算法。就是以一個叫GC Roots的對象爲起點,經過引用鏈向下搜索,若是一個對象經過引用鏈沒法與GC Roots對象連接,就視爲可回收對象,上面說的那種相互引用的狀況天然也解決了。
擴展:即便是可達性分析中不可達的對象也並非非死不可,只是暫處‘緩刑’,真正宣告一個對象死亡至少還要經歷兩次標記過程:當被斷定不可達以後那麼他被第一次標記並進行篩選,若對象沒有覆蓋finalize()方法或者finalize()方法已經被虛擬機調用過就‘放生’,若是被斷定須要執行finalize()方法就會被放到一個叫F-Queue的隊列中進行第二次標記對象被再次被引用就會放生,不然就會被回收。
finalize()方法
finalize()是Object中的方法,當垃圾回收器將要回收對象所佔內存以前被調用,即當一個對象被虛擬機宣告死亡時會先調用它finalize()方法,讓此對象處理它生前的最後事情(這個對象能夠趁這個時機掙脫死亡的命運)性能

說到這裏敏銳的小夥伴可能以及察覺到了,上面都在說引用因此引用的定義就顯得尤其關鍵了
JDK1.2後Java對引用的概念進行了擴充,將引用分爲:強引用、軟引用、弱引用、虛引用四種測試

強引用:用處很大,不管如何都不會被GC回收
軟引用:有必定用處但不大,內存實在不夠纔會在內存溢出以前回收掉
弱引用:比軟引用強度更弱一些,GC會讓它多活一輪,下一輪就回收
虛引用:必回收,惟一做用就是被GC回收時會收到一個系統通知

複製算法
前面說的標記-清除算法其實兩個過程效率都很低,而且回收以後內存被‘摳出不少洞’內存碎片化嚴重,此時若是過來了一個較大的對象,找不到一整塊連續的內存空間就不得不提早觸發另一次GC回收。
而複製算法則選擇將內存一分爲二每次只使用其中一半,滿了以後將存活的對象整齊複製到另外一塊乾淨的內存上,將剩下的碎片一次性擦除,簡單高效。可是也存在一個很大的缺陷,那就是可用內存變爲原來的一半了。優化

分代收集算法
事實上後來IBM公司通過研究發現,98%的對象都是‘朝生夕死’,因此並不須要1:1的劃份內存,即咱們如今經常使用的分代收集算法:
根據對象的存活週期將內存劃分爲兩塊,分別爲新生代和老年代,而後對各代採用不一樣的回收算法,在新生代中大部分是‘朝生夕死’的對象,繼續將新生代8:2劃分爲Eden區和survival區,其中survival區1:1分紅s0和s1兩塊,採用以前說的複製算法,減小內存碎片的產生。
新生代滿了會進行一次minor GC ,minor GC 存活的對象轉移到survival區,survival區滿了就會將survival區進行回收,存活的survival區對象複製到另一塊survival區中,而且survival區對象每存活一輪年齡+1當到達必定年齡就會前往老年代。日誌

擴展01:JVM什麼時候會進行全局GCcode

01.手動調用System.GC 但也不是當即調用
02.老年代空間不足
03.永生代空間不足
04.計算得知新生代前往老年代平均值大於老年代剩餘空間

擴展02:在壓力測試時,發現FullGC頻率很高,如何解決對象

01.觀察GC日誌,判斷是否有內存泄漏,或者存在內部不合理點
02.調整JVM參數,如新生代、老年代大小 S0+S1大小比例,選用不一樣的當即回收器
03.Dump內存,作進一步的對象分析
04.壓測腳本的編寫,性能問題解決前能夠發現問題,並對解決方案進行驗證
相關文章
相關標籤/搜索