JVM(4)——垃圾回收算法

三垃圾回收算法

1哪些變量引用的對象是不能回收的?

​ JVM中使用了一種可達性分析算法來判定哪些對象是可以被回收的,哪些對象是不可以被回收的。
​ 這個算法的意思,就是說對每個對象,都分析一下有誰在引用他,然後一層一層往上去判斷,看是否有一個GC Roots。

2哪些可以作爲GC Roots的對象

  1. 虛擬機(棧幀中的本地變量表)中引用的對象
  2. 方法區中類靜態屬性引用的對象
  3. 方法區中常量引用的對象
  4. 本地方法棧中JNI(即一般說的native方法)中引用的對象

3.Java中對象不同的引用類型

  1. 強引用
  2. 軟引用
    軟引用就是說有的對象可有可無,如果內存實在不夠了,可以回收他。
  3. 弱引用
    發生垃圾回收,就會把這個對象回收掉。
  4. 虛引用

4.finalize()方法的作用

​ 有GC Roots引用的對象不能回收,沒有GC Roots引用的對象可以回收,如果有GC Roots引用,但是如果是軟引用或者弱引用的,也有可能被回收掉。

  1. 回收的環節了,假設沒有GC Roots引用的對象,是一定立馬被回收嗎?

    ​ 不是的,這裏有一個finalize()方法可以拯救他自己,假如這個對象重寫了Object類中的finialize()方法,此時會先嚐試調用一下他的finalize()方法,看是否把自己這個實例對象給了某個GC Roots變量,如果重新讓某個GC Roots變量引用了自己,那麼就不用被垃圾回收了

5.一種不太好的垃圾回收思路,標記清除算法

​ 直接對被使用的那塊內存區域中的垃圾對象進行標記。標記出哪些對象是可以被垃圾回收的,然後就直接對那塊內存區域中的對象進行垃圾回收,把內存空出來。

​ 垃圾回收後,保留了一些被人引用的存活對象,存活對象在內存區域裏東一個西一個,非常的凌亂,而且造成了大量的內存碎片,內存碎片太多會造成內存浪費

6.一個合理的垃圾回收思路,複製算法

真正的複製算法會把新生代內存區域劃分爲三塊,1個Eden區,2個Survivor區,其中Eden區佔80%內存空間,每一塊Survivor區各佔10%內存空間,比如說Eden區有800MB內存,每一塊Survivor區就100MB內存,8:1:1默認

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-bQvm8v61-1572926008971)(/Users/laiyanxin/Library/Application Support/typora-user-images/image-20191028214245387.png)]

  1. 剛開始對象都是分配在Eden區內的,如果Eden區快滿了,此時就會觸發垃圾回收,
  2. 會把Eden區中的存活對象都一次性轉移到一塊空着的Survivor區。接着Eden區就會被清空,然後再次分配新對象到Eden區裏,然後就會如上圖所示,Eden區和一塊Survivor區裏是有對象的,其中Survivor區裏放的是上一次Minor GC後存活的對象。
  3. 接着新對象繼續分配在Eden區和另外那塊開始被使用的Survivor區,然後始終保持一塊Survivor區是空着的,就這樣一直循環使用這三塊內存區域。

這麼做最大的好處,就是隻有10%的內存空間是被閒置的,90%的內存都被使用上了

無論是垃圾回收的性能,內存碎片的控制,還是說內存使用的效率,都非常的好。

7什麼時候進入老年代

  1. 默認的設置下,當對象的年齡達到15歲的時候,也就是躲過15次GC的時候,他就會轉移到老年代裏去。可以通過JVM參數“-XX:MaxTenuringThreshold”來設置,默認是15歲

  2. 動態對象年齡判斷

    另外一個規則可以讓對象進入老年代,不用等待15次GC過後纔可以。大致規則就是,假如說當前放對象的Survivor區域裏,一批對象的總大小大於了這塊Survivor區域的內存大小的50%,那麼此時大於等於這批對象年齡的對象,就可以直接進入老年代了,際這個規則運行的時候是如下的邏輯:年齡1+年齡2+年齡n的多個年齡對象總和超過了Survivor區域的50%,此時就會把年齡n以上的對象都放入老年代。

  3. 大對象直接進入老年代
    有一個JVM參數,就是“-XX:PretenureSizeThreshold”,可以把他的值設置爲字節數,比如“1048576”字節,就是1MB,創建一個大於這個大小的對象就直接把這個大對象放到老年代裏去

