GC垃圾回收

来源:https://www.cnblogs.com/scChen/archive/2019/04/01/10639234.html
-Advertisement-
Play Games

通常GC採用有向圖的方式記錄和管理堆區中的所有對象 JVM將堆記憶體劃分為 Eden、Survivor 和 Tenured/Old 空間。 1. 年輕代 所有新生成的對象首先都是放在Eden區。 年輕代的目標就是儘可能快速的收集掉那些生命周期短的對象,對應的是Minor GC,每次 Minor GC ...


通常GC採用有向圖的方式記錄和管理堆區中的所有對象  

JVM將堆記憶體劃分為 Eden、Survivor 和 Tenured/Old 空間。

  1. 年輕代

  所有新生成的對象首先都是放在Eden區。 年輕代的目標就是儘可能快速的收集掉那些生命周期短的對象,對應的是Minor GC,每次 Minor GC 會清理年輕代的記憶體,演算法採用效率較高的複製演算法,頻繁的操作,但是會浪費記憶體空間。當“年輕代”區域存放滿對象後,就將對象存放到年老代區域。

  2. 年老代

  在年輕代中經歷了N(預設15)次垃圾回收後仍然存活的對象,就會被放到年老代中。因此,可以認為年老代中存放的都是一些生命周期較長的對象。年老代對象越來越多,我們就需要啟動Major GC和Full GC(全量回收),來一次大掃除,全面清理年輕代區域和年老代區域。

  3. 持久代

  用於存放靜態文件,如Java類、方法等。持久代對垃圾回收沒有顯著影響。

Minor GC:

  用於清理年輕代區域。Eden區滿了就會觸發一次Minor GC。清理無用對象,將有用對象複製到“Survivor1”、“Survivor2”區中(這兩個區,大小空間也相同,同一時刻Survivor1和Survivor2只有一個在用,一個為空)

  ·Major GC:

  用於清理老年代區域。

  ·Full GC:

  用於清理年輕代、年老代區域。 成本較高,會對系統性能產生影響。

記憶體回收機制:
記憶體回收就是釋放掉在記憶體中已經沒用的對象。
首先,要判斷怎樣的對象是沒用的對象。這裡有2種方法:
1.採用標記計數的方法(引用計數法):
給記憶體中的對象給打上標記,對象被引用一次,計數就加1,引用被釋放了,計數就減一,當這個計數為0的時候,這個對象就可以被回收了。當然,這也就引發了一個問題:迴圈引用的對象是無法被識別出來並且被回收的。所以就有了第二種方法:
2.採用根搜索演算法(引用可達法):
從一個根出發,搜索所有的可達對象,這樣剩下的那些對象就是需要被回收的
判斷完了哪些對象是沒用的,這樣就可以進行回收了
最簡單的,就是直接清空那個需要被回收的對象。但是這又出現了一個問題,就是記憶體會被分為一塊一塊的小碎片。
為瞭解決這個問題,可以採用第二種方法,就是在之前的基礎上將存活的對象給整理一下,使他們變成一個連續的記憶體,從而釋放出連續的較大的記憶體空間。
還有一中回收方法就是採用複製的辦法:將記憶體分為2塊,一塊用來存放對象,另一塊用來放著,當存放對象的那塊滿了以後就將上面存活的對象給複製過來,然後在這塊記憶體上工作,並且將之前的記憶體清空,當自己這塊滿了以後再複製回去,如此反覆。

垃圾回收過程:

    1、新創建的對象,絕大多數都會存儲在Eden中,

    2、當Eden滿了(達到一定比例)不能創建新對象,則觸發垃圾回收(GC),將無用對象清理掉,

           然後剩餘對象複製到某個Survivor中,如S1,同時清空Eden區

    3、當Eden區再次滿了,會將S1中的不能清空的對象存到另外一個Survivor中,如S2,

          同時將Eden區中的不能清空的對象,也複製到S1中,保證Eden和S1,均被清空。

    4、重覆多次(預設15次)Survivor中沒有被清理的對象,則會複製到老年代Old(Tenured)區中,

    5、當Old區滿了,則會觸發一個一次完整地垃圾回收(FullGC),之前新生代的垃圾回收稱為(minorGC)

finalized()方法一個對象只能執行一次,只能在第一次進入被回收的隊列,而且對象所屬於的類重寫了finalize方法才會被執行。第二次進入回收隊列的時候,不會再執行其finalize方法,而是直接被二次標記,在下一次GC的時候被GC。 放一張圖吧   垃圾回收他是在虛擬機空閑的時候或者記憶體緊張的時候執行的,什麼時候回收不是由程式員來控制的,這也就是java比較耗記憶體的原因之一。
還有在垃圾回收的時候當檢測到對象沒有用了,需要被回收的時候並不會馬上被回收,而是將其放入到一個準備回收的隊列,去執行finalize方法。。然等到下次記憶體回收的時候要是他還是沒有被任何人引用的話,就將其給回收了。   面試問題
  1. 怎樣確定哪些對象是不再被引用的對象?(兩次標記)
首先:通過可達性分析演算法對GC-ROOT對象向下搜索,搜索到的對象到GC-ROOT的路徑被稱為引用鏈,如果一個對象沒有到達GC-ROOT的引用鏈,則此對象被標記。   然後: 對已標記的對象進行篩選,篩選的條件是對象是否有必要執行finalized()方法。當對象沒有覆蓋finalized()方法,或者虛擬機已經調用過此方法,都被視為“沒有必要執行”。   最後:若對象被判定為有必要執行finalized()方法,這些對象將被放置在F-Queue隊列中,進行第二次標記。此時虛擬機自動創建的Finalizer線程來會出發finalized()方法,若對象在finalized()方法中逃逸,此對象將在第二次標記時被移除“即將回收”的集合,如果還沒有逃脫,對象就被回收。
  1. 什麼時候回收?如何回收?
Java將堆記憶體分為3大部分:新生代、老年代和永久代。對新生代,主要採用複製演算法,而針對老年代,通常採用標記-清除演算法或者標記-整理演算法來進行回收。 複製演算法的思想是將記憶體分成大小相等的兩塊區域,每次使用其中的一塊。當這一塊的記憶體用完了,就將還存活的對象複製到另一塊區域上,然後對該塊進行記憶體回收。
  1. 垃圾收集器
垃圾收集器相應地也分為新生代收集器和老年代收集器。其中新生代收集器主要有Serial收集器、ParNew收集器和Parallel Scavenge收集器。老年代收集器主要有Serial Old收集器、Parallel Old收集器和CMS收集器。