JVM: GC過程

堆區是用來存儲new出來的對象的,當對象填充滿堆區後,就會導致內存爆掉,程序就GG了。

就需要科學的進行GC:

首先需要判斷這個對象是否應該被刪除,如果應該被刪除,那麼需要將這個對象清理掉。

判斷的標準:GCRoot(一般是指被棧上的直接或間接引用、本地方法棧直接或間接引用的對象、方法區的j靜態static變量或常量直接或間接引用的對象)

和GCRoot沒有相連的關係的就可以刪除。

清理堆區對象的思路:

  • 標記-清理(對象後打標,如果應該被刪除就標記一下,然後再掃描一次,把含有標記的對象刪除掉)

但是標記清理算法的缺點是產生內存碎片。【就像衣服打洞】

  • 標記-整理(清除過後,後面的對象補上來)

這樣就減少了內存的碎片,但是代價太大,所有對象都需要前移。

  • 複製算法:

複製算法將內存1分爲2.在1區上創建的對象標記是否需要刪除,等到快滿的時候,不是直接將這個對象刪除,而是往2區進行復制,

需要刪除的就不復制過來了,不需要刪除的這些就緊湊的複製過來。這樣就避免了內存碎片問題和開銷也不大。

但是其缺點就是需要2倍的內存。


實際GC過程:

將堆區進行劃分爲:年輕代、老年代。

對於年輕代又進行劃分爲3個區:E區、S(surive)0區、S1區。

而老年代就只有1個區。

整個過程在進行new對象的時候它其實是產生在E區的。當E區快滿了的時候,就會觸發Young區的GC,採用的是複製算法。會對需要刪除的對象上打標記,不需要刪除的對象依次複製到S0區。

這裏有2點疑問:

  1. E區比S區大
  2. 有2個S區

E區比S區大的原因是對象都有個特點:“朝生夕死”,很容易就夭折了,倖存下來的比較少。比例大概是1:1:8

需要2塊S區是因爲需要交替工作的,即E區打完標記倖存後放入S0區,然後需要將E區和S1區刪除,然後等下一次E區快滿的時候將S0區、E區所有對象進行打標全部複製到S1區。S0和S1交替使用作爲倖存下來的區域。

即E+S1複製到S0、E+S0複製到S1、E+S1複製到S0。。。。。反覆執行。

比複製算法直接1分爲2內存的利用率高點。

然後再看old區:

其實每次Young GC這個對象的年齡就會+1,即如果在這次GC後這個對象活下來了其年齡就+1.

如果age=15,就不再往S區複製了而是直接到Old區。

Old區除了存了年齡》=15歲的對象還存儲“大對象”(大對象的複製消耗比較大,所以直接存到old區)

如果Old區快滿的時候也會進行GC,Old GC一般會伴隨Young GC,所以Old GC又叫Full GC,會引起整個java程序暫停然後全力的進行垃圾回收,通過標記清理或標記整理的算法。

總結:標記清理或標記整理主要用於Full GC,複製算法主要用於Young GC.

年輕代中比較有名的收集器是ParNew,老年代中比較有名的垃圾收集器是CMS。

最新版的JDK不再建議用以前的垃圾收集器了,而是採用全新的G1垃圾收集器

 

 

 

 

 

 

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