8Minor GC後的對象太多無法放入Survivor區怎麼辦?

​ 這個時候就必須得把這些對象直接轉移到老年代去

9年代空間分配擔保規則

​ 如果新生代裏有大量對象存活下來,確實是自己的Survivor區放不下了,必須轉移到老年代去

那麼如果老年代裏空間也不夠放這些對象呢?這該咋整呢?

 	1. 首先,在執行任何一次Minor GC之前,JVM會先檢查一下老年代可用的可用內存空間,是否大於新生代所有對象的總大小,
           	2. 如果說發現老年代的內存大小是大於新生代所有對象的,此時就可以放心大膽的對新生代發起一次Minor GC了,因爲即使Minor GC之後所有對象都存活,Survivor區放不下了,也可以轉移到老年代去。
                     	3. 假如執行Minor GC之前,發現老年代的可用內存已經小於了新生代的全部對象大小了,就會看一個“-XX:-HandlePromotionFailure”的參數是否設置了
                  	4. 如果有這個參數,那麼就會繼續嘗試進行下一步判斷。下一步就是看看老年代的內存大小,是否大於之前每一次Minor GC後進入老年代的對象的平均大小。平均都有10MB左右的對象會進入老年代,那麼此時老年代可用內存大於10MB。這就說明,很可能這次Minor GC過後也是差不多10MB左右的對象會進入老年代,此時老年代空間是夠的
                          	5. 如果上面那個步驟判斷失敗了,或者是“-XX:-HandlePromotionFailure”參數沒設置,此時就會直接觸發一次“Full GC”,就是對老年代進行垃圾回收,儘量騰出來一些內存空間,然後再執行Minor GC。
                         	6. 如果上面兩個步驟都判斷成功了,那麼就是說可以冒點風險嘗試一下Minor GC。此時進行Minor GC有幾種可能。

第一種可能,Minor GC過後,剩餘的存活對象的大小,是小於Survivor區的大小的,那麼此時存活對象進入Survivor區域即可。

第二種可能,Minor GC過後,剩餘的存活對象的大小,是大於 Survivor區域的大小,但是是小於老年代可用內存大小的,此時就直接進入老年代即可。

第三種可能,很不幸,Minor GC過後,剩餘的存活對象的大小,大於了Survivor區域的大小,也大於了老年代可用內存的大小。此時老年代都放不下這些存活對象了,就會發生“Handle Promotion Failure”的情況,這個時候就會觸發一次“Full GC”。

如果要是Full GC過後,老年代還是沒有足夠的空間存放Minor GC過後的剩餘存活對象,那麼此時就會導致所謂的“OOM”內存溢出了

10老年代垃圾回收算法

老年代採取的是標記整理算法,這個過程說起來比較簡單,首先標記出來老年代當前存活的對象,這些對象可能是東一個西一個的,接着會讓這些存活對象在內存裏進行移動,把存活對象儘量都挪動到一邊去,讓存活對象緊湊的靠在一起,避免垃圾回收過後出現過多的內存碎片,然後再一次性把垃圾對象都回收掉
老年代的垃圾回收算法的速度至少比新生代的垃圾回收算法的速度慢10倍

11JVM的運行原理

所謂JVM優化,就是儘可能讓對象都在新生代裏分配和回收,儘量別讓太多對象頻繁進入老年代,避免頻繁對老年代進行垃圾回收,同時給系統充足的內存大小,避免新生代頻繁的進行垃圾回收。

12真實實戰

