JVM垃圾回收機制(GC)
引入:我們都知道,棧內存中方法運行完畢後會有彈棧的操作,不會產生垃圾,而堆內存中卻沒有這種操作,當堆內存中很多無用的成員變量、對象等等積壓到一定程度時,就會發生堆內存溢出的一個錯誤OutOfMemoryError (Java heap space)堆內存溢出 ,雖然說堆內存的大小是可以調節的,但是它還是解決不了根本問題。那麼爲了避免這種情況的發生,出現了垃圾回收機制,也就是我們所說的GC。
堆內存的結構
堆內存邏輯上是分爲三個部分:新生代、養老代、永久代(jdk1.7以後我們稱永久代爲元空間)。
但是實際上負責存儲的只有:新生代、養老代。
回收機制介紹
對象在伊甸區(Eden)被new出來,當伊甸區滿了以後還需要創建對象,這時候JVM會對伊甸區進行垃圾回收(YGC也叫輕GC),將伊甸區中的沒有被其他對象所引用的對象進行銷燬(finalize()方法用於銷燬對象的)。然後將伊甸區中存活的對象移動到倖存0區,並且該對象年齡爲1,當伊甸區再滿了之後會對伊甸區和倖存0區的對象進行GC,然後會將伊甸區和倖存0區存活的對象移動到倖存1區,如果當前存活的對象GC前是倖存區的,那麼他們的年齡+1,就這樣GC一次交換一次年齡增長一次,如果有對象經過了15次GC依然存活(15歲),會被轉移到養老區(Old),當養老區滿了之後也會進行垃圾回收(Full GC也叫重GC),對養老區進行垃圾清理,當最後沒有可清理的垃圾時且新生代、養老代都滿了之後,會報一個異常:OutOfMemoryError (Java heap space)
異常原因:
代碼中創建了大量的對象,且長時間不能被回收,導致創建新對象出現堆內存溢出。
關於新生代GC之後有交換,誰空誰是To的圖解:
文字解釋:第一次GC會把存活的對象存入倖存0區(當前的Form區),第二次GC會把所有存活的對象存入倖存1區(當前From區),第三次GC會把所有存活的對象存入倖存0區(當前From區),你會發現每次有對象的哪個區就是From區,沒有對象的那個區就是To區。
這樣就驗證了上面那句:GC之後有交換,誰空誰是To
解讀GC日誌
從百度上扒拉了兩個GC圖,希望可以幫到你。
簡稱Young GC 爲 YGC
簡稱Full GC 爲 FGC
Young GC:
Full GC:
GC算法
-
引用計數法(已經不用了)
缺點:老算法,比較消耗內存,比較難處理循環引用。 -
複製回收算法:(GC 90%用的都是這個,這個算法一般用在YGC就是我們的新生代)
優點:不會產生內存碎片,效率高。
缺點:浪費內存空間。
最經典的體現在這句話:GC之後有交換,誰空誰是To。 裏面所提到的交換其實就是在一次GC之後,把剩餘存活的對象複製到下一個倖存區,從而就導致了必須要有兩個倖存區的概念 (倖存0區和倖存1區) 所以就是比較消耗內存空間。因爲是直接複製的所以比較省時,不會產生內存碎片。 -
標記清除算法:(用於FGC,一般用在老年代)
優點:節約空間
缺點:掃描標記耗時,併產生內存碎片,效率不高。
先標記那些對象是需要回收的,標記完成後,對帶有標記的對象進行統一的回收,因爲分爲了標記和回收兩步,所以效率較低,同時對象與對象之間會有內存碎片產生。 -
標記清除壓縮算法:(用於老年代)
優點:更加節約空間,不會產生內存碎片
缺點:與標記清除算法相比多了壓縮的這一步,所以耗時更長,效率不高。
先標記那些對象是需要回收的,標記完成後,對帶有標記的對象進行統一的回收,然後在對存活的對象進行壓縮規整,分爲了標記、清除、壓縮三步,多以效率更低一些,但是由於已經壓縮規整了所以不會產生內存碎片。
總結: 那個算法合適我們就用那個算法
一般新生代用:複製算法(效率高,耗內存空間)
一般老年代用:標記清除與壓縮的結合(省內存空間,但是效率低)
如果這篇文章對您有幫助,那麼請留下您的足跡吧…!