JVM GC垃圾回收機制

1、JVM GC概述

一、JVM GC回收哪一個區域內的垃圾

JVM GC只回收堆區和方法區內的對象。而棧區的數據,在超出做用域後會被JVM自動釋放掉,因此其不在JVM GC的管理範圍內。算法

二、JVM GC怎麼判斷對象能夠被回收了

在Java程序中,當一個對象O被建立時,它被放在Heap裏。當GC運行的時候,若是發現沒有任何引用指向O, 它就會被回收以騰出內存空間,或者換句話說, 一個對象被回收, 必須知足如下條件:性能

  • 1.對象沒有引用
  • 2.做用域發生未捕獲異常
  • 3.程序在做用域正常執行完畢
  • 4.程序執行了System.exit()
  • 5.程序發生意外終止(被殺線程等)

綜上所述:學習

  • 1.沒有任何引用指向它
  • 2.GC

在Java程序中不能顯式的分配和註銷內存,由於這些事情JVM都幫咱們作了,那就是GC。有些時候咱們能夠將相關的對象設置成null來試圖顯示的清除內存,可是並非設置爲null就會必定被標記爲可回收,有可能會發生逃逸。將對象設置成null至少沒有什麼壞處,可是調用System.gc()會顯著地影響系統性能,使用System.gc()時候並非立刻執行GC操做,而是會等待一段時間,甚至不執行,並且System.gc()若是被執行,會觸發Full GC,這很是影響性能。優化

三、JVM GC何時執行

Eden區空間不夠存放新對象的時候,執行Scavenge(Minro)GC。升到老年代的對象大於老年代剩餘空間的時候執行Full(Major) GC。線程

四、每一個空間的執行順序

(1)絕大多數剛剛被建立的對象會存放在伊甸園空間(Eden)。3d

(2)在伊甸園空間執行第一次GC(Scanvage GC)以後,存活的對象被移動到其中一個倖存者空間 (Survivor)。指針

(3)此後,每次伊甸園空間執行GC後,存活的對象會被堆積在同一個倖存者空間。cdn

(4)當一個倖存者空間飽和,還在存活的對象會被移動到另外一個倖存者空間。而後會清空已經飽和的那個倖存者空間。對象

(5)在以上步驟中重複N次(N=MaxTenuringThreshold(年齡閥值設定,默認15))依然存活的對象,就會被移動到老年代。blog

從上面的步驟能夠發現,兩個倖存者空間,必須有一個是保持空的。若是兩個兩個倖存者空間都有數據,或兩個空間都是空的,那必定是你的系統出現了某種錯誤。咱們須要重點記住的是,對象在剛剛被建立以後,是保存在伊甸園空間的(Eden)。那些長期存活的對象會經由倖存者空間(Survivor)轉存到老年代空間。也有例外出現,對於一些比較大的對象(須要分配一塊比較大的連續內存空間)則直接進入到老年代,通常在年輕代空間不足的狀況下發生。

五、有以下緣由可能致使Full GC

  • 1.年老代被寫滿
  • 2.持久代被寫滿
  • 3.System.gc()被顯示調用
  • 4.上一次GC以後Heap的各域分配策略動態變化

2、JVN GC調優

在學習Java GC 以前,咱們須要記住一個單詞:stop-the-world。它會在任何一種GC算法中發生。stop-the-world 意味着JVM由於須要執行GC而中止了應用程序的執行。當stop-the-world發生時,除GC所需的線程外,全部的線程都進入等待狀態,直到GC任務完成。

通過n次垃圾回收存活的對象(這個n被稱爲年齡閥值,默認是15次)。老年代空間的構成其實很簡單,它不像新生代空間那樣劃分爲幾個區域,它只有一個區域,裏面存儲的對象並不像新生代空間絕大部分對象都是朝聞道,夕死矣。這裏的對象幾乎都是從Survivor空間中熬過來的,它們毫不會輕易的狗帶。所以,Full GC發生的次數不會有Scanvage GC那麼頻繁,而且作一次Full GC的時間比Scanvage GC 要更長(約10倍)。

因此GC調優主要是減小Full GC的觸發次數,能夠經過NewRatio(新生代與老年代的佔比)控制新生代轉 老年代的比例,也可經過MaxTenuringThreshold(年齡閥值設定)設置對象進入老年代的年齡閥值。

默認的所佔空間比例年輕代(Young generation) :老年代(Old generation) = 1 : 2

默認新生代空間的分配:Eden : SurvivorA : SurvivorB = 8 : 1 : 1

總結:

  • 1.能夠經過NewRatio控制新生代轉老年代的比例
  • 2.經過MaxTenuringThreshold設置對象進入老年代的年齡閥值(默認是15次)。
  • 3.不要輕易調用System.gc()

3、JVM GC算法

一、複製算法

複製算法將內存劃分爲兩個區間,使用此算法時,全部動態分配的對象都只能分配在其中一個區間(活動區間),而另一個區間(空間區間)則是空閒的。

採用從根集合掃描,將存活的對象複製到空閒區間,當掃描完畢活動區間後,會將活動區間一次性所有回收。此時本來的空閒區間變成了活動區間。下次GC時候又會重複剛纔的操做,以此循環。 複製算法在存活對象比較少的時候,極爲高效,可是帶來的成本是犧牲一半的內存空間用於進行對象的移動。

二、標記-清除算法

採用從根集合進行掃描,對存活的對象進行標記,標記完畢後,再掃描整個空間中未被標記的對象進行直接回收,該算法不須要進行對象的移動,而且僅對不存活的對象進行處理,在存活的對象比較多的狀況下極爲高效,但因爲標記-清除算法直接回收不存活的對象,並無對還存活的對象進行整理,所以會致使內存碎片。

三、標記-整理算法

標記-整理算法是在標記-清除算法之上,又進行了對象的移動排序整理,所以成本更高,但卻解決了內存碎片的問題。

採用從根集合掃描,對存活的對象進行標記,標記完畢後,再掃描整個空間中未被標記的對象進行直接回收, 將全部存活的對象往左端空閒空間移動,並更新對應的指針。雖然進行了對象的移動排序整理,所以成本更高,但卻解決了內存碎片的問題。

四、總結

JVM爲了優化內存的回收,使用了分代回收的方式,對於新生代內存的回收(Scavenge GC)主要採用 複製算法。而對於老年代的回收(Full GC),大多采用標記-整理算法。

相關文章
相關標籤/搜索