假如有個系統是日處理數據量在上億的規模,生產環境部署了多臺機器,每臺機器大概每分鐘負責執行100次數據提取和計算的任務,每次會提取大概1萬條左右的數據到內存裏來計算,平均每次計算大概需要耗費10秒左右的時間
每臺機器是4核8G的配置,JVM內存給了4G,其中新生代和老年代分別是1.5G的內存空間

這裏每條數據都是比較大的,大概每條數據包含了平均20個字段,可以認爲平均每條數據在1KB左右的大小。那麼每次計算任務的1萬條數據就對應了10MB的大小。

如果新生代是按照8:1:1的比例來分配Eden和兩塊Survivor的區域,那麼大體上來說,Eden區就是1.2GB,每塊Survivor區域在100MB左右。

基本上按照這個內存大小而言,大家會發現,每次執行一個計算任務,就會在Eden區裏分配10MB左右的對象,那麼一分鐘大概對應100次計算任務,其實基本上一分鐘過後,Eden區裏就全是對象,基本就全滿了(100MB*10=1000M),新生代裏的Eden區,基本上1分鐘左右就迅速填滿了。

13垃圾回收器簡介

在新生代和老年代進行垃圾回收的時候,都是要用垃圾回收器進行回收的,不同的區域用不同的垃圾回收器。

**Serial和Serial Old垃圾回收器:**分別用來回收新生代和老年代的垃圾對象

工作原理就是單線程運行,垃圾回收的時候會停止我們自己寫的系統的其他工作線程,讓我們系統直接卡死不動,然後讓他們垃圾回收,這個現在一般寫後臺Java系統幾乎不用。

**ParNew和CMS垃圾回收器:**ParNew現在一般都是用在新生代的垃圾回收器,CMS是用在老年代的垃圾回收器,他們都是多線程併發的機制,性能更好,現在一般是線上生產系統的標配組合

**G1垃圾回收器:**統一收集新生代 和老年代,採用了更加優秀的算法和設計機制,

14Stop the World

VM的痛點:Stop the World,因爲在垃圾回收的時候,儘可能要讓垃圾回收器專心致志的幹工作,不能隨便讓我們寫的Java系統繼續對象了,所以此時JVM會在後臺直接進入“Stop the World”狀態.也就是說,他會直接停止我們寫的Java系統的所有工作線程,讓我們寫的代碼不再運行!然後讓垃圾回收線程可以專心致志的進行垃圾回收的工作.

15到底什麼時候會嘗試觸發Minor GC?

當新生代的Eden區和其中一個Survivor區空間不足時。

觸發MinorGC情況有:

  1. 新生代現有存活對象小於老年代剩餘內存 ,即老年空間代足以支撐可能晉升的對象
  2. 情況1不成立,查看設置了空間擔保且可以擔保成功

16觸發Minor GC之前會如何檢查老年代大小,涉及哪幾個步驟和條件?

  1. 先判斷新生代中所有對象的大小是否 小於 老年代的可用區域 true 則 觸發Minor GC,false則繼續進行下面2中的判斷
  2. 如果設置了-XX:HandlePromotionFailure這個參數,那麼進入第3步 如果沒有設置-XX:HandlePromotionFailure參數,那麼觸發Full GC
  3. 判斷Minor GC歷次進入老年代的平均大小是否 小於 老年代的可用區域 true 則觸發Minor GC,false則會觸發Full GC

17什麼時候在Minor GC之前就會提前觸發一次Full GC?

當判斷 新生代歷次進入老年代對象的平均大小 大於 老年代的可用區域就會觸發一次Full GC,讓老年代騰出一些空間,騰出空間後再進行Minor GC。

18Full GC的算法是什麼?

標記整理算法,速度很慢

19Minor GC過後可能對應哪幾種情況?

(1)小於Survivor區域,進入Survivor區域

(2)大於survivor區域,小於老年代可用空間,進入老年代

(3)大於survivor區域,大於老年代可用空間,進行FullGC,如果FullGC後,老年代可用空間仍小於存活對象,拋出OOM

20哪些情況下Minor GC後的對象會進入老年代?

經過15次(默認,可以設置)MinorGC的

某個年齡的對象大於survivor區域的